Forum: Mikrocontroller und Digitale Elektronik C Frage zu Subtraktion


von Thorsten S. (whitejack)


Lesenswert?

Hi,

ich habe zwei Variablen, nicht vorzeichenbehaftet:

uint8_t  a=0, b=0; //können werte von 0-255 enthalten.

Ich möchte b von a abziehen und auf das Ergebnis "reagieren"

if ((a-b)>=0)
{
  dies
}
else
{
  das
}

Wenn a kleiner b ist, dann wäre das Ergebnis ja negativ und damit 
kleiner Null. Wird das hierbei berücksichtigt oder muss ich noch irgenwo 
"casten"?

Wenn das so nicht geht, würde das dann gehen?

if ((int16_t(a-b))>=0)

Oder müsste ich es so machen?

if ( ( (int16_t a)-(int16_t b) ) >=0 )

Danke

Gruß
TS

von Wolfgang (Gast)


Lesenswert?

Thorsten S. schrieb:
> Wenn a kleiner b ist, dann wäre das Ergebnis ja negativ und damit
> kleiner Null.

Wenn du unsigned Datentypen verwendest, gibt es keine negativen 
Ergebnisse.
2 - 3 = 255

von EAF (Gast)


Lesenswert?

Thorsten S. schrieb:
> Oder müsste ich es so machen?
>
> if ( ( (int16_t a)-(int16_t b) ) >=0 )
fast....
if((((int16_t)a)-((int16_t)b))>=0)

von Christian K. (the_kirsch)


Lesenswert?

Die Berechnung wird natürlich ohne Probleme durchgeführt.
Wenn das Ergebnis negativ sein würde, wird das wieder auf Positive 
Zahlen abgebildet.
Bei 8-Bit Variablen bist du dann Mathematisch ausgedrückt in einem 
"Endlicher Körper" von 2^8=256. Es gibt nur Zahlen zwischen 0 und 255. 
Das Ergebnis wird automatisch Modulo 256 gerechnet.

Beispiel:
11 - 42 = -31 => -31 Mod 256 = 225

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Einfach nicht voneinander abziehen oder erst nach dem Vergleich 
abziehen:
1
if(a >= b) {
2
    // Differenz garantiert >= 0
3
} else {
4
    // Differenz negativ
5
}

von Thorsten S. (whitejack)


Lesenswert?

Wolfgang schrieb:
> Wenn du unsigned Datentypen verwendest, gibt es keine negativen
> Ergebnisse.
> 2 - 3 = 255

Ich dachte das wäre aus meinen Ansätzen mit dem Type Cast zu einem 
sigend Typ klar.

EAF schrieb:
> if((((int16_t)a)-((int16_t)b))>=0)

Danke.

Hannes J. schrieb:
> Einfach nicht voneinander abziehen oder erst nach dem Vergleich
> abziehen:

Genial, manchmal hat man einen Knoten im Hirn...

Gruß
TS

von Wolfgang (Gast)


Lesenswert?

Thorsten S. schrieb:
> Wolfgang schrieb:
>> Wenn du unsigned Datentypen verwendest, gibt es keine negativen
>> Ergebnisse.
>> 2 - 3 = 255
>
> Ich dachte das wäre aus meinen Ansätzen mit dem Type Cast zu einem
> sigend Typ klar.

Nur weil du mit (int16_t(a-b)) aus den bspw. 255 ein signed machst, wird 
das im Nachhinein nicht negativ.
Warum rechnest du die Differenz aus, wenn du wissen willst, ob a>=b ist?

von Rolf M. (rmagnus)


Lesenswert?

Thorsten S. schrieb:
> uint8_t  a=0, b=0; //können werte von 0-255 enthalten.

> if ((a-b)>=0)

> Wenn a kleiner b ist, dann wäre das Ergebnis ja negativ und damit
> kleiner Null. Wird das hierbei berücksichtigt oder muss ich noch irgenwo
> "casten"?

Was heißt "berücksichtigt"?
Auf Grund der Integer Promotion werden a und b erst mal nach (signed) 
int konvertiert, bevor die Rechnung ausgeführt wird. Das passiert in C 
grundsätzlich mit allem, was kleiner ist als int. Das Ergebnis kann also 
sehr wohl negativ sein.

: Bearbeitet durch User
von P. S. (namnyef)


Lesenswert?

In diesem Fall muss man gar nichts machen, weil die Operanden sowieso 
nach "signed integer" promoted werden.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Rolf M. schrieb:
> Auf Grund der Integer Promotion

Bullshit. Wenn sowohl a als auch b vom Typ uint8_t sind [1] dann 
wird für die Rechnung (a-b) gar nichts promoted. Für den Vergleich mit 
dem int Literal 0 würde ein Compiler vielleicht promoten. Aber da 
liegt das Kind ja bereits im Brunnen.

Und ein vernünftiger Compiler (jeder?) würde erkennen, daß ein uint8_t 
(bzw jeder unsigned) immer >= 0 ist. Er würde also den ganzen Vergleich 
wegoptimieren und eine Warnung ausspucken.


[1] wohl unsigned char auf allen nicht-exotischen Platformen

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Axel S. schrieb:
> Bullshit. Wenn sowohl a als auch b vom Typ uint8_t sind [1] dann
> wird für die Rechnung (a-b) gar nichts promoted.

Lern die Sprache, bevor du so einen Unsinn von dir gibst. Die Promotion 
findet statt!

von (prx) A. K. (prx)


Lesenswert?

Bei
  uint8_t a=0, b=0;
entspricht die Rechnung
  (a - b)
aufgrund der Promotion Regeln dem Ausdruck
  ((int)a - (int)b)
weil, simpel ausgedrückt, alles kleiner als int als int gerechnet wird 
und int definitiv grösser als uint8_t ist. Anders wärs bei
  unsigned a=0, b=0;
weil a und b dabei nicht kleiner als int sind.

von Rolf M. (rmagnus)


Lesenswert?

Axel S. schrieb:
> Wenn sowohl a als auch b vom Typ uint8_t sind [1] dann wird für
> die Rechnung (a-b) gar nichts promoted.

Das ist kompletter …

> Bullshit.

> Und ein vernünftiger Compiler (jeder?) würde erkennen, daß ein uint8_t
> (bzw jeder unsigned) immer >= 0 ist. Er würde also den ganzen Vergleich
> wegoptimieren und eine Warnung ausspucken.

Auch das ist …

> Bullshit.

von EAF (Gast)


Lesenswert?

Axel S. schrieb:
> Bullshit. Wenn sowohl a als auch b vom Typ uint8_t sind [1] dann
> wird für die Rechnung (a-b) gar nichts promoted. Für den Vergleich mit
> dem int Literal 0 würde ein Compiler vielleicht promoten. Aber da
> liegt das Kind ja bereits im Brunnen.
>
> Und ein vernünftiger Compiler (jeder?) würde erkennen, daß ein uint8_t
> (bzw jeder unsigned) immer >= 0 ist. Er würde also den ganzen Vergleich
> wegoptimieren und eine Warnung ausspucken.
Du irrst

Avr gnu++c17 -Os
1
  byte a =  1;
2
  byte b = 44;
3
4
  if(a-b < 0) cout << F("a-b")<< endl;
5
  if(b-a < 0) cout << F("b-a")<< endl;

Ausgabe:
1
a-b

von Stefan F. (Gast)


Lesenswert?

Um das zu verstehen muss man wissen, dass der C-Compiler 
unterschiedliche Algorithmen (Rechen-Methoden) für unterschiedliche 
Datentypen anwendet.

Wenn die Operanden alle "unsinged int" sind, verwendet er einen 
Algorithmus der nur damit funktioniert und auch nur ein solches Ergebnis 
liefern kann.

Subtraktionen können falsche Ergebnisse liefern, wenn der Wertebereich 
des Ergebnisses überschritten wird. In diesem Fall eben bei negativen 
Zahlen.

Bei Additionen und Multiplikationen können Überläufe stattfinden. 
"unsigned int" ist normalerweise 16 Bit. Wenn du 20000·40 berechnest, 
hast du so einen Überlauf. Es sei denn, du vastest wenigstens einen 
Operand auf "long int". Dann wird nämlich der größere Algorithmus für 32 
Bit verwendet.

Bei Divisionen on Integer Zahlen werden keine Nachkomma-Stellen 
berechnet, weil der Algorithmus das nicht kann. Ist hingegen einer der 
Operanden eine Fließkommazahl, dann wird der komplexere Algorithmus 
verwendet, der Nachkomma-Stellen im Ergebnis liefert.

Zusätzlich muss natürlich das Ergebnis in eine dazu passende Variable 
geschrieben werden, sonst wird es nochmal umgewandelt, was eventuell mit 
Verlusten behaftet ist.

> int8_t ergebnis = 3.0 * 5.0;
Das ist OK, weil das Ergebnis 15 ist und in die Variable passt.

> int8_t ergebnis = -3.0 * 5.0;
Das ist nicht OK, weil die Variable keine Negativen Zahlen speichern 
kann. Das Resultat wird fehlerhaft sein.

Inden meisten Programmiersprachen (auch C, allerdings nicht auf AVR) ist 
die Division durch 0 die einzige ungültige Operation, die zu einem 
Programmabbruch führt. Alle anderen ungültigen Kombinationen führen 
lediglich zu kaputten Ergebnissen, das Programm läuft aber trotzdem 
weiter.

Wahrscheinlich gibt es noch mehr Ausnahmen, aber so habe ich es mir 
gemerkt.

von mh (Gast)


Lesenswert?

EAF schrieb:
> Avr gnu++c17 -Os

Mal davon abgesehen, dass du C++ benutzt, was genau ist dein "byte"? Es 
ist kein std::byte.

von Stefan F. (Gast)


Lesenswert?

EAF schrieb:
>   byte a =  1;
>   byte b = 44;
>   if(a-b < 0) cout << F("a-b")<< endl;
>   if(b-a < 0) cout << F("b-a")<< endl;
>
> Ausgabe:a-b

Das überrascht mich. Wie ist das möglich?

von Oliver S. (oliverso)


Lesenswert?

Stefan ⛄ F. schrieb:
> …

Trink erst mal einen Kaffee, boote anständig durch, und dann schreibste 
das nochmal. Ohne all den Quatsch.

Oliver

von (prx) A. K. (prx)


Lesenswert?

Stefan ⛄ F. schrieb:
> "unsigned int" ist normalerweise 16 Bit

Das hat noch nicht einmal in jeder Zeit gestimmt, um die es in der 
Retro-Diskussion im Nachbarthread geht. Stimmen tut nur: es ist 
mindestens 16 Bit breit. Und so selten sind 32-Bitter auch in 
Mikrocontrollern nicht.

: Bearbeitet durch User
von EAF (Gast)


Lesenswert?

mh schrieb:
> was genau ist dein "byte"?
1
if(is_same<byte,unsigned char>())cout << F(" byte ist unsigned char") << endl;

Ausgabe:
1
byte ist unsigned char

von (prx) A. K. (prx)


Lesenswert?

Oliver S. schrieb:
> Trink erst mal einen Kaffee, boote anständig durch, und dann schreibste
> das nochmal. Ohne all den Quatsch.

Yep. Habe ich gestern eine Feier verpasst? Eine mit genug Alkohol, um 
die Birnen noch jetzt erheblich zu beeinflussen. Ich bin auch ein wenig 
über etliche der Antworten verwirrt. Ist immerhin eine Standardfrage, 
auf die es eigentlich eine Standardantwort gibt. Statt dessen kommt erst 
einmal ein Bündel Quatsch.

von EAF (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Das überrascht mich. Wie ist das möglich?
1
  byte a =  1;
2
  byte b = 44;
3
  auto c = a-b;
4
  if(is_same<decltype(c),int>())cout << F("ist int") << endl;
Ausgabe: ist int

von (prx) A. K. (prx)


Lesenswert?

Stefan ⛄ F. schrieb:
>> int8_t ergebnis = -3.0 * 5.0;
> Das ist nicht OK, weil die Variable keine Negativen Zahlen speichern
> kann. Das Resultat wird fehlerhaft sein.

Oh jeh...

von Norbert (Gast)


Lesenswert?

Guten Morgen.
Mal am praktischen Beispiel getestet:
1
#include <stdint.h>
2
#include <stdio.h>
3
// gcc -Wall -Wextra -pedantic -std=c11 -Os -o "x" "x.c"
4
uint8_t f1(uint8_t a, uint8_t b) { return a - b; }
5
int f2(uint8_t a, uint8_t b) { return a - b; }
6
int main(void) {
7
    uint8_t a = 1;
8
    uint8_t b = 2;
9
    printf("%d %u\n", a-b, a-b);
10
    printf("%d %u\n", f1(a,b), f1(a,b));
11
    printf("%d %u\n", f2(a,b), f2(a,b));
12
}
13
// -1 4294967295
14
// 255 255
15
// -1 4294967295
Da finde ich Ausgabezeile Zwei recht interessant.

von (prx) A. K. (prx)


Lesenswert?

Norbert schrieb:
> Da finde ich Ausgabezeile Zwei recht interessant.

Weshalb?

von STK500-Besitzer (Gast)


Lesenswert?

Norbert schrieb:
> Da finde ich Ausgabezeile Zwei recht interessant.

Warum?

von EAF (Gast)


Lesenswert?

Norbert schrieb:
> Da finde ich Ausgabezeile Zwei recht interessant.
Ich nicht.
Die implizite Type Konvertierung hat doch voll geklappt.
Nach Lehrbuch.

von Norbert (Gast)


Lesenswert?

(prx) A. K. schrieb:
> Norbert schrieb:
>> Da finde ich Ausgabezeile Zwei recht interessant.
>
> Weshalb?
Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet 
wird, sollte -1 zurück gegeben werden.
So aber wird uint8_t gerechnet und dann erst nach int konvertiert.

von W.S. (Gast)


Lesenswert?

Thorsten S. schrieb:
> Ich möchte b von a abziehen und auf das Ergebnis "reagieren"
>
> if ((a-b)>=0)

Du möchtest also partout mit dem Kopf durch die Wand.
Aha, darum wird in den folgenden Beiträgen so unsäglich viel 
herumgecastet.

Warum nicht einfach ein simpler Vergleich 'if (a>=b)...' oder warum 
unbedingt als Operanden zweimal unsigned char und kein simpler int? Mußt 
du so elend mit RAM sparen, wo das Ergebnis ohnehin negativ sein kann 
und auch betragsmäßig größer als ein signed char?

W.S.

von EAF (Gast)


Lesenswert?

Norbert schrieb:
> So aber wird uint8_t gerechnet und dann erst nach int konvertiert.

Falsch!
Es wird mit int gerechnet und beim return nach uint8_t konvertiert.

von Norbert (Gast)


Lesenswert?

EAF schrieb:
> Norbert schrieb:
>> So aber wird uint8_t gerechnet und dann erst nach int konvertiert.
>
> Falsch!
> Es wird mit int gerechnet und beim return nach uint8_t konvertiert.
… obwohl ein int Wert zurückgegeben wird? Interessant!

von mh (Gast)


Lesenswert?

Norbert schrieb:
> (prx) A. K. schrieb:
>> Norbert schrieb:
>>> Da finde ich Ausgabezeile Zwei recht interessant.
>>
>> Weshalb?
> Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet
> wird, sollte -1 zurück gegeben werden.
> So aber wird uint8_t gerechnet und dann erst nach int konvertiert.

Nö ...

Oder vielleicht doch, dank UB?

von Rolf M. (rmagnus)


Lesenswert?

Norbert schrieb:
> (prx) A. K. schrieb:
>> Norbert schrieb:
>>> Da finde ich Ausgabezeile Zwei recht interessant.
>>
>> Weshalb?
> Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet
> wird, sollte -1 zurück gegeben werden.

Wie soll da jemals -1 zurück gegeben werden können? Die Funktion f1 hat 
als return-Typ uint8_t. Der kann nicht -1 sein.

> uint8_t f1(uint8_t a, uint8_t b) { return a - b; }

Hier wird a - b in int gerechnet. Das Ergebnis ist vom Typ int und hat 
für a=1 und b=2 den Wert -1. Allerdings hast du als Return-Typ der 
Funktion ja uint8_t festgelegt, daher muss der Wert erst in diesen Typ 
konvertiert werden.

> So aber wird uint8_t gerechnet und dann erst nach int konvertiert.

Falsche Schlussfolgerung.

(prx) A. K. schrieb:
> Ich bin auch ein wenig über etliche der Antworten verwirrt. Ist immerhin
> eine Standardfrage, auf die es eigentlich eine Standardantwort gibt. Statt
> dessen kommt erst einmal ein Bündel Quatsch.

Das hab ich mir auch gedacht. Ist die integer promotion und wo sie sich 
wie auswirkt, wirklich so vielen hier unbekannt?

von (prx) A. K. (prx)


Lesenswert?

Norbert schrieb:
> Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet
> wird, sollte -1 zurück gegeben werden.

Beachte: Die zweite Ausgabezeile ist jene mit f1(). Eine Funktion, deren 
Returnwert als uint8_t deklariert ist.

von EAF (Gast)


Lesenswert?

Norbert schrieb:
> obwohl ein int Wert zurückgegeben wird? Interessant!
verstehst du dein eigenes Programm nicht?


Norbert schrieb:
> uint8_t f1(uint8_t a, uint8_t b) { return a - b; }
Wo wird da ein int zurückgegeben?
Die Antwort: Nirgendwo!

von mh (Gast)


Lesenswert?

(prx) A. K. schrieb:
> Norbert schrieb:
>> Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet
>> wird, sollte -1 zurück gegeben werden.
>
> Beachte: Die zweite Ausgabezeile ist jene mit f1(). Eine Funktion, deren
> Returnwert als uint8_t deklariert ist.

Und der Wert wird dann nach int promoted für den Aufruf von printf, 
welcher mit einem falschen Conversion Specifier stattfindet...

von Stefan F. (Gast)


Lesenswert?

(prx) A. K. schrieb:
> Das hat noch nicht einmal in jeder Zeit gestimmt, um die es in der
> Retro-Diskussion im Nachbarthread geht.

Offenbar hast du Recht. Ich habe es gerade an PC ausgetestet.

Damit muss ich mit dringend im Detail befassen.

von (prx) A. K. (prx)


Lesenswert?

mh schrieb:
> Oder vielleicht doch, dank UB?

Da sehe ich  kein UB. Die Rechnung (a - b) ist einwandfrei definiert, 
und eine Konvertierung in einen Typ ohne Vorzeichen ist es auch.

von mh (Gast)


Lesenswert?

(prx) A. K. schrieb:
> mh schrieb:
>> Oder vielleicht doch, dank UB?
>
> Da sehe ich  kein UB. Die Rechnung (a - b) ist einwandfrei definiert,
> und eine Konvertierung in einen Typ ohne Vorzeichen ist es auch.

Nicht die Rechnung, der printf-Aufruf.

von Norbert (Gast)


Lesenswert?

F1() gibt natürlich uint8_t zurück (also Ausgabezeile Zwei)
Pardon an alle. Off-by-One Error meinerseits. Sorry

Beitrag #6893419 wurde vom Autor gelöscht.
von mh (Gast)


Lesenswert?

(prx) A. K. schrieb im Beitrag #6893419:
> mh schrieb:
>> Und der Wert wird dann nach int promoted für den Aufruf von printf,
>> welcher mit einem falschen Conversion Specifier stattfindet...
>
> Ist ein falscher Conversion Specifier für int<=>unsigned UB?

Wenn ich den Standard richtig verstehe (aus ISO/IEC 9899:201x, N1570):
1
7.21.6.1 The fprintf function
2
[...]
3
8) o,u,x,X The unsigned int argument is converted [...]
4
9) If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
5
[...]

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Und der Wert wird dann nach int promoted für den Aufruf von printf,
> welcher mit einem falschen Conversion Specifier stattfindet...

%d wäre hier in allen Fällen korrekt, weil das Argument an printf immer 
entweder schon vom Typ int ist, oder in diesen promoted wird. Bei %d 
kommt ja auch in allen Fällen das erwartete Ergebnis raus.

von EAF (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Damit muss ich mit dringend im Detail befassen.
Sehe ich auch so!

Deine Fantasie spielt dir öfter mal Streiche.
Ist aber keine Überraschung.
Geht wohl jedem mal so.
Dein Problem ist nur, dass du das dann VOR jeder Überprüfung, laut und 
den Wissenden spielend, rausposaunst.

Tipp:
Einbildung und Realität sind kaum zu unterscheiden, wenn man nur lange 
genug schon in den "falschen" Bahnen denkt.

von (prx) A. K. (prx)


Lesenswert?

mh schrieb:
> Wenn ich den Standard richtig verstehe

Hast ja recht, habs auch gerade gesehen.

Ist allerdings reichlich unwahrscheinlich, dass bei gleicher Grösse was 
anbrennt, bloss weil die Vorzeichenangabe falsch ist.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> mh schrieb:
>> Und der Wert wird dann nach int promoted für den Aufruf von printf,
>> welcher mit einem falschen Conversion Specifier stattfindet...
>
> %d wäre hier in allen Fällen korrekt, weil das Argument an printf immer
> entweder schon vom Typ int ist, oder in diesen promoted wird. Bei %d
> kommt ja auch in allen Fällen das erwartete Ergebnis raus.

Bis C23 gibts noch ne andere Möglichkeit, wenn Compiler/CPU nicht 
angegeben sind ... Integral Promotion promoted nicht immer zu int, in 
speziellen Fällen auch unsigned int ;-)

von Stefan F. (Gast)


Lesenswert?

Quellcode zum Nachvollziehen:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
int main()
5
{  
6
  puts("8 Bit");
7
  uint8_t c=1;
8
  uint8_t d=0xFF;
9
  printf("%X + %X = %X (%d bytes)\n",c,d,c+d,sizeof(c+d));
10
  printf("%X - %X = %X (%d bytes)\n\n",c,d,c+d,sizeof(c-d));
11
  
12
  puts("16 Bit");
13
  uint16_t e=1;
14
  uint16_t f=0xFFFF;
15
  printf("%X + %X = %X (%d bytes)\n",e,f,e+f,sizeof(e+f));
16
  printf("%X - %X = %X (%d bytes)\n\n",e,f,e+f,sizeof(e-f));
17
  
18
  puts("32 Bit");
19
  uint32_t g=1;
20
  uint32_t h=0xFFFFFFFF;
21
  printf("%lX + %lX = %lX (%d bytes)\n",g,h,g+h,sizeof(g+h));
22
  printf("%lX - %lX = %lX (%d bytes)\n\n",g,h,g+h,sizeof(g-h));
23
  
24
  puts("64 Bit");
25
  uint64_t i=1;
26
  uint64_t j=0xFFFFFFFFFFFFFFFF;
27
  printf("%llX + %llX = %llX (%d bytes)\n",i,j,i+j,sizeof(i+j));
28
  printf("%llX - %llX = %llX (%d bytes)\n",i,j,i+j,sizeof(i-j));
29
}

Ausgabe auf PC:
1
8 Bit
2
1 + FF = 100 (4 bytes)
3
1 - FF = 100 (4 bytes)
4
5
16 Bit
6
1 + FFFF = 10000 (4 bytes)
7
1 - FFFF = 10000 (4 bytes)
8
9
32 Bit
10
1 + FFFFFFFF = 0 (4 bytes)
11
1 - FFFFFFFF = 0 (4 bytes)
12
13
64 Bit
14
1 + FFFFFFFFFFFFFFFF = 0 (8 bytes)
15
1 - FFFFFFFFFFFFFFFF = 0 (8 bytes)

Ausgabe auf AVR:
1
8 Bit
2
1 + FF = 100 (2 bytes)
3
1 - FF = 100 (2 bytes)
4
5
16 Bit
6
1 + FFFF = 0 (2 bytes)
7
1 - FFFF = 0 (2 bytes)
8
9
32 Bit
10
1 + FFFFFFFF = 0 (4 bytes)
11
1 - FFFFFFFF = 0 (4 bytes)
12
13
64 Bit

64 Bit kann der printf() auf AVR offenbar nicht.

Was man hier dennoch schön sieht ist, dass die Größe des Ergebnisses 
offenbar nicht immer mit den Operanden identisch ist. Es scheint 
folgende Regel zu gelten:

