casting in C

OP #6152809
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
Beitrag #6152825 wurde von einem Moderator gelöscht.
Gast #6152846
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.
Gast #6152868
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!
Gast #6152873
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.
Gast #6153624
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.
Gast #6153672
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.
#6153964
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.
Gast #6154260
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.
#6154316
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.
Gast #6154541
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?
#6154563
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.
Gast #6154602
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.
OP #6157851
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
Gast #6157870
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.
#6158039
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.
#6158059
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 ;)
Gast #6158065
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.
#6158114
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
#6158129
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.
Beitrag #6158132 wurde von einem Moderator gelöscht.
Gast #6158161
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.
#6158233
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.
#6158378
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.
Gast #6158415
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.
(Firma: MED-EL GmbH) #6158469
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.
#6158478
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
Gast #6158490
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.
Gast #6158507
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
#6158529
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.
#6158854
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren