Wenn ADC == 3 und offset == 7 kommt -31567 raus.
Das verstehe ich nicht ganz.
Aber mir ist sowieso grundsätzlich nicht wirklich klar wie der gcc beim
Typecasting vorgeht. Insbesondere wenn dabei signed/unsigned im Spiel
ist.
Wenn ich sowas schreibe...
1
(int32_t)(a-b)
... wird doch vermutlich die Operation (a-b) mit ihren natürlichen Typen
durchgeführt (bzw. ggf. automatisch gecastet) und erst das Ergebnis in
int32 umgewandelt, oder?
Oder was passiert z.b. hier:
1
int16_tresult;
2
result=(int8_t)-8;
Das Ergebnis ist -8. Der Ausdruck (int8_t)-8 ist aber eigentlich nur 8
bit lang. Vermutlich wird hier automatisch noch ein (int16_t) cast bei
der Zuweisung an result durchgeführt?
Es scheint also dass der Compiler bei Zuweisung eines "kleineren" an
einen "größeren" Typ die zusätzlichen Bits entsprechend der signedness
des zieldatentyps erzeugt.
Andererseits:
1
uint16_tresult;
2
result=(int8_t)-8;
3
printf("%d\n",result);
sagt:
65528
Wenn (int8_t)-8 binär 11111000 repräsentiert, dann aber als unsigned
interpretiert wird, müsste es doch eigentlich zu 0000000011111000
verlängert werden?
Oder nimmt der Compiler hier Länge des Zieldatentyps, ignoriert aber
seine Signedness und verwedet die der Quelle?
Muss ich daraus folgern, dass die Angabe der Signedness innerhalb eines
Casts irrelevant ist?
Ich kapier grad garnix mehr :-).
viele Grüße!
Klaus
Klaus W. schrieb:
>> Wenn ADC == 3 und offset == 7 kommt -31567 raus.>
Schon möglich.
Habs nicht nachgerechnet.
> Aber mir ist sowieso grundsätzlich nicht wirklich klar wie der gcc beim> Typecasting vorgeht.
Nach den C-Regeln:
Welches ist der Datentyp des linken Operanden einer Operation?
Welches ist der Datentyp des rechtne Operanden einer Operation?
Sind beide gleich oder muss einer an den anderen angepasst werden?
Danach wird dann die Operation ausgewählt
Die Operation liefert dann selbst wieder einen Datentyp (der
normalerweise identisch ist, mit dem Datentyp auf den die beiden
Operanden gebracht wurden)
Und so gehts dann weiter, den arithemtischen Punkt vor Strich Regeln
nach und natürlich unter Berücksichtigung von zusätzlichen Klammerungen.
Von innen nach aussen, von links nach rechts.
Daher ist ein beliebter Fehler, den Datentyp des Ergebnisses umzucasten
(zb von int nach double) und sich zu wundern, warum zb eine Division in
int gemacht wird, wo doch das Ergebnis auf double gecastet wurde.
Zum Zeitpunkt an dem die Entscheidung fällt, welche Operation genommen
wird, ist der Datentyp des Ergebnisses sowas von uninteressant.
> Insbesondere wenn dabei signed/unsigned im Spiel> ist.
Wenn einer der beiden Operanden unsigned ist, wird auch der andere zu
unsigned.
Oder kurz gesagt: bei signed/unsigned gewinnt immer unsigned.
> Wenn ich sowas schreibe...>
1
>(int32_t)(a-b)
2
>
> ... wird doch vermutlich die Operation (a-b) mit ihren natürlichen Typen> durchgeführt (bzw. ggf. automatisch gecastet)
Nö. Mit dem Datentyp der dem höherwertigen Datentyp von a bzw b
entspricht.
int - long - double
sind a und b vom Typ char, wird die Operation in int gemacht. Ein
Optimizer kann das noch verändern, aber grundsätzlich wird nicht kleiner
als int gerechnet.
> und erst das Ergebnis in> int32 umgewandelt, oder?
Yep.
Für die Auswahl des - Operators ist es unerheblich ob das Ergebnis davon
in int32 umgewandelt wird.
> Oder was passiert z.b. hier:>
1
>int16_tresult;
2
>result=(int8_t)-8;
3
>
> Das Ergebnis ist -8. Der Ausdruck (int8_t)-8 ist aber eigentlich nur 8> bit lang. Vermutlich wird hier automatisch noch ein (int16_t) cast bei> der Zuweisung an result durchgeführt?
Der Compiler kann immer von sich aus in einen 'höheren' Datentyp casten.
Eine kleine Zahl kann mit einem 'größeren' Datenytp immer dargestellt
werden.
Aber umgekehrt geht das nicht. 465 passt nun mal nicht in 8 Bit. Daher
warnen gute Compiler an dieser Stelle. Die Warnung wird man nur dann
los, wenn man explizit, mit einem Cast, sagt: Ist schon ok.
>
1
>uint16_tresult;
2
>result=(int8_t)-8;
3
>printf("%d\n",result);
4
>
> sagt:> 65528
Das glaub ich nicht. %d verlangt einen int in der Argumentliste. Du hast
keinen int, du hast einen unsigned int.
> Oder nimmt der Compiler hier Länge des Zieldatentyps, ignoriert aber> seine Signedness und verwedet die der Quelle?
Bei signed/unsigned Fragen, wird die Bitdarstellung nicht verändert.
Intern ändert sich am Bitmuster gar nichts. Lediglich die Interpretation
während einer Berechnung (und damit auch die Auswahl der Operation)
verändert sich.
In dieser Operation
result = (int8_t)-8;
muss der Compiler 2 Dinge tun.
* die Bitlängen anpassen
* signed/unsigned anpassen
Zuerst wird die Bitlänge angepasst.
Dann wird signed/unsigned angepasst (welches keine auszuführende
Operation benötigt - das Bitmuster bleibt das Gleiche).
Dein Bitmuster für -8 lautet 11111000
wird also zunächst auf 16 Bit aufgeblasen. Und da es sich hier um einen
signed int handelt, wird das Vorzeichenbit natürlich dupliziert, denn
der signed Zahlenwert darf sich ja nicht ändern, nur weil die Bitlänge
größer wird.
Aus 11111000 wird so 1111111111111000
Als unsigned Zahl ist das dann 65528
Uff, vielen Dank erstmal!
Ich muss mir das alles nochmal durch den Kopf gehen lassen...
aber:
Karl heinz Buchegger schrieb:
> Klaus W. schrieb:> In dieser Operation> result = (int8_t)-8;> muss der Compiler 2 Dinge tun.> * die Bitlängen anpassen> * signed/unsigned anpassen>> Zuerst wird die Bitlänge angepasst.> Dann wird signed/unsigned angepasst (welches keine auszuführende> Operation benötigt - das Bitmuster bleibt das Gleiche).>> Dein Bitmuster für -8 lautet 11111000> wird also zunächst auf 16 Bit aufgeblasen. Und da es sich hier um einen> signed int handelt, wird das Vorzeichenbit natürlich dupliziert, denn> der signed Zahlenwert darf sich ja nicht ändern, nur weil die Bitlänge> größer wird.> Aus 11111000 wird so 1111111111111000> Als unsigned Zahl ist das dann 65528
Ich schließe daraus, dass meine bisherige Vorstellung, dass Signedness
und Länge eines Objekts quasi eine untrennbare Einheit beim Casten
bilden falsch ist.
Dass signed/unsigned das Bitmuster nicht ändert, gilt ja nur, wenn nicht
gleichzeitig eine Bitlängenänderung stattfindet, oder?
Der Hinweis, dass bei einer Operation immer unsigned gewinnt war schon
mal recht erhellend.
Moment, das bedeutet ja eigentlich, dass die Berechnung eines wie auch
immer gearteten Terms komplett unsigned durchgeführt wird, sobald auch
nur EIN unsigned objekt enthalten ist?
Klaus W. schrieb:
> Der Hinweis, dass bei einer Operation immer unsigned gewinnt war schon> mal recht erhellend.> Moment, das bedeutet ja eigentlich, dass die Berechnung eines wie auch> immer gearteten Terms komplett unsigned durchgeführt wird, sobald auch> nur EIN unsigned objekt enthalten ist?
Nein, denn erstens gilt die "Typangleichung" der beteiligten Operanden
nicht für jeden Operator, und zum anderen gibt es ja auch noch Klammern,
so dass Teilterme durchaus signed gerechnet werden können.
Stefan Ernst schrieb:
> Klaus W. schrieb:>>> Der Hinweis, dass bei einer Operation immer unsigned gewinnt war schon>> mal recht erhellend.>> Moment, das bedeutet ja eigentlich, dass die Berechnung eines wie auch>> immer gearteten Terms komplett unsigned durchgeführt wird, sobald auch>> nur EIN unsigned objekt enthalten ist?>> Nein, denn erstens gilt die "Typangleichung" der beteiligten Operanden> nicht für jeden Operator, und zum anderen gibt es ja auch noch Klammern,> so dass Teilterme durchaus signed gerechnet werden können.
Uff, das scheint reichlich kompliziert...
Hm, mit den Klammern hast Du schon recht, aber zumindest das Ergebnis
müsste dann immer unsigned sein.
Ist aber auch nicht, denn wenn ich Karl Heinz richtig verstanden habe,
müsste ja zumindest
1
result=(uint8_t)-8-(uint8_t)4;
und
1
result=(int8_t)-8-(uint8_t)4;
identisch sein.
Im ersten Fall kommt aber 244 und im zweiten -12 raus.
Was ja auch irgendwie Sinn macht.
Stefan Ernst schrieb:
> Klaus W. schrieb:>>> Im ersten Fall kommt aber 244 und im zweiten -12 raus.>> 244 und -12 haben das selbe 8-Bit-Muster. ;-)
Stimmt, aber "result" ist hier wie in den Experimenten oben eine 16 bit
Variable.
> Dass signed/unsigned das Bitmuster nicht ändert, gilt ja nur, wenn nicht> gleichzeitig eine Bitlängenänderung stattfindet, oder?
Bei einem Cast ändert sich das Bitmuster überhaupt nicht!
Also jetzt hab ich bald nen Knoten im Hirn.
Ich versuche das nochmal zu analysieren:
1. Fall:
1
result=(uint8_t)-8-(uint8_t)4;
Ich weiss eigentlich schon garnicht welche Bitlänge eine "blanke"
Konstante wie "4" oder "-8" hat. Ist hier aber vermutlich nicht
relevant.
(uint8_t) macht aus -8 -> 11111000 (bezüglich der länge) und sagt dem
Compiler dass es als unsigned zu interpretieren ist.
Mit "4" passiert das gleiche -> 00000100.
Bei der Subtraktion entsteht 11110100. Also -12 oder 244. Je nach
Interpretation. Ich vermute aber der Compiler geht als unsigned damit um
weil die Operanden auch unsigned waren.
Da result 16 bit ist, wird also mit 0 erweitert -> 0000000011110100.
Was als signed interpretiert 244 ergibt.
2. Fall:
1
result=(int8_t)-8-(uint8_t)4;
-8 wird ebenfalls zu 11111000 aber als signed interpretiert.
Wäre es nun so, dass bei der Subtraktion beide Operanden als unsigned
interpretiert werden, wenn einer von ihnen unsigned ist, müsste der
weitere Ablauf dem Fall 1 entsprechen.
Tatsächlich ist aber eher das Gegenteil der Fall und die Operation wird
komplett als signed ausgeführt. Demnach wird auch das Ergebnis als
signed behandelt, weswegen bei der Erweiterung zu 16 bit
1111111111110100 entsteht.
D.h. also für die Art der Erweiterung auf 16bit (signed/unsigned) bei
der Zuweisung scheint die "rechte Seite" verantwortlich zu sein.
Aber (um zu dem anfänglichen Beispiel zurückzukehren)
1
int16_toffset;
2
int16_ti_charge;
3
4
i_charge=((((int32_t)(ADC-offset))*1330)/241);
Ich bin mir nicht ganz sicher, welche Eigenschaften ADC eigentlich hat,
vermute aber dass es unsigned ist. Daher modifizieren ich das zur
Überprüfung nochmal so:
1
int16_toffset;
2
int16_ti_charge;
3
uint16_tadc;
4
5
offset=7;
6
adc=4;
7
i_charge=((((int32_t)(adc-offset))*1330)/241);
Das Ergebnis ist wieder -31561.
Jetzt ist die Frage, ob signed oder unsigned bei der Erweiterung des
Ergebnisses des Ausdrucks (adc-offset) auf 32 bit zugrunde gelegt wird.
Aus den obigen Experimenten schließe ich, dass es hier völlig wurscht
ist, ob ich int32 oder uint32 schreibe, da die signedness der "rechten
Seite" relevant ist. Ich könnte mir denken dass hier "signed" verwendet
wird
(Wieder wegen der Ergebnisse obiger Experimente).
Also 4-7 = -3, in 16 bit: 1111111111111101. Erweitert zu 32 bit:
1111111111111111111111111111101. *1330 /241 = -16
In 32 bit also 1111111111111111111111111110000. Die Wandlung in 16bit
macht daraus 1111111111110000. Das könnte man jetzt als -16 oder
65520 interpretieren, aber niemals als -31561.
Der "Fehler" muss also irgendwo weiter "innen" bei der Berechnung
passieren.
Aber ich verstehe nicht wo.
H. schrieb:
>> Dass signed/unsigned das Bitmuster nicht ändert, gilt ja nur, wenn nicht>> gleichzeitig eine Bitlängenänderung stattfindet, oder?> Bei einem Cast ändert sich das Bitmuster überhaupt nicht!
Hm, stimmt. Aber bei nachfolgenden Operationen oder Zuweisungen (was ja
wohl auch nur eine Operation ist) durchaus schon, oder?
> 2. Fall:>> result = (int8_t)-8 - (uint8_t)4;>> -8 wird ebenfalls zu 11111000 aber als signed interpretiert.>> Wäre es nun so, dass bei der Subtraktion beide Operanden als unsigned> interpretiert werden, wenn einer von ihnen unsigned ist, müsste der> weitere Ablauf dem Fall 1 entsprechen.
Was du anscheinend nicht bedacht hast, ist, dass nach deinen eigenen
Casts ja noch die "Integer Promotion Rules" zum Zuge kommen (und erst
dort kommt dann auch das "unsigned gewinnt"). Da aber die Ausgangswerte
alle in ein signed int passen, werden alle Operanden (in beiden Fällen)
nach signed int promotet (auch die uint8_t). Es gibt danach kein
unsigned mehr, das "gewinnen" könnte.
Aus Fall 1 wird:
(signed int) 248 - (signed int) 4
Aus Fall 2 wird:
(signed int) -8 - (signed int) 4
> Das merkwürdige Ergebnis berechnet also nur der AVR.
Weil auf dem Linux-System ein int größer als 16 Bit ist. Da werden bei
(adc - offset) die beiden Operanden auch erst jeweils zu einem signed
int promotet. Beim AVR sind die beiden Operanden bereits auf int-Größe
und werden daher nur nach unsigned promotet.
Auf Linux-System:
(int32_t)(ADC - offset) = -3
Auf AVR:
(int32_t)(ADC - offset) = 65533
Stefan Ernst schrieb:
> Was du anscheinend nicht bedacht hast, ist, dass nach deinen eigenen> Casts ja noch die "Integer Promotion Rules" zum Zuge kommen (und erst
Ja, anscheinend....
Seufz
:-)
Stefan Ernst schrieb:
> dort kommt dann auch das "unsigned gewinnt"). Da aber die Ausgangswerte> alle in ein signed int passen, werden alle Operanden (in beiden Fällen)> nach signed int promotet (auch die uint8_t). Es gibt danach kein> unsigned mehr, das "gewinnen" könnte.
Ja, aber moment, das weiss man ja nur in meinem Testprogramm!
Sonst müsste das ja zur Laufzeit entschieden werden?
Klaus W. schrieb:
> Ja, aber moment, das weiss man ja nur in meinem Testprogramm!> Sonst müsste das ja zur Laufzeit entschieden werden?
Warum? Es geht doch um den Typ, nicht den konkreten Inhalt. Es steht
doch immer zur Compilezeit fest, welchen Typ die Beteiligten haben, und
ob alle möglichen Werte diesen Typs durch ein signed int darstellbar
sind. In deinem Beispiel sind es uint8_t und int8_t. Egal was da konkret
drinsteht, es ist auf jeden Fall mit einem signed int darstellbar.
Stefan Ernst schrieb:
>> Das merkwürdige Ergebnis berechnet also nur der AVR.>> Weil auf dem Linux-System ein int größer als 16 Bit ist. Da werden bei
Auch wenn ich die Variable ausdrücklich mit int16_t definiere?
Hm, weil es ein 32 Bit System ist und die übrigen 16bit trotzdem
irgendwie da sind? Dann ist das also eher so eine Art
Mindestanforderung?
Da muss man bei irgendwelchen Bit-Shifts ja direkt auch aufpassen.
> (adc - offset) die beiden Operanden auch erst jeweils zu einem signed> int promotet. Beim AVR sind die beiden Operanden bereits auf int-Größe> und werden daher nur nach unsigned promotet.
Das hab ich nicht ganz kapiert. Also auf Linux rechnet die cpu obwohl
ich int16_t definiert habe mit 32 bit. Da muss aber doch dann
längenmäßig auch nichts mehr promoted werden, weil das dann doch schon
die ganze zeit 32 bit ist?
> Auf Linux-System:> (int32_t)(ADC - offset) = -3>> Auf AVR:> (int32_t)(ADC - offset) = 65533
Was ja auch wieder das gleiche 16-bit-muster ist, welches durch das
(int32_t) im beispiel:
[c]
offset=7;
adc=4;
i_charge = ((((int32_t)(adc - offset))*1330)/241);
[\c]
für die weiteren Operationen (*1330, /241) dann doch wieder als -3
interpretiert werden müsste?
Oder haben da die Promotion Rules noch eine höhere Priorität als das
cast obwohl es (außerhalb der klammer) erst danach kommt?
Rätsel über Rätsel...
Stefan Ernst schrieb:
> Klaus W. schrieb:>>> Ja, aber moment, das weiss man ja nur in meinem Testprogramm!>> Sonst müsste das ja zur Laufzeit entschieden werden?>> Warum? Es geht doch um den Typ, nicht den konkreten Inhalt. Es steht> doch immer zur Compilezeit fest, welchen Typ die Beteiligten haben, und> ob alle möglichen Werte diesen Typs durch ein signed int darstellbar> sind. In deinem Beispiel sind es uint8_t und int8_t. Egal was da konkret> drinsteht, es ist auf jeden Fall mit einem signed int darstellbar.
Hm, viellicht müsste man erst mal klären was man unter "darstellbar"
versteht.
240 z.b. kann ich mit einem vorzeichenlosen integer der länge 8 bit
darstellen. mit einem vorzeichenbehafteten integer der länge 8 bit aber
nicht.
Oder sehe ich da was falsch?
Klaus W. schrieb:
> Auch wenn ich die Variable ausdrücklich mit int16_t definiere?> Das hab ich nicht ganz kapiert. Also auf Linux rechnet die cpu obwohl> ich int16_t definiert habe mit 32 bit.
Das ist Bestandteil der "Integer Promotion Rules". Die Operanden haben
mindestens die Größe eines ints. Notfalls werden sie vor der Berechnung
eben entsprechend erweitert.
>> Auf AVR:>> (int32_t)(ADC - offset) = 65533>> Was ja auch wieder das gleiche 16-bit-muster ist, welches durch das> (int32_t) im beispiel:>> [c]> offset=7;> adc=4;> i_charge = ((((int32_t)(adc - offset))*1330)/241);> [\c]>> für die weiteren Operationen (*1330, /241) dann doch wieder als -3> interpretiert werden müsste?
Nein, denn das Ergebnis von (ADC - offset) ist unsigned. Also findet bei
der Erweiterung auf int32_t keine Sign-Extension statt.
Klaus W. schrieb:
> Hm, viellicht müsste man erst mal klären was man unter "darstellbar"> versteht.>> 240 z.b. kann ich mit einem vorzeichenlosen integer der länge 8 bit> darstellen. mit einem vorzeichenbehafteten integer der länge 8 bit aber> nicht.> Oder sehe ich da was falsch?
Es geht aber nicht um einen "vorzeichenbehafteten integer der länge 8
bit", sondern um ein "signed int", und der ist bei AVR nun mal 16 Bit
groß.
PS: So langsam bekomme ich das Gefühl, dass wir uns im Kreis drehen.
> PS: So langsam bekomme ich das Gefühl, dass wir uns im Kreis drehen.
Ja, ich auch :-).
Ich werde jetzt erst mal über alles in Ruhe nachdenken.
Erstmal vielen Dank für Deine Mühe!
Karl heinz Buchegger schrieb:
> Oder kurz gesagt: bei signed/unsigned gewinnt immer unsigned.
Nochmal kurzgefasst um Missverständnissen vorzubeugen: Das stimmt in
dieser Form nur, wenn beide Seiten die gleiche Anzahl Bits haben, und
diese mindestens der von int/unsigned entspricht.
Hallo!
So, zwischenzeitlich habe ich mich nochmal etwas mit der Materie
beschäftigt.
@Stefan Ernst:
> PS: So langsam bekomme ich das Gefühl, dass wir uns im Kreis drehen.
Im Kreis nicht, aber ziemlich aneinander vorbei :-)
Natürlich will ich die Geduld von niemand überstrapazieren und ich
bitte meine Unwissenheit zu entschuldigen. :-)
Zumindest meine ich jetzt begriffen zu haben weshalb der Compiler im
eingangs erwähnten Beispiel zu diesem Ergebnis kommt. Also nochmal:
1
int16_toffset=7;// So war die ursprüngliche Definition, aber wie
2
// nun gelernt, ist int sowieso gleich int16_t auf
3
// AVR.
4
uint16_tadc=4;
5
int16_ti_charge;
6
i_charge=((((int32_t)(adc-offset))*1330)/241);
Zuerst wird also (adc - offset) bearbeitet.
Nachdem es hier keine expliziten Casts gibt, kommen die Promotion Rules
zum Einsatz. Auf der Suche nach einer entsprechenden Referenz bin ich
auf ein PDF mit dem Titel "Programming languages - C, Committee Draft
August 3, 1998 WG14/N843" gestoßen.
Gibt es davon inzwischen auch eine Final Version? Wenn ich richtig
verstehe, ist C9x ein ISO Standard und wenn man so umgangssprachlich von
ANSI-C spricht, stimmt das gar nicht wirklich?
Gibt es eigentlich offizielle Quellen für derartige Referenzdokumente?
Ich schätze, wenn man ernsthaft C programmieren will, kommt man nicht
drum rum sich damit auseinander zu setzen.
However... darin findet sich folgende Aussage, die in meinem Fall
zutreffen dürfte:
"Otherwise, if the operand that has unsigned integer type has rank
greater or equal to the rank of the type of the other operand,
then the operand with signed integer type is converted to the type
of the operand with unsigned integer type."
Ich nehme an das ist es, was Karl Heinz mit
"Oder kurz gesagt: bei signed/unsigned gewinnt immer unsigned."
gemeint hat.
(adc - offset) wird demnach zu unsigned int (hier 16 bit).
Nun kommt mein (int32_t) cast.
Hier wird aber offenbar ZUERST in 32bit gewandelt und erst DANN
signed drauss gemacht. Weswegen die neu hinzgekommenen 16 bit
mit "0" und nicht mit "1" aufgefüllt werden.
Also das was Stefan Ernst mit
" Also findet bei der Erweiterung auf int32_t keine Sign-Extension
statt." gemeint hat.
Naja, der Rest ist klar (denke ich jedenfalls): aus der kleinen
negativen Zahl wird eine recht große Positive, die nach der
Multiplikation noch größer wird und deren Ergebnis schließlich nicht
mehr in die 16 bit Variable i_charge passt.
Ich vermute da werden die höherwertigen Bits einfach abgeschnitten und
es kommt irgend ein Unsinn dabei raus?
Ok, so weit so klar. Trotzdem würde ich die ganze Thematik gerne noch
etwas abstrakter zu fassen bekommen.
Also erstmal zu den integer Typen grundsätzlich:
Ich fand die Bitlängen Zuordnungen in C schon immer etwas verwirrend
(ist es vermutlich auch) und habe mich daher in der Mikrocontroller
Programmierung auf die (u)int(8/16/..)_t Typen "verlassen" in der
Meinung damit quasi feste Bitlängen zu bekommen.
Mal einen Blick in stdint.h werfen:
Daraus kann man für den AVR folgendes destillieren:
1
typedefsignedcharint8_t;
2
typedefunsignedcharuint8_t;
3
typedefsignedintint16_t;
4
typedefunsignedintuint16_t;
5
typedefsignedlongintint32_t;
6
typedefunsignedlongintuint32_t;
7
typedefsignedlonglongintint64_t;
8
typedefunsignedlonglongintuint64_t;
Ist stdint.h eigentlich erst eine Neuerscheinung von C99?
Unter Linux wo int 32bit lang ist (vermutlich einfach wegen der
intel Achitektur), müsste stdint.h dementsprechend anders aussehen.
Ja, tatsächlich:
1
typedefsignedcharint8_t;
2
typedefshortintint16_t;
3
typedefintint32_t;
4
typedeflongintint64_t;
5
typedeflonglongintint64_t;
6
typedefunsignedcharuint8_t;
7
typedefunsignedshortintuint16_t;
8
typedefunsignedintuint32_t;
9
typedefunsignedlongintuint64_t;
10
typedefunsignedlonglongintuint64_t;
Verstehe ich aber richtig, dass die neuen C99 Integer Typen eigentlich
auch wieder nur Mindestanforderungen sind? Wenn ein signed char
(= int8_t) in einem Intel-Register steht, wird das auch 32Bit
beanspruchen?
Ok, zurück zu den Casts. Interessanter Weise funktioniert
das obige Beispiel z.B. wenn man es so modifiziert:
Hier wird also aus dem uint (adc - offset) erstmal wieder int
und dann bei der Erweiterung auf 32bit auch korrekt mit "1"
gefüllt.
Am sinnvollsten wäre vermutlich zu schreiben:
Trotzdem würde ich die Verwendung der Casts durch den Compiler gerne
irgendwie in allgemeingültigen Regeln formulieren.
* (casts) haben eine höhere Priorität als die Promotion Rules?
D.h. die Promotion Rules werden erst auf das Ergebnis des
Casts angewendet?
* Bei einem (cast) wird zuerst die Bitlänge entsprechend
der signedness des ursprünglichen Datentyps angepasst und
DANN das neue (signed/unsigned) Label "draufgeklebt"?
* Heisst das, wenn ich mittels Cast Bitlänge und
Signedness gleichzeitig verändern will, muss ich das
immer mit zwei casts machen?
Also erstmal korrekte signedness "einstellen", damit die anschließende
Bitlängenanpassung mit korrektem Vorzeichen erfolgt?
* In mancher C-Literatur ist von impliziter Typanpassung die Rede.
Ich nehme an damit sind eigentlich die Promotion Rules gemeint?
H. (Gast) schreibt:
"Bei einem Cast ändert sich das Bitmuster überhaupt nicht!"
Stimmt das so pauschal wirklich?
Ich habe mir einen Cast bisher immer so als unären Operator vorgestellt.
Sofern ich nur signed in unsigned oder umgekehrt caste, ändert sich
natürlich nichts an dem Bitmuster.
Wenn sich die Bitlänge verändert, ändert sich damit aber doch auch
das Bitmuster. Falls dabei gleichzeitig noch signed/unsigned
im Spiel ist, z.T. doch recht erheblich?
Kann es sein dass ich grundsätzlich irgend was falsch mache,
wenn ich mir über diese Dinge so viele Gedanken mache(n muss)?
Viele Grüße!
Klaus
Klaus W. schrieb:
> Nachdem es hier keine expliziten Casts gibt, kommen die Promotion Rules> zum Einsatz.
Nein. Die Promotion Rules kommen immer zum Einsatz. Ein expliziter Cast
ändert nur die Ausgangsbasis. Man könnte es auch so betrachten, dass du
mit dem Cast einen "Zwischenschritt" einschiebst.
> Wenn ich richtig> verstehe, ist C9x ein ISO Standard und wenn man so umgangssprachlich von> ANSI-C spricht, stimmt das gar nicht wirklich?
ANSI-C ist einfach nur ein älterer Standard.
> Gibt es eigentlich offizielle Quellen für derartige Referenzdokumente?http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf> * (casts) haben eine höhere Priorität als die Promotion Rules?> D.h. die Promotion Rules werden erst auf das Ergebnis des> Casts angewendet?> * Bei einem (cast) wird zuerst die Bitlänge entsprechend> der signedness des ursprünglichen Datentyps angepasst und> DANN das neue (signed/unsigned) Label "draufgeklebt"?> * Heisst das, wenn ich mittels Cast Bitlänge und> Signedness gleichzeitig verändern will, muss ich das> immer mit zwei casts machen?> Also erstmal korrekte signedness "einstellen", damit die anschließende> Bitlängenanpassung mit korrektem Vorzeichen erfolgt?> * In mancher C-Literatur ist von impliziter Typanpassung die Rede.> Ich nehme an damit sind eigentlich die Promotion Rules gemeint?
Man könnte vielleicht an dem einen oder anderen Detail deiner Regeln
rummäkeln (oder das eine oder andere Detail ergänzen), aber ich denke
vom Prinzip her hast du es verstanden.
> H. (Gast) schreibt:> "Bei einem Cast ändert sich das Bitmuster überhaupt nicht!">> Stimmt das so pauschal wirklich?
Nein. Hängt natürlich auch davon ab, auf was genau sich das "ändern"
bezieht. Z.B. bei einer Erweiterung von 8 auf 16 Bit kann sich das
"Gesamt-Bitmuster" natürlich ändern, wobei aber das Bitmuster der
ursprünglichen 8 Bit sich nicht ändert.
Und wenn Floats ins Spiel kommen, stimmt die Aussage gar nicht mehr.
> Kann es sein dass ich grundsätzlich irgend was falsch mache,> wenn ich mir über diese Dinge so viele Gedanken mache(n muss)?
Nein. Das Nichtbeachten/Nichtverstehen der Promotion Rules ist nicht
selten der Grund, wenn etwas nicht so läuft, wie man denkt dass es
laufen müsste.
Klaus W. schrieb:
> Nachdem es hier keine expliziten Casts gibt, kommen die Promotion Rules> zum Einsatz. Auf der Suche nach einer entsprechenden Referenz bin ich> auf ein PDF mit dem Titel "Programming languages - C, Committee Draft> August 3, 1998 WG14/N843" gestoßen.> Gibt es davon inzwischen auch eine Final Version?
Natürlich, die muss man aber kaufen :-)
> Ich schätze, wenn man ernsthaft C programmieren will, kommt man nicht> drum rum sich damit auseinander zu setzen.
Nein, eher nicht.
Diese Dokumente sind gut für Compilerbauer und wenn es darum geht
Streitfragen auf 'alademischen Niveau' zu entscheiden. Als normaler,
durchaus guter Programmierer, benötigt man diese Dokumente eigentlich so
gut wie nie. Die Information darin, ist zu verstreut und oftmals auch so
richtig 'juristisch' verpackt, dass man schon Übung haben muss, um das
Lesen zu können.
Mit einer der üblichen Standard-Literatur-Quellen ist man besser
bedient.
> "Otherwise, if the operand that has unsigned integer type has rank> greater or equal to the rank of the type of the other operand,> then the operand with signed integer type is converted to the type> of the operand with unsigned integer type.">> Ich nehme an das ist es, was Karl Heinz mit> "Oder kurz gesagt: bei signed/unsigned gewinnt immer unsigned."> gemeint hat.
Yep.
Übrigens ein gutes Beispiel für die 'juristische Schreibweise'.
> (adc - offset) wird demnach zu unsigned int (hier 16 bit).
Genauer:
adc ist bereits ein unsigned int
offset wird zu einem unsigned umgepfriemelt
Dann wird die Subtraktion gemacht
Und natürlich hat auch das Ergebnis der Subtraktion den Typ unsigned int
> Nun kommt mein (int32_t) cast.> Hier wird aber offenbar ZUERST in 32bit gewandelt und erst DANN> signed drauss gemacht.
Genau.
> Weswegen die neu hinzgekommenen 16 bit> mit "0" und nicht mit "1" aufgefüllt werden.>> Also das was Stefan Ernst mit>> " Also findet bei der Erweiterung auf int32_t keine Sign-Extension> statt." gemeint hat.>
Ganz genau
> Naja, der Rest ist klar (denke ich jedenfalls): aus der kleinen> negativen Zahl
welche negative Zahl. Da ist keine negative Zahl. Das Ergebnis von adc -
offset ist Kraft Definition immer positiv, da unsigned.
Allerdings gibt es bei der Subtraktion einen Unterlauf. Und wie der im
Falle unsigned gehandhabt wird, steht auch im Standard: Es wird Modulo
Arithmetik betrieben. Und so kommt es, dass bei dieser Subtraktion eine
große positive Zahl herauskommt.
> wird eine recht große Positive, die nach der> Multiplikation noch größer wird und deren Ergebnis schließlich nicht> mehr in die 16 bit Variable i_charge passt.
Das könnte hinkommen (müsste man jetzt nachrechnen).
> Ich vermute da werden die höherwertigen Bits einfach abgeschnitten und> es kommt irgend ein Unsinn dabei raus?
Ganz genau.
> Ich fand die Bitlängen Zuordnungen in C schon immer etwas verwirrend> (ist es vermutlich auch) und habe mich daher in der Mikrocontroller> Programmierung auf die (u)int(8/16/..)_t Typen "verlassen" in der> Meinung damit quasi feste Bitlängen zu bekommen.
Das ist auch so.
> Ist stdint.h eigentlich erst eine Neuerscheinung von C99?
Ja. Nach langem Bitten und Betteln der Cummunity
> Verstehe ich aber richtig, dass die neuen C99 Integer Typen eigentlich> auch wieder nur Mindestanforderungen sind?
Nein, das verstehst du falsch.
In dem Fall gilt tatächlich: Drinn ist, was drauf steht.
> Wenn ein signed char> (= int8_t) in einem Intel-Register steht, wird das auch 32Bit> beanspruchen?
Na, ja. Das ist klar. In einem Register hat natürlich dort jeder Wert 32
Bit, weils anders nicht geht. Aber C kennt keine CPU Register. Die
Bitlänge interessiert hier nur beim Speichern und Laden im RAM. Alles
andere kann der Compiler (fast) machen wie er will, solange er sich an
die Verarbeitungsregeln hält.
> H. (Gast) schreibt:> "Bei einem Cast ändert sich das Bitmuster überhaupt nicht!">> Stimmt das so pauschal wirklich?
Nein.
Nur in diesem speziellen signed/unsigned Fall, wenn die Bitlängen schon
stimmen.
> Kann es sein dass ich grundsätzlich irgend was falsch mache,> wenn ich mir über diese Dinge so viele Gedanken mache(n muss)?
Das kann sein.
Generell solltest du nach der Devise verfahren:
Wenn du viele Casts verwenden musst, dann zurücklehnen, überlegen warum
das so ist und die Datentypen aller Variablen noch mal auf
SInnhaftigkeit durchgehen.
Ein Cast ist eine Waffe! Damit hebelst du das Typsystem des Compilers
aus. Daher: sparsam verwenden.
Eine Ergänzung hätte ich da noch :-)
> i_charge = ((((int32_t)(int16_t)(adc - offset))*1330)/241);>> Am sinnvollsten wäre vermutlich zu schreiben:>> i_charge = ((int32_t)((int16_t)ADC - offset)*1330)/241;
Ich würde folgendes für "am sinnvollsten" halten:
1
i_charge=(((int32_t)ADC-offset)*1330)/241;
Erstens ist das etwas übersichtlicher, und zweitens funktioniert das
auch dann noch, wenn der ADC die 16 Bit voll nutzen würde.
@Stefan Ernst:
> Nein. Die Promotion Rules kommen immer zum Einsatz. Ein expliziter Cast> ändert nur die Ausgangsbasis. Man könnte es auch so betrachten, dass du> mit dem Cast einen "Zwischenschritt" einschiebst.
Ja, so hatte ich das gemeint.
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
Danke!
> Und wenn Floats ins Spiel kommen, stimmt die Aussage gar nicht mehr.
Ja, das ist eh klar...
@ Karl heinz Buchegger
>> Gibt es davon inzwischen auch eine Final Version?>Natürlich, die muss man aber kaufen :-)
Ja wie jetzt :-). Stefan hat doch oben einen Link gepostet?
> Mit einer der üblichen Standard-Literatur-Quellen ist man besser bedient.
Hm, also hier liegen auch so ein paar C-Schinken herum, aber diese
Thematik habe ich bislang noch in keinem davon erschöpfend behandelt
gefunden.
>Genauer:> adc ist bereits ein unsigned int> offset wird zu einem unsigned umgepfriemelt>>Dann wird die Subtraktion gemacht>Und natürlich hat auch das Ergebnis der Subtraktion den Typ unsigned int
Richtig. So hatte ich das schon auch verstanden.
>> Naja, der Rest ist klar (denke ich jedenfalls): aus der kleinen>> negativen Zahl>welche negative Zahl. Da ist keine negative Zahl. Das Ergebnis von adc ->offset ist Kraft Definition immer positiv, da unsigned.
Mhmm, jaaa, aber ICH sehe das Bitmuster trotzdem als "kleine negative
Zahl".
>Allerdings gibt es bei der Subtraktion einen Unterlauf. Und wie der im>Falle unsigned gehandhabt wird, steht auch im Standard: Es wird Modulo>Arithmetik betrieben. Und so kommt es, dass bei dieser Subtraktion eine>große positive Zahl herauskommt.
Ja, aber das Bitmuster ist doch wieder das gleiche?
Na, bevor wir jetzt Gefahr laufen aufgrund meiner mangelhaft präzisen
Beschreibungen wieder aneinander vorbeireden :-). Ich denke ich hab's
schon kapiert.
@Stefan:
> Ich würde folgendes für "am sinnvollsten" halten:> i_charge = (((int32_t)ADC - offset)*1330)/241;>> Erstens ist das etwas übersichtlicher, und zweitens funktioniert das>auch dann noch, wenn der ADC die 16 Bit voll nutzen würde.
Ja, das entspricht wohl auch der Philosophie von Karl Heinz.
Was ich allerdings eigentlich damit bezwecken wollte, war möglichst
wenig Code im Flash zu erzeugen, aber eine höhere Genauigkeit zu
erreichen als bei reiner 16bit Arithmetik. Daher dachte ich die Addition
noch in 16 bit auszuführen, die Multiplikation in 32-bit und das
Ergebnis möglichst schnell wieder auf 16 bit zu bringen.
viele Grüße,
Klaus
Klaus W. schrieb:
>>> Gibt es davon inzwischen auch eine Final Version?>>Natürlich, die muss man aber kaufen :-)> Ja wie jetzt :-). Stefan hat doch oben einen Link gepostet?
Naja, das ist ja auch "nur" ein Draft. Was "richtig offizielles" muss
man wohl kaufen.
Stefan Ernst schrieb:
> Klaus W. schrieb:>>>> Gibt es davon inzwischen auch eine Final Version?>>>Natürlich, die muss man aber kaufen :-)>> Ja wie jetzt :-). Stefan hat doch oben einen Link gepostet?>> Naja, das ist ja auch "nur" ein Draft. Was "richtig offizielles" muss> man wohl kaufen.
Hm, ok, aber das ist ja schon fast offiziell...
"The lastest publically available version of the standard is the
combined C99 + TC1 + TC2, WG14 N1124, dated 2005-05-06. This is a WG14
working paper, but it reflects the consolidated standard at the time of
issue."
Was dann darin fehlt ist
"Technical Corrigendum 3 (ISO/IEC 9899:1999 Cor. 3:2007(E)) was
published in 2007"
Aber es heisst weiter:
"It can be obtained free of charge fro ISO. "
Mahlzeit!
Ja, der Thread ist alt! Aber er hat mir gerade sehr geholfen, als ich
mir bei einem ach so einfachen Teil meines Quellcodes stundenlang einen
abgebrochen habe.
Ich sage nur:
int32_t ergebnis = 0;
ergebnis = (int32_t)(int16_t)"weitere operationen";
ergebnis = korrektes, vorzeichenbehaftetes ergebnis!
Da wäre ich nieeeee drauf gekommen und in meinem super dicken C-Buch
steht auch nix dazu drin.
Also vielen Dank für diesen Thread! :)