Das Ergebnis hat immer die Größe der größten Operanden, aber mindestens 
die Größe des "Standard" Integer. Der ist bei meinem PC 32 Bit groß und 
bei AVR 16 Bit.

Ist das so korrekt?

von Stefan F. (Gast)


Lesenswert?

EAF schrieb:
> Dein Problem ist nur, dass du das dann VOR jeder Überprüfung, laut und
> den Wissenden spielend, rausposaunst.

Ja, du hast Recht. Ich schäme mich dafür.

von mh (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ist das so korrekt?

Schalt erstmal alle passenden Warnungen deines Compilers an und kümmer 
dich um die Warnungen, die es wahrscheinlich gibt.

von (prx) A. K. (prx)


Lesenswert?

Stefan ⛄ F. schrieb:
> 64 Bit kann der printf() auf AVR offenbar nicht.

Welcher? https://gcc.gnu.org/wiki/avr-gcc#ABI

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

mh schrieb:
> Integral Promotion promoted nicht immer zu int, in
> speziellen Fällen auch unsigned int

Ist das nicht technisch identisch?

Stefan ⛄ F. schrieb:
8 Bit
1 + FF = 100 (4 bytes)
1 - FF = 100 (4 bytes)

Unabhängig ob die 4 Bytes nun int oder uint sind, erwarte ich 0x100 oder 
0b100000000. Oder ist das etwa wieder falsch?

von Stefan F. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> 64 Bit kann der printf() auf AVR offenbar nicht.

(prx) A. K. schrieb:
> Welcher?

Version 5.4.0

von Yalu X. (yalu) (Moderator)


Lesenswert?

Alle die es noch nicht getan haben (und davon scheint es hier einige zu
geben), sollten sich

  ISO/IEC 9899:2018, Abschnitt 6.3.1 (Arithmetic Operands).

(oder wahlweise auch den Draft N2176) zu Gemüte führen. Da sind die
Konvertierungen, um die es hier geht, vollständig spezifiziert. Der
Abschnitt ist mit zweieinhalb Seiten deutlich kürzer als dieser Thread
(und der wir immer länger und länger).

Wem das danach immer noch zu kompliziert ist, sollte von C auf Haskell
umsteigen. Da gibt es keine impliziten Konvertierungen. Bei Addition,
Subtraktion und den meisten anderen arithmetischen Operationen sind die
beiden Operanden und das Ergebnis grundsätzlich vom selben Typ. Wer sich
dieser Regel widersetzt, wird vom Compiler in seine Schranken verwiesen.

: Bearbeitet durch Moderator
von Stefan F. (Gast)


Lesenswert?

Yalu X. schrieb:
> ISO/IEC 9899:2018, Abschnitt 6.3.1 (Arithmetic Operands).

Leider ist das Dokument der Öffentlichkeit (also mir) nicht zugänglich.

Der Draft ist dort: 
https://teaching.csse.uwa.edu.au/units/CITS2002/resources/n2176.pdf

von mh (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> mh schrieb:
>> Integral Promotion promoted nicht immer zu int, in
>> speziellen Fällen auch unsigned int
>
> Ist das nicht technisch identisch?
Nein ist es nicht.

> Stefan ⛄ F. schrieb:
> 8 Bit
> 1 + FF = 100 (4 bytes)
> 1 - FF = 100 (4 bytes)
>
> Unabhängig ob die 4 Bytes nun int oder uint sind, erwarte ich 0x100 oder
> 0b100000000. Oder ist das etwa wieder falsch?
Ich habe keine Ahnung was du hier fragst.

Yalu X. schrieb:
> Wem das danach immer noch zu kompliziert ist, sollte von C auf Haskell
> umsteigen.

Glaubst du, jemand der Integer Promotion in C nicht versteht, kommt mit 
Haskell klar (also dem Rest der Sprache/Denkweise)?

von (prx) A. K. (prx)


Lesenswert?

Stefan ⛄ F. schrieb:
> Stefan ⛄ F. schrieb:
>> 64 Bit kann der printf() auf AVR offenbar nicht.
>
> (prx) A. K. schrieb:
>> Welcher?
>
> Version 5.4.0

Ich meine nicht die Version. GCC kann 64-Bit Integer seit Ewigkeiten, 
aber vielleicht nicht jeder andere Compiler für AVR.

In der Doku der avrlibc steht bei printf allerdings kein "%ll..." drin 
und ein kurzer Blick in dessen Source legt nahe, dass dort der Hase im 
Pfeffer liegt.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Stefan ⛄ F. schrieb:
> Yalu X. schrieb:
>> ISO/IEC 9899:2018, Abschnitt 6.3.1 (Arithmetic Operands).
>
> Leider ist das Dokument der Öffentlichkeit (also mir) nicht zugänglich.

Doch, aber du musst halt 198 CHF dafür berappen :)

> Der Draft ist dort:
> https://teaching.csse.uwa.edu.au/units/CITS2002/resources/n2176.pdf

Super, dann hast du ja alle Informationen, die du brauchst, um
zukünftigen Überraschungen vorzubeugen :)

von Stefan F. (Gast)


Lesenswert?

(prx) A. K. schrieb:
> In der Doku der avrlibc steht bei printf allerdings kein "%ll..." drin
> und ein kurzer Blick in dessen Source legt nahe, dass dort der Hase im
> Pfeffer liegt.

Ich verwende die avr libc 1.2.0

von Stefan F. (Gast)


Lesenswert?

Yalu X. schrieb:
> Super, dann hast du ja alle Informationen, die du brauchst, um
> zukünftigen Überraschungen vorzubeugen

Ja, ich lese gerade. Habe ich mir direkt in meine Büchersammlung 
kopiert.

von (prx) A. K. (prx)


Lesenswert?

Stefan ⛄ F. schrieb:
> Leider ist das Dokument der Öffentlichkeit (also mir) nicht zugänglich.

Drafts zu beispielsweise C99 oder C11 sind problemlos verfügbar und 
unterscheiden sich vom Standard nicht signifikant.

von Rolf M. (rmagnus)


Lesenswert?

Stefan ⛄ F. schrieb:
> Es scheint
> folgende Regel zu gelten:
>
> Das Ergebnis hat immer die Größe der größten Operanden, aber mindestens
> die Größe des "Standard" Integer. Der ist bei meinem PC 32 Bit groß und
> bei AVR 16 Bit.
>
> Ist das so korrekt?

Im Prinzip ja. Wobei es da dann noch ein paar Sonderfälle gibt.

(prx) A. K. schrieb:
> Stefan ⛄ F. schrieb:
>> 64 Bit kann der printf() auf AVR offenbar nicht.
>
> Welcher? https://gcc.gnu.org/wiki/avr-gcc#ABI

Was hat das ABI mit der Implementation von printf() zu tun?

von Stefan F. (Gast)


Lesenswert?

Rolf M. schrieb:
> Was hat das ABI mit der Implementation von printf() zu tun?

Habe ich auch nicht verstanden. Offenbar ist es eher eine Frage der 
Implementierung von printf() also der library.

Der entscheidende Satz in der Spezifikation ist wohl

"If an int can represent all values of the original type (as restricted 
by the width, for a bit-field), the value is converted to an int; 
otherwise, it is converted to an unsigned int."

Rolf M. schrieb:
> Wobei es da dann noch ein paar Sonderfälle gibt.

Kannst du mir Beispiele nennen?

Ich frage nach, weil diese Spezifikation nicht gerade leichter Lesestoff 
ist. Ich habe meinen Kopf leider mit anderen Programmiersprachen voll - 
wegen der Arbeit. C ist bei mir nur Hobby.

(prx) A. K. schrieb:
> Drafts zu beispielsweise C99 oder C11 sind problemlos verfügbar und
> unterscheiden sich vom Standard nicht signifikant.

Ich habe ja jetzt mit Yalus Hilfe den Draft von C17 (Dokument N2176) 
gefunden.

von Yalu X. (yalu) (Moderator)


Lesenswert?

mh schrieb:
> Yalu X. schrieb:
>> Wem das danach immer noch zu kompliziert ist, sollte von C auf Haskell
>> umsteigen.
>
> Glaubst du, jemand der Integer Promotion in C nicht versteht, kommt mit
> Haskell klar (also dem Rest der Sprache/Denkweise)?

Zumindest sind dort alle Regeln sehr einfach gehalten (was sich in der
im Vergleich zu C deutlich kürzeren Sprachspezifikation ausdrückt).
Außerdem gibt es kaum Überraschungen zur Laufzeit, denn bevor es dazu
kommt, erhält man i.Allg. eine Fehlermeldung des Compilers, die recht
ausführlich erklärt, warum dieser den ihm vorgesetzten Quellcode nicht
umzusetzen kann.

Aber natürlich ist es auch in Haskell schwer, Programme zu schreiben.
Programmieren (egal in welcher Sprache) fällt niemanden in den Schoß,
man muss es erst mit viel Schweiß lernen :)

von Stefan F. (Gast)


Lesenswert?

mh schrieb:
> Ich habe keine Ahnung was du hier fragst.

Was ich meine ist, dass das Ergebnis als int und uint im 
Speicher/Register identisch ist.
1
#include <stdio.h>
2
#include <stdint.h>
3
4
int main()
5
{  
6
  uint8_t a=0;
7
  uint8_t b=1;
8
  
9
  uint16_t d=a-b;
10
  int16_t e=a-b;
11
  
12
  printf("d = %d = %X (%d bytes)\n",d,d,sizeof(d));
13
  printf("e = %d = %X (%d bytes)\n",e,e,sizeof(e));
14
}

Ausgabe auf PC:
1
d = 65535 = FFFF (2 bytes)
2
e = -1 = FFFFFFFF (2 bytes)

Dezimal dargestellt erscheint der unsigned Integer falsch, denn er kann 
keine negative Zahl darstellen.

Nur der signed Integer stellt das Ergebnis richtig dar. Hier gibt mir 
printf() allerdings zu viele "F" aus.

Im Speicher sind es in beiden Fällen die gleichen 2 Bytes, nämlich FFFF.

von Rolf M. (rmagnus)


Lesenswert?

Hier ist das auch nochmal alles zusammengefasst:
https://en.cppreference.com/w/c/language/conversion

Stefan ⛄ F. schrieb:
> Dezimal dargestellt erscheint der unsigned Integer falsch, denn er kann
> keine negative Zahl darstellen.

Mathematisch gesehen ja, aber C definiert durchaus, welcher Wert da 
herauskommt.

> Nur der signed Integer stellt das Ergebnis richtig dar. Hier gibt mir
> printf() allerdings zu viele "F" aus.

Weil hier eine sign extension durchgeführt wird.
Ein uint16_t mit dem Wert 65535 behält bei der Konvertierung in einen 
32-Bit-Typ diesen Wert bei. Ein int16_t mit dem Wert -1 tut das auch. 
Allerdings ist die interne Bitrepräsentation dieser beiden Werte zwar 
vorher gleich, aber nachher unterschiedlich. Im einen Fall müssen die 
zusätzlichen Bits mit 0 aufgefüllt werden, im anderen mit 1.

von PittyJ (Gast)


Lesenswert?

Hannes J. schrieb:
> Einfach nicht voneinander abziehen oder erst nach dem Vergleich
> abziehen:
> if(a >= b) {
>     // Differenz garantiert >= 0
> } else {
>     // Differenz negativ
> }

War auch mein erster Gedanke. Die einfachste Lösung.
Scheint wohl hier nur keinen zu interessieren.

von mh (Gast)


Lesenswert?

PittyJ schrieb:
> Hannes J. schrieb:
>> Einfach nicht voneinander abziehen oder erst nach dem Vergleich
>> abziehen:
>> if(a >= b) {
>>     // Differenz garantiert >= 0
>> } else {
>>     // Differenz negativ
>> }
>
> War auch mein erster Gedanke. Die einfachste Lösung.
> Scheint wohl hier nur keinen zu interessieren.

Nach dem Motto: "Probleme nicht Lösen, sondern einen Workaround 
benutzen".

von EAF (Gast)


Lesenswert?

PittyJ schrieb:
> Scheint wohl hier nur keinen zu interessieren.

Quatsch!
Wurde doch sofort akzeptiert.


Siehe:
Thorsten S. schrieb:
> Hannes J. schrieb:
>> Einfach nicht voneinander abziehen oder erst nach dem Vergleich
>> abziehen:
>
> Genial, manchmal hat man einen Knoten im Hirn...


Das ganze andere Getöse ist nur ein Nebenkriegsschauplatz.

von Rolf M. (rmagnus)


Lesenswert?

PittyJ schrieb:
> War auch mein erster Gedanke. Die einfachste Lösung.
> Scheint wohl hier nur keinen zu interessieren.

Nunja, die daraus entstandene Diskussion scheint ja für so manchen hier 
bitter nötig gewesen zu sein.

von Stefan F. (Gast)


Lesenswert?

Rolf M. schrieb:
> Nunja, die daraus entstandene Diskussion scheint ja für so manchen hier
> bitter nötig gewesen zu sein.

Ja genau. Ich finde gut, dass das hier besprochen wurde. Zumindest für 
mich war es lehrreich.

von EAF (Gast)


Lesenswert?

Ja, ist schon ok, so eine Themaerweiterung ....

von Oliver S. (oliverso)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ja genau. Ich finde gut, dass das hier besprochen wurde. Zumindest für
> mich war es lehrreich.

Der letzte Durchgang durchs Thema ist noch keine 3 Wochen alt

Beitrag "C arithmetic promotion Fallstricke"

Oliver

von HildeK (Gast)


Lesenswert?

Rolf M. schrieb:
> Nunja, die daraus entstandene Diskussion scheint ja für so manchen hier
> bitter nötig gewesen zu sein.

Für mich auch 😀.
Diese Feinheiten sind einem Gelegenheitsprogrammierer meist nicht 
bewusst und die werde ich mir auch nicht vollständig merken können.
Behält man die grundsätzliche Problemstellung wenigstens im Hinterkopf, 
kommt man auch auf die Lösung von Hannes J.

Hannes J. schrieb:
> Einfach nicht voneinander abziehen ...

oder auf eine andere Formulierung des Vergleichs. Oder sei es auch nur 
im Fall einer Fehlfunktion einer der ersten Gedanken, an solchen Stellen 
nochmals nachzudenken.

von W.S. (Gast)


Lesenswert?

mh schrieb:
>> Die einfachste Lösung.
>> Scheint wohl hier nur keinen zu interessieren.
>
> Nach dem Motto: "Probleme nicht Lösen, sondern einen Workaround
> benutzen".

Genau DAS ist C typisch.

Aber man kann sich dran gewöhnen - wie hier zu sehen ist.

W.S.

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.