Forum: Mikrocontroller und Digitale Elektronik casting in C


von Elias H. (eliashu)


Lesenswert?

Habe zu folgendem Code eine Frage:

TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 
10ms

Ziel ist es den Wert auf 8Bit zu bekommen, da das Registern eben so groß 
ist.

Mir ist unklar warum ein "-" vor der äußersten Klammerauddruck steht?

Wie castet man 16Bit in 8Bit um? Dachte das geht nicht.

(uint8_t)(int16_t) oder passiert hier etwas anderes?

Bitte keine Verweise auf Lehrbücher, da wurde ich nicht fündig.

Danke vorab.

MfG Elias

von CPU (Gast)


Lesenswert?

welchen Zahlenwert hat F_CPU?

von Oliver S. (oliverso)


Lesenswert?

Elias H. schrieb:
> Bitte keine Verweise auf Lehrbücher, da wurde ich nicht fündig.

Dann hast du die falschen Bücher.
Ja, da wird von 16 signed auf 8 Bit unsigned gecastet, und ja, das geht.
Schreib dir die Bitmuster der Werte auf, und schau dir an, was da 
passiert.

Oliver

von Theor (Gast)


Lesenswert?

@ Elias

Magst mal schreiben, wo Du diese Anweisung her hast? Link?

Beitrag #6152825 wurde vom Autor gelöscht.
von Elias H. (eliashu)


Lesenswert?

Zahlenwert von F_CPU ist 1000000

von Elias H. (eliashu)


Lesenswert?

https://www.mikrocontroller.net/articles/Entprellung

ist aus dem Code: Comfortroutine (C AVR)

von Zeno (Gast)


Lesenswert?

CPU schrieb:
> welchen Zahlenwert hat F_CPU?

Ist für diesen Cast völlig irrelevant und beantwortet die Frage nach dem 
Minus nicht.


Die Funktion des Minuszeichens erschließt sich mir auch nicht und ich 
konnte dazu auch nichts finden, weder in den bei mir vorhandenen C 
Büchern noch im Netz.
Also was bewirkt das Minus? Ich habe bisher so ein Konstrukt noch nicht 
gesehen.

von Karl M. (Gast)


Lesenswert?

Hallo das hier um Timer 0 geht und der im overflow modus betrieben wird, 
wird immer von dem Startwert aufwärts gezählt.

Beim überlsuf von 0xff zu 0x00 erfolgt ein Interrupt Request.
Steht aber alles im Datenblatt und man muss sich nicht dumm stellen.

Tipp schreibe 0-(<expression>), dann wird es klarer!

von CPU (Gast)


Lesenswert?

F_CPU = 1000000, gut, mal rechnen:

1000000 / 1024 = 976,56...
* 10e-3        = 9,7656...
 + 0.5         = 10,2656...
-(..)          = -10,2656...
(int16_t)      = 0xFFF6
(uint8_t)      = 0xF6    // preload for 10ms

Der Blick in die Glaskugel...du hast ein Register TCNT0 welches 
vorwärtszählt und bis zum Überlauf 10ms brauchen soll.

von Markus F. (mfro)


Lesenswert?

Elias H. schrieb:
> Mir ist unklar warum ein "-" vor der äußersten Klammerauddruck steht?

Ich würde annehmen, schlicht aus demselben Grund aus dem üblicherweise 
das unäre Minus verwendet wird: weil man von etwas den negativen Wert 
haben möchte.

von as (Gast)


Lesenswert?

Zeno schrieb:
> Ist für diesen Cast völlig irrelevant und beantwortet die Frage nach dem
> Minus nicht.

Es ermöglicht Dir, das Beispiel Nachzuvollziehen. Ein aus der Luft 
gegriffener wert könnte mit falschem Überlauf verwirren.

von Zeno (Gast)


Lesenswert?

as schrieb:
> Es ermöglicht Dir, das Beispiel Nachzuvollziehen. Ein aus der Luft
> gegriffener wert könnte mit falschem Überlauf verwirren.

Karl hat es verständlich erklärt, auch ohne einen konkreten Wert.

von W.S. (Gast)


Lesenswert?

Elias H. schrieb:
> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for
> 10ms

Gibt es hier etwa jemanden, der einen solchen Quellcode für gut und 
ordentlich erachtet?

Wenn schon, dann sollte man sowas in einer oder mehreren #define(s) 
separat erledigen und eben nicht so eine Verrenkung fabrizieren.

W.S.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

W.S. schrieb:
> Gibt es hier etwa jemanden, der einen solchen Quellcode für gut und
> ordentlich erachtet?

Sprach der Magic Number Priester?

von Stefan F. (Gast)


Lesenswert?

Elias H. schrieb:
> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);

Wow, auf die Idee muss man erst einmal kommen: einen Integer mit 
Fließkomma zu verwursten, negativ machen, dann auf 16bit casten, dann 
nochmal auf 8bit ohne Vorzeichen casten.

Was hat der Autor geraucht? Das Zeug will ich auch haben. Dann ergeben 
vielleicht auch andere seltsame Dinge in meinem Leben Sinn. Zum Beispiel 
Pferde im Flur oder Häuser auf der Autobahn. Oder das Gerede mancher 
Frauen.

Das ist meiner Meinung nach für Menschen unlesbarer Code. Wer so etwas 
schreibt, hat den Sinn der Programmiersprache verfehlt. Da kann man 
gleich Maschinencode schreiben.

von Peter D. (peda)


Lesenswert?

Stefan ⛄ F. schrieb:
> Was hat der Autor geraucht? Das Zeug will ich auch haben.

Das sind ganz normale Standardtechniken.
Ein erfahrener Programmierer schreibt keine magischen Nummern hin, 
sondern den Rechenweg. Das erlaubt es z.B. eine andere F_CPU zu 
verwenden, ohne den Ausdruck korrigieren zun müssen.
Die Berechnung in float vermeidet Rundungsfehler und Überläufe.
Der cast zu int hindert den Compiler daran, die float-Lib zur Laufzeit 
einzubinden.
Der cast zum Zieltyp vermeidet dann eine Compilerwarnung.

von Johannes S. (Gast)


Lesenswert?

Der Term besteht sowieso nur aus Konstanten, den kann der Präprozessor 
schon wegräumen.

von Stefan F. (Gast)


Lesenswert?

Peter D. schrieb:
> Die Berechnung in float vermeidet Rundungsfehler

Das habe ich anders im Kopf. Float ist dafür bekannt (besonders bei sehr 
großen und sehr kleinen Zahlen) auffällig ungenau zu sein.

> Der cast zu int hindert den Compiler daran, die float-Lib
> zur Laufzeit einzubinden.
> Der cast zum Zieltyp vermeidet dann eine Compilerwarnung.

Interessant, da habe ich mal wieder was gelernt. Dankeschön.

von Rolf M. (rmagnus)


Lesenswert?

Karl M. schrieb:
> Tipp schreibe 0-(<expression>), dann wird es klarer!

Man könnte sogar 256-(<expression>) schreiben.

CPU schrieb:
> F_CPU = 1000000, gut, mal rechnen:
>
> 1000000 / 1024 = 976,56...

Nicht ganz. Das Ergebnis ist 976, da in Integer gerechnet wird. Erst die 
Rechnungen danach sind dann in double.

Zeno schrieb:
> as schrieb:
>> Es ermöglicht Dir, das Beispiel Nachzuvollziehen. Ein aus der Luft
>> gegriffener wert könnte mit falschem Überlauf verwirren.
>
> Karl hat es verständlich erklärt, auch ohne einen konkreten Wert.

Mit wird es klarer. So etwas nennt man "Beispiel".

Stefan ⛄ F. schrieb:
> Was hat der Autor geraucht?

Kritisieren, ohne eine bessere Alternative zu zeigen, ist einfach.

Johannes S. schrieb:
> Der Term besteht sowieso nur aus Konstanten, den kann der Präprozessor
> schon wegräumen.

Dem Präprozessor ist das ziemlich egal. Der macht nur die Ersetzung von 
F_CPU und TCNT0, sonst nichts.

Stefan ⛄ F. schrieb:
> Peter D. schrieb:
>> Die Berechnung in float vermeidet Rundungsfehler
>
> Das habe ich anders im Kopf. Float ist dafür bekannt (besonders bei sehr
> großen und sehr kleinen Zahlen) auffällig ungenau zu sein.

Hier kommen aber keine sehr großen oder sehr kleinen Zahlen vor, sondern 
nur welche in einem Bereich, in dem es genauer als int ist. Außerdem 
wird hier nicht in float, sondern in double gerechnet - was aber hier 
vermutlich keinen Unterschied macht, da es sich um Code für AVR-GCC zu 
handeln scheint.

von Herrmann (Gast)


Lesenswert?

Hallo

Vielleicht wäre es etwas verständlicher, wenn der Code mit einer 
weiteren Klammer geschrieben wäre:
TCNT0 = (uint8_t)(int16_t)(0-(F_CPU / 1024 * 10e-3 + 0.5));

von Interessierter (Gast)


Lesenswert?

Rolf M. schrieb:
>> 1000000 / 1024 = 976,56...
>
> Nicht ganz. Das Ergebnis ist 976, da in Integer gerechnet wird. Erst die
> Rechnungen danach sind dann in double.

Bewirkt das +0.5 nicht, dass hier für den ganzen Ausdruck Double genutzt 
wird? Andernfalls wäre es ja sinnlos, weil 0.5 in Integer gecastet 0 
ergibt, meine ich.

Ergänzungsfrage: Stünde 0.5 außerhalb der Klammer, würde dann der 
Ausdruck in der Klammer als Integer berechnet und anschließend mit 0.5 
als Double multipliziert, Ergebnis wäre dann wieder Double?
Also so: TCNT0 = (uint8_t)(int16_t)-((F_CPU / 1024 * 10e-3) + 0.5);

Ach ja, warum eigentlich Double und nicht Float?

von Rolf M. (rmagnus)


Lesenswert?

Interessierter schrieb:
> Rolf M. schrieb:
>>> 1000000 / 1024 = 976,56...
>>
>> Nicht ganz. Das Ergebnis ist 976, da in Integer gerechnet wird. Erst die
>> Rechnungen danach sind dann in double.
>
> Bewirkt das +0.5 nicht, dass hier für den ganzen Ausdruck Double genutzt
> wird?

Nicht der ganze Ausdruck. Jeder Teilausdruck wird für sich behandelt. 
Das erste, was ausgerechnet wird, ist F_CPU / 1024. Da F_CPU vermutlich 
vom Typ unsigned long ist und 1024 vom Typ int, wird die Berechnung in 
unsigned long durchgeführt. Danach kommt die Multiplikation mit 10e-3, 
was ein double ist. Das heißt, das Ergebnis aus der ersten Berechnung 
wird nach double konviertiert, und dann geht's mit dem Typ weiter. Auch 
das +0.5 wird dann in double gerechnet.

> Ergänzungsfrage: Stünde 0.5 außerhalb der Klammer, würde dann der
> Ausdruck in der Klammer als Integer berechnet und anschließend mit 0.5
> als Double multipliziert, Ergebnis wäre dann wieder Double?
> Also so: TCNT0 = (uint8_t)(int16_t)-((F_CPU / 1024 * 10e-3) + 0.5);

Nein, da 10e-3 auch ein double ist.

> Ach ja, warum eigentlich Double und nicht Float?

Weil das der Default für floating point literals ist. Wenn man float 
will, muss man das explizit angeben. 0.5 ist double, float wäre 0.5f.

von Karl M. (Gast)


Lesenswert?

Ich mag Züge, C und Peter Danneggers Code.

Hier leider nur als Fragment wiedergegeben, so dass man nur schwer das 
Ziel dieses Ausdrucks nachvollziehen kann.

Seine Programme, die ich bisher gefunden und gelesen haben, zeugen von 
sehr vier Erfahrung und einem eigenen Stil.
Da wird nichts umsonst oder aufwändig in C formuliert, Zielsetzung in 
meinen Augen ist effizienter C zu Assembler Code.

Z.B. seine Matrix-Tastaturabfrage, mit Entprellung und KeyPressed und 
KeyReleased usw. Funktionen.

Also Lieber Peter, wenn Du wieder etwas neues hast, dann lass mich bitte 
daran teilhaben.

von Elias H. (eliashu)


Lesenswert?

CPU schrieb:
> F_CPU = 1000000, gut, mal rechnen:
>
> 1000000 / 1024 = 976,56...
> * 10e-3        = 9,7656...
>  + 0.5         = 10,2656...
> -(..)          = -10,2656...
> (int16_t)      = 0xFFF6
> (uint8_t)      = 0xF6    // preload for 10ms
>
> Der Blick in die Glaskugel...du hast ein Register TCNT0 welches
> vorwärtszählt und bis zum Überlauf 10ms brauchen soll.


Ich habe das jetzt mal nachgerechnet wie oft der Timercounter zählen 
muss wenn der Prozessor mit 1Mhz arbeitet und der Prescaler des 
Timercounter mit 1024 den Takt teilt.
Das bedeutet zunächst alle 0,000 102 4 Takte pro Sekunde (Tps) wird der 
Timercounter um 1 erhöht.
10 ms entsprechen 0,010 Tps

Teilt man also 0,010 / 0,000 102 4 = 97,65, aufgerundet ergibt das einen 
Wert von 98.

Bedudeutet also der Timercounter muss 98Mal zählen, dann darf er einen 
Interrupt auslösen, dies ergibt ein Interval von 10ms oder 0,010Tps

Eine unit8_t kann also eine Zahl die maximal 512 (Dez.) annehmen. Das 
Timercounter Daten Register schafft allerdings nur 8Bit (256) und das 
Counter Register zählt grundsätzlich von 0 bis 255. Daher sollte der 
geladene Wert TCNT0 = 98; oder TCNT0 = 0x62; sein

Da ich die Berechnung
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
aus mehren Gründen nicht navollziehen kann. Bei mir kommt in diesem 
Klammerausdruck (F_CPU / 1024 * 10e-3 + 0.5) = 1,47 heraus. Ich habe 
10e-3 nicht wie oben gerechnet mit der Eulereschen Zahl exponiert 
sondern den Ausdruck exponenziell gerechnet (10e-3 = 10^(-3) = 0.0001). 
Für die Euleresche Zahl muss in C die math.lib includiert werden und 
eine funktionsaufruf ist notwendig.

Kann mir wer sagen was für ein Wert wirklich in dem Klammerausdruck 
herrauskommt? und wie 10e-3 zu verstehen ist?

Bei den beiden Cast´s bin ich nicht ganz sicher ob ich die Schritte 
arithmeitsch richtig erfasst habe. Der rechte Klammerausdruck ist 
negativ, daher der Cast mit Vorzeichenhafteten int 16Bit. Der nächste 
Cast auf 8 Bit ohne vorzeichenbehftete int 8Bit müsste daher das 1er und 
2er Komplement gebildet haben und vom MSB weg 8 Stellen weggeschnitten 
haben. Ist das so richtig?

MfG Elias

von Johannes S. (Gast)


Lesenswert?

Elias H. schrieb:
> Eine unit8_t kann also eine Zahl die maximal 512 (Dez.) annehmen.

Nein, 255.

Das ganze berechnet der Compiler während der Übersetzung, das braucht 
keine Libs und wird zu zwei Zeilen Assembler übersetzt. Deshalb kann man 
hier ohne Probleme mit double und Long Long int rechnen.

von Karl M. (Gast)


Lesenswert?

Elias H. schrieb:
> (10e-3 = 10^(-3) = 0.0001)

Das ist schon mal falsch!

Und dieser Ausdruck hat nichts mit e^x zu tun.

von A.S. (Gast)


Lesenswert?

Elias H. schrieb:
> 10e-3 = 10^(-3) = 0.0001)

2e1 sind 2*10^1=20
2e-3 sind 2*10^-3=0,002
10e-3 sind 10*10^-3=10*0,001=0,01

von Rolf M. (rmagnus)


Lesenswert?

Elias H. schrieb:
> Das bedeutet zunächst alle 0,000 102 4 Takte pro Sekunde (Tps) wird der
> Timercounter um 1 erhöht.

Die Einheit ist nicht "Takte pro Sekunde", sondern schlicht Sekunden.

> 10 ms entsprechen 0,010 Tps

10 ms sind 0,01 Sekunden.

> Eine unit8_t kann also eine Zahl die maximal 512 (Dez.) annehmen.

255.

> Das Timercounter Daten Register schafft allerdings nur 8Bit (256)

Auch 255. Was lässt dich eigentlich vermuten, dass ein uint8_t nicht 
auch 8 Bit und damit den selben Wertebereich hätte?

> Ich habe 10e-3 nicht wie oben gerechnet mit der Eulereschen Zahl
> exponiert sondern den Ausdruck exponenziell gerechnet (10e-3 = 10^(-3) =
> 0.0001).
> Für die Euleresche Zahl muss in C die math.lib includiert werden und
> eine funktionsaufruf ist notwendig.

Das hat mit Euler genauso wenig zu tun wie mit dem exklusiven Oder. In C 
wäre 10^(-3) nämlich eine exklusiv-Oder-Verknüpfung aus 10 und -3. Was 
du meinst, wäre auch ein Funktionaufruf: pow(10, -3).

> Kann mir wer sagen was für ein Wert wirklich in dem Klammerausdruck
> herrauskommt? und wie 10e-3 zu verstehen ist?

Siehe 
https://de.wikipedia.org/wiki/Gleitkommazahl#Exponentialschreibweise

> Bei den beiden Cast´s bin ich nicht ganz sicher ob ich die Schritte
> arithmeitsch richtig erfasst habe. Der rechte Klammerausdruck ist
> negativ, daher der Cast mit Vorzeichenhafteten int 16Bit. Der nächste
> Cast auf 8 Bit ohne vorzeichenbehftete int 8Bit müsste daher das 1er und
> 2er Komplement gebildet haben und vom MSB weg 8 Stellen weggeschnitten
> haben. Ist das so richtig?

Ja.

A.S. schrieb:
> 10e-3 sind 10*10^-3=10*0,001=0,01

Eigentlich ist das allerdings eine unübliche Schreibweise. Normalerweise 
schaut man, dass die Mantisse immer im Bereich 1 ≤ m < 10 liegt. Man 
schreibt also eher 1e-2.

von M. K. (sylaina)


Lesenswert?

Stefan ⛄ F. schrieb:
> Das habe ich anders im Kopf. Float ist dafür bekannt (besonders bei sehr
> großen und sehr kleinen Zahlen) auffällig ungenau zu sein.

Uh, bist genauso alt wie ich, wie? Der Fehler hatte aber nicht float 
sondern war ein Designfehler weshalb eine FPU in grauer Vorzeit mal 
falsch rechnete (ich meine, das war im Pentium I der Fall).

Interessierter schrieb:
> Bewirkt das +0.5 nicht, dass hier für den ganzen Ausdruck Double genutzt
> wird? Andernfalls wäre es ja sinnlos, weil 0.5 in Integer gecastet 0
> ergibt, meine ich.

Das macht schon das *10^-3 ;). Die +0.5 sorgen für ein kaufmännisches 
Runden da so ein digitales Rechenwerk idR nur Runden durch Abschneiden 
kann ;)

Interessierter schrieb:
> Ach ja, warum eigentlich Double und nicht Float?

Der Compiler macht Double draus, das ist so weil nicht zu Float 
gezwungen wird. Ist aber eigentlich auch wieder quatsch da das für einen 
AVR ist und AVR kann nur Float, die kennen gar kein Double ;)

Elias H. schrieb:
> Eine unit8_t kann also eine Zahl die maximal 512 (Dez.) annehmen

In welchem Universium denn das? Ein uint8_t kann maximal 255 annehmen 
und nicht 512 ;)

Elias H. schrieb:
> Das
> Timercounter Daten Register schafft allerdings nur 8Bit (256)

Da verwechselt du etwas: Ein 8 bit Register kann 256 verschiedene 
Zustände annehmen aber nur maximal die Zahl 255 darstellen. Auch 0 ist 
eine Zahl ;)

Elias H. schrieb:
> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
> aus mehren Gründen nicht navollziehen kann. Bei mir kommt in diesem
> Klammerausdruck (F_CPU / 1024 * 10e-3 + 0.5) = 1,47 heraus.

Den Rechenweg bei 1 MHz als F_CPU würde ich gern mal sehen ;)

von Stefan F. (Gast)


Lesenswert?

M. K. schrieb:
>> Das habe ich anders im Kopf. Float ist dafür bekannt (besonders bei sehr
>> großen und sehr kleinen Zahlen) auffällig ungenau zu sein.

> Uh, bist genauso alt wie ich, wie? Der Fehler hatte aber nicht float
> sondern war ein Designfehler weshalb eine FPU in grauer Vorzeit mal
> falsch rechnete (ich meine, das war im Pentium I der Fall).

Den Intel Bug meinte ich nicht. Ich meine, dass Float Zahlen nur wenig 
signifikante Stellen haben, das vergesse ich selbst oft.

Das war schon heftig damals, Massen von Intel Prozessoren in jedem 
dritten Computer rechneten falsch! Eine bis dahin unvorstellbare 
Situation.

Heute haben wir Spectre. Wobei das meiner Meinung nach kein Bug ist, 
sondern eine Nebenwirkung von unpassenden Kombinationen aus 
Optimierungen und Anwendungen.

Wenn ich in einem Bürogebäude die Zwischentüren vereinfache, damit man 
sich schneller bewegen kann, und dann dort sowohl Telekom als auch 
Vodafone einziehen lasse, dann darf ich mich nicht wundern, dass da ab 
und zu mal jemand zum Lauschen in den benachbarten Flur schleicht.

von Johannes S. (Gast)


Lesenswert?

M. K. schrieb:
> Ist aber eigentlich auch wieder quatsch da das für einen AVR ist und AVR
> kann nur Float, die kennen gar kein Double ;)

Das berechnet der Compiler und nicht der AVR, das scheint immer noch 
nicht klar zu sein.

von M. K. (sylaina)


Lesenswert?

Johannes S. schrieb:
> Das berechnet der Compiler und nicht der AVR, das scheint immer noch
> nicht klar zu sein.

Da hast du recht und ich hab mich schlecht ausgedrückt ;)

von Oliver S. (oliverso)


Lesenswert?

Johannes S. schrieb:
> M. K. schrieb:
>> Ist aber eigentlich auch wieder quatsch da das für einen AVR ist und AVR
>> kann nur Float, die kennen gar kein Double ;)
>
> Das berechnet der Compiler und nicht der AVR, das scheint immer noch
> nicht klar zu sein.

Was nichts daran ändert. Der Compiler darf das zwar zur Compilezeit 
selber ausrechenen, aber nur so, daß das gleiche Ergebnis wie zur 
Laufzeit herauskommt. Also muß auch der mit float rechnen.

M. K. schrieb:
>> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
>> aus mehren Gründen nicht navollziehen kann. Bei mir kommt in diesem
>> Klammerausdruck (F_CPU / 1024 * 10e-3 + 0.5) = 1,47 heraus.
>
> Den Rechenweg bei 1 MHz als F_CPU würde ich gern mal sehen ;)


(uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5)

Operator-Prefernz: / und * sind gleichwertig, und sind höher als +
Klammern zuerst.

Also:
Klammerausruck:
1.000.000UL / 1024 = 976 (unsigned Intergerrechnung)
976 * 10e-3 = 9,76 (float-Berechnung, Falle: da steht nicht 1e-3, 
sondern 10e-3, für 10ms)
0,976+0,5 = 10,26 (float-Berechnung)
-(10,26) = -10,26 (float-Berechnung)

Dann casten:
(int16_t)(-10,26) = -10
uint8_t(-10) = 246

Der Timer-OVF kommt beim Überlauf von 255 nach 0. 246 bis 256 (=255+1) 
sind 10 Schritte, also 10ms.

Oliver

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

M. K. schrieb:
> Interessierter schrieb:
>> Bewirkt das +0.5 nicht, dass hier für den ganzen Ausdruck Double genutzt
>> wird? Andernfalls wäre es ja sinnlos, weil 0.5 in Integer gecastet 0
>> ergibt, meine ich.
>
> Das macht schon das *10^-3 ;). Die +0.5 sorgen für ein kaufmännisches
> Runden da so ein digitales Rechenwerk idR nur Runden durch Abschneiden
> kann ;)

Die Rechenwerke können es teils schon, aber C kann es nicht, außer über 
die Funktion round(), wodurch es aber dann keine Compilezeit-Konstante 
mehr ist.

Stefan ⛄ F. schrieb:
> Den Intel Bug meinte ich nicht. Ich meine, dass Float Zahlen nur wenig
> signifikante Stellen haben, das vergesse ich selbst oft.

Das hat aber nichts mit der Größe der Zahl zu tun. Die Anzahl an 
signifikanten Stellen ist immer gleich, so lange es keine denormale Zahl 
ist.

Oliver S. schrieb:
> Johannes S. schrieb:
>> M. K. schrieb:
>>> Ist aber eigentlich auch wieder quatsch da das für einen AVR ist und
>>> AVR kann nur Float, die kennen gar kein Double ;)
>>
>> Das berechnet der Compiler und nicht der AVR, das scheint immer noch
>> nicht klar zu sein.

Und es stimmt nicht ganz. Double kennt auch der avr-gcc. Aber es ist 
dort genauso klein wie float.

> Was nichts daran ändert. Der Compiler darf das zwar zur Compilezeit
> selber ausrechenen, aber nur so, daß das gleiche Ergebnis wie zur
> Laufzeit herauskommt. Also muß auch der mit float rechnen.

Dass double so klein ist, ist allerdings eh schon ein Verstoß gegen ISO 
C.

> Operator-Prefernz: / und * sind gleichwertig,

Und links-assoziativ.

> Also:
> Klammerausruck:
> 1.000.000UL / 1024 = 976 (unsigned Intergerrechnung)
> 976 * 10e-3 = 9,76 (float-Berechnung, Falle: da steht nicht 1e-3,
> sondern 10e-3, für 10ms)

double, nicht float.

: Bearbeitet durch User
Beitrag #6158132 wurde vom Autor gelöscht.
von Oliver S. (oliverso)


Lesenswert?

Rolf M. schrieb:
> double, nicht float.

Wenn schon Korintenkacken, dann "32-bit double".

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Oliver S. schrieb:
> Rolf M. schrieb:
>> double, nicht float.
>
> Wenn schon Korintenkacken, dann "32-bit double".

Hab ich ja geschrieben:

Rolf M. schrieb:
> Double kennt auch der avr-gcc. Aber es ist dort genauso klein wie float.

von Stefan F. (Gast)


Lesenswert?

Rolf M. schrieb im Beitrag #6158129 wegen Fließkommazahlen:
> Das hat aber nichts mit der Größe der Zahl zu tun. Die Anzahl an
> signifikanten Stellen ist immer gleich, so lange es keine denormale Zahl
> ist.

Ja sicher.

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Rolf M. schrieb:
>> double, nicht float.
>
> Wenn schon Korintenkacken, dann "32-bit double".

In C99 (ISO-IEC 9899 6.2.5 §10) steht:
1
There are three real floating types, designated as float, double, and long double.42) The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double.

Die ist so zu lesen, dass alle drei Type identisch sein dürfen.

Und ansonsten sind nur Untergrenzen für die Genauigkeit/Bereich 
festgelegt, die man mit weniger als IEEE-754-float erreichen kann.

von Peter D. (peda)


Lesenswert?

Rolf M. schrieb:
> Eigentlich ist das allerdings eine unübliche Schreibweise.

Bei Technikern ist es sehr gebräuchlich, daß der Exponent ein Vielfaches 
von 3 ist (... p, n, m, k, M, G ...).

50ms = 50e-3 s
22MΩ = 22e6 Ω

von Oliver S. (oliverso)


Lesenswert?

Die Schreibweise 10e-3 kommt daher, daß der Wert die gewünschte 
Zykluszeit (10ms) darstellt.

Die ganze Berechnung ist zwar richtig, aber ein typischen Beispiel 
dafür, wie man lesbaren und verständlichen Code nicht schreiben sollte.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
> In C99 (ISO-IEC 9899 6.2.5 §10) steht:
> There are three real floating types, designated as float, double, and
> long double.42) The set of values of the type float is a subset of the
> set of values of the type double; the set of values of the type double
> is a subset of the set of values of the type long double.
>
> Die ist so zu lesen, dass alle drei Type identisch sein dürfen.

Ja.

> Und ansonsten sind nur Untergrenzen für die Genauigkeit/Bereich
> festgelegt, die man mit weniger als IEEE-754-float erreichen kann.

Nein. float muss mindestens 6 signifikante Dezimalstellen korrekt 
darstellen, double mindestens 10. Das schafft man aber nicht mit einem 
IEEE-754 single float. Das findet man in C99 unter 5.2.4.2.2 §8.

von Oliver S. (oliverso)


Lesenswert?

Rolf M. schrieb:
> darstellen, double mindestens 10. Das schafft man aber nicht mit einem
> IEEE-754 single float. Das findet man in C99 unter 5.2.4.2.2 §8.

Damit wären alle C-Implementierungen, die float als IEEE-754 single 
float (4 Byte) implemetieren, nicht standardkonform.

Oliver

von Johannes S. (Gast)


Lesenswert?

Oliver S. schrieb:
> Was nichts daran ändert. Der Compiler darf das zwar zur Compilezeit
> selber ausrechenen, aber nur so, daß das gleiche Ergebnis wie zur
> Laufzeit herauskommt. Also muß auch der mit float rechnen.

Das bezweifle ich.  Der avr-gcc meckert erst wenn ich eine 12 stellige 
Zahl überlaufen lasse.
1
#define C1 100000000000.0
2
3
int8_t m = (unsigned long long)(C1 + 1.0) - 100000000000ULL;

Mit einer 0 weniger geht es, das passt aber nicht mehr in ein float 
rein.

von Walter T. (nicolas)


Lesenswert?

C1 ist double, nicht float.

von Johannes S. (Gast)


Lesenswert?

Ja, die Aussage war aber das der Compiler auch so wie der avr in float 
rechnet.

von Walter T. (nicolas)


Lesenswert?

Johannes S. schrieb:
> Ja, die Aussage war aber das der Compiler auch so wie der avr in float
> rechnet.

Die Aussage war, dass der Compiler in float rechnet, wenn es der AVR 
auch tut. Tut er bei beidem nicht. Also ist die Aussage gleichzeitig 
wahr und mißverständlich.

von Johannes S. (Gast)


Lesenswert?

Wenn man an C1 und 1.0 ein f anhängt, dann ändert das nichts an der 
Compilermeldung.

von Jürgen W. (Firma: MED-EL GmbH) (wissenwasserj)


Lesenswert?

Viel zu kompliziert gedacht.

Mach' Folgendes:

#define F_CPU 1000000ul     // unsigned long ist schon mal gut


Jetzt bau' das ganze mal anders auf (hier Schritt für Schritt)
(F_CPU / 1024 * 10e-3 + 0.5)

1. Multipliziere den Klammerausdruck mit 2, damit aus 0.5 '1' wird und 
schiebe im Anschluß nach rechts; außerdem ist 1/1024 ident zu '>>10'
((F_CPU>>10)/1000u*2u + 1u)>>1

2. Dann den eff. Divisor 1/500 umbauen auf 1/500~~(131>>16)
(((((F_CPU>>10)*131u)>>16) + 1u)>>1)

Fertig.

Damit sind sämtliche Float-Werte udgl. aus der Gleichung heraußen.

von Oliver S. (oliverso)


Lesenswert?

Johannes S. schrieb:
> Das bezweifle ich.  Der avr-gcc meckert erst wenn ich eine 12 stellige
> Zahl überlaufen lasse.

Aha.

warning: overflow in conversion from 'long long unsigned int' to 
'int8_t' {aka 'signed char'} changes value from '18446744073709549568' 
to '0' [-Woverflow]

Was genau hat das mit der double/float-Problematik zu tun?

Johannes S. schrieb:
> Mit einer 0 weniger geht es, das passt aber nicht mehr in ein float
> rein.

Aha. Klingt sehr unwahrscheinlich.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Oliver S. schrieb:
> Rolf M. schrieb:
>> darstellen, double mindestens 10. Das schafft man aber nicht mit einem
>> IEEE-754 single float. Das findet man in C99 unter 5.2.4.2.2 §8.
>
> Damit wären alle C-Implementierungen, die float als IEEE-754 single
> float (4 Byte) implemetieren, nicht standardkonform.

Warum?

von Stefan F. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Das ist meiner Meinung nach für Menschen unlesbarer Code

Vor ein paar Tagen hatte ich dafür noch -2 bekommen, und jetzt wird 
schon so lange über diese eine Zeile diskutiert.

von Johannes S. (Gast)


Lesenswert?

Bei meinem Test wird nicht von ULL in int8 gecastet, die diff der 
Berechnung ist 1.
Der Compiler rechnet mit anderen floats als der avr.

Korrektur:
Die diff wird 0 wegen Überlauf, also doch mit float gerechnet. Komisch 
ist nur die Warnung die erst bei den 12 Stellen kommt.

von Karl M. (Gast)


Lesenswert?

Jürgen W. schrieb:
> Mach' Folgendes:

Hallo,

und somit weiß keiner, dass hier mit dem Timer0 und Vorteile 1024 bei 
100Hz Intervallen gearbeitet wird.

Ich schreibe dies dann so, bzw. zur besseren Lesbarkeit um die 0 
kongruent 256 modulo 256 erweitert:
1
#define F_CPU 1000000ul // Beispiel zum Rechnen
2
#define T0_PRESCALER 1024  // T0 Teiler
3
#define T0_FREQUENCY 100   // T0 Zielfrequenz [Hz]
4
#define T0_PRELOAD (uint8_t)(int16_t)(0-(1.0 * F_CPU / T0_PRESCALER / T0_FREQUENCY +0.5)) // T0 im Timer0 Overflow Mode

von Oliver S. (oliverso)


Lesenswert?

Johannes S. schrieb:
> Bei meinem Test wird nicht von ULL in int8 gecastet, die diff der
> Berechnung ist 1.

Doch, wird es. Zumindest in dem von dir gezeigten Code.
Die Compilerwarnung ist die originale von avr-gcc 9.2.0.

Und das vom Compiler zur Compilezeit errechnete Ergebnis ist 0.

Oliver

von Oliver S. (oliverso)


Lesenswert?

Rolf M. schrieb:
> Warum?

???

Wenn der C-Standard für float mindestens 6 Dezimalstellen fordert, die 
Implementierung aber IEEE-754 single float dafür implementiert, was nach 
deiner Aussage keine 6 Dezimalstellen schafft, passt das irgendwie nicht 
zusammen.

Oliver

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Oliver S. schrieb:
> Wenn der C-Standard für float mindestens 6 Dezimalstellen fordert, die
> Implementierung aber IEEE-754 single float dafür implementiert, was nach
> deiner Aussage keine 6 Dezimalstellen schafft, passt das irgendwie nicht
> zusammen.

Ich hab nicht behauptet, dass IEEE single keine 6 Dezimalstellen 
schafft, sondern dass es keine 10 Stellen schafft. Das heißt, es ist 
nicht für den C-Typ double ausreichend, und daher ist die 
avr-gcc-Implementation in der Hinsicht nicht ISO-C-konform.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Ok, dann habe ich deinen Text missverstanden.

Oliver

von Dirk B. (dirkb2)


Lesenswert?

Jürgen W. schrieb:
> 1. Multipliziere den Klammerausdruck mit 2, damit aus 0.5 '1' wird und
ok
> schiebe im Anschluß nach rechts;
/ 2 ist lesbarer

> außerdem ist 1/1024 ident zu '>>10'
> ((F_CPU>>10)/1000u*2u + 1u)>>1
>
> 2. Dann den eff. Divisor 1/500 umbauen auf 1/500~~(131>>16)
> (((((F_CPU>>10)*131u)>>16) + 1u)>>1)

Viel zu kompliziert und unleserlich.
Das wäre ok, wenn das zur Laufzeit in einer Schleife abläuft.
Hier rechnet aber der Compiler das aus

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.