Forum: Mikrocontroller und Digitale Elektronik Warum ist mein int8_t 4 Bytes groß? 0xFF != 0xFFFFFFFF


von Matthias M. (mettwurstbrot)


Lesenswert?

Hallo,
ich bin gerade etwas verwirrt.

Im EEPROM speichere ich an Adresse 0x08 einen Offsetwert. Dieser kann 
von -128 bis 127 gehen. Daher interpretiere ich das gelesene Byte als 
int8_t. Dessen Wert sollte laut 
https://de.wikibooks.org/wiki/C-Programmierung_mit_AVR-GCC/_Datentypen 
passen.

Die beiden EEPROM-Header sind inkludiert, weil ich der Arduino-Umsetzung 
zunächst nicht getraut habe. Sie liefern aber beide das gleiche 
Ergebnis.

Nun passiert aber folgendes:
1
#include "EEPROM.h"
2
#include "eeprom.h"
3
4
void setup() {
5
  Serial.begin(9600);
6
7
  int8_t offset = EEPROM.read(0x08);
8
  Serial.print("Size of offset: ");
9
  Serial.println(sizeof(offset));
10
11
  Serial.println(EEPROM.read(0x08), HEX);
12
  Serial.println(EEPROM.read(0x08), DEC);
13
  Serial.println(eeprom_read_byte((uint8_t*)0x08), HEX);
14
  Serial.println(offset, HEX);
15
  Serial.println(offset, DEC);
16
17
  if (offset == 0xFF) {
18
    Serial.println("Offset ist 0xFF und wird auf 0 gesetzt!");
19
    offset = 0;
20
  }
21
  else {
22
    Serial.print("Offset ist nicht 0xFF sondern ");
23
    Serial.println(offset);
24
  }
25
}
26
27
void loop() {
28
29
}

liefert die Ausgabe:
Size of offset: 1
FF
255
FF
FFFFFFFF
-1
Offset ist nicht 0xFF sondern -1

Wenn ich auf uint8_t umstelle, passt es grundsätzlich:
1
#include "EEPROM.h"
2
#include "eeprom.h"
3
4
void setup() {
5
  Serial.begin(9600);
6
7
  uint8_t offset = EEPROM.read(0x08);  // <-- HIER geändert
8
  Serial.print("Size of offset: ");
9
  Serial.println(sizeof(offset));
10
11
  Serial.println(EEPROM.read(0x08), HEX);
12
  Serial.println(EEPROM.read(0x08), DEC);
13
  Serial.println(eeprom_read_byte((uint8_t*)0x08), HEX);
14
  Serial.println(offset, HEX);
15
  Serial.println(offset, DEC);
16
17
  if (offset == 0xFF) {
18
    Serial.println("Offset ist 0xFF und wird auf 0 gesetzt!");
19
    offset = 0;
20
  }
21
  else {
22
    Serial.print("Offset ist nicht 0xFF sondern ");
23
    Serial.println(offset);
24
  }
25
}
26
27
void loop() {
28
29
}

liefert die Ausgabe:
Size of offset: 1
FF
255
FF
FF
255
Offset ist 0xFF und wird auf 0x00 gesetzt!

Natürlich kann ich mir den Nullpunkt selber auf 127 legen und komme dann 
auf das richtige Ergebnis. Aber ich verstehe nicht, warum der gcc dieses 
Ergebnis liefert.

Könnt ihr mir da bitte helfen, oder zumindest sagen, wie ich auf die 
richtige Spur komme?

Danke, Matthias

von Einer K. (Gast)


Lesenswert?

Es ist nicht der gcc, welcher dir hier einen Streich spielt, sondern 
Serial.print(ln)

von A.B. (Gast)


Lesenswert?

Ich verstehe deine Frage bzw. das Problem nicht ganz.
Stört dich die Ausgabe
1
FFFFFFFF
oder das Ergebnis des Vergleiches?

von Einer K. (Gast)


Lesenswert?

Korrektur:
Ich habe das mal getestet....
Serial.print() liefert die erwarteten Ergebnisse!
1
int8_t test1 = -127;
2
int8_t test2 = -1;
3
4
void setup()
5
{
6
  Serial.begin(9600);
7
  Serial.println(sizeof(test1));
8
  Serial.println(test1);
9
  Serial.println(test2);
10
}
11
12
void loop(){}

Result:
1
1
2
-127
3
-1

von Student (Gast)


Lesenswert?

Es ist nicht nur das Serial, denn sonst wäre die if ja getriggert 
worden.

Ich tippe mal darauf:
1
int8_t offset = 0xFF;
2
uint8_t byte = 0xFF;
3
4
if (offset == byte)

Sprich: Der GCC sieht die 0xFF als Unsigned an (Das macht ja bei 
Hexadezimal durchaus Sinn: Addressen und Co), und da eben zwar die Bits 
übereinstimmen, aber die Zahlen dezimal unterschiedlich sind, haut es 
nicht hin.

Oder noch schlimmer: Er macht aus deinem 0xFF zumindest ein 0x00FF und 
shiftet das Vorzeichen, weil ja FF nicht in unsigned passt. Versuche mal 
explizit "-1" anstatt 0xFF zu nehmen.

-1 ist übrigens die 2K-Darstellung von 0xFF

von Thomas K. (muetze1)


Lesenswert?

Die IF Bedingung kann bei int8_t nur fehlschlagen, da ein int8_t niemals 
einen Wert von 255 erreichen kann. Wenn müsstest du auf -1 testen oder 
aber den int8_t auf einen uint8_t casten für den Vergleich auf 0xFF. 
Aber das nur am Rande.

von Metti (Gast)


Lesenswert?

Mich stört nur das Ergebnis des Vergleiches. Ausgegeben wird 
normalerweise nichts.

if (offset == 0xFF)
  offset = 0;

Das soll halt funktionieren :)

von Patrick C. (pcrom)


Lesenswert?

>> if (offset == 0xFF)
>> Das soll halt funktionieren :)

Nee das soll nicht unbedingt funktionieren.

Man kan doch auch nicht Volts mit Temperatur vergleichen oder Alter mit 
Gewicht ?

offset ist int8
0xFF ist uint8

Gut wahre (nicht getested aber ich sehe da keine problemen) :
if (offset == (int8)0xFF) // Weil der Compiler dann beide als int8 sieht
oder
if ((uint8) offset == 0xFF) // Weil der Compiler dann beide als uint8 
sieht

Mehr informationen >> Google auf 'typecasting'

von Kaj (Gast)


Lesenswert?

Einfach mal die Warnungen einschalten...
1
int main(void)
2
{
3
    int8_t s_offset = 0xff;
4
5
    if(s_offset == 0xff) {
6
        printf("What ever...\n");
7
    }
8
    return 0;
9
}
1
Warnung: comparison is always false due to limited range
2
of data type [-Wtype-limits]
3
     if(s_offset == 0xff) {
4
                 ^~

von Einer K. (Gast)


Lesenswert?

Metti schrieb:
> Mich stört nur das Ergebnis des Vergleiches. Ausgegeben wird
> normalerweise nichts.
>
> if (offset == 0xFF)
>   offset = 0;
>
> Das soll halt funktionieren :)

offset ist int8_t
0xFF ist int16_t

Um den Vergleich zu machen muss der Kompiler offset zum int16_t 
konvertieren.
Also muss der Vergleich (offset == 0xFF) IMMER falsch ergeben. Egal 
welchen Wert offset annimmt.

Und damit ist der Vergleich, wie  Thomas K. schon sagte völlig unsinnig.

von Matthias M. (mettwurstbrot)


Lesenswert?

OK, danke für die Hinweise.
1
  if (offset == -1) {
2
    Serial.println("Offset ist 0xFF und wird auf 0 gesetzt!");
3
    offset = 0;
4
  }
5
  else {
6
    Serial.print("Offset ist nicht 0xFF sondern ");
7
    Serial.println(offset);
8
  }
9
}

funktioniert mit int8_t wie gewünscht.
Sowohl bei leerem EEPROM (dann wird auf 0 gesetzt) als auch bei realem 
Wert:

Size of offset: 1
DD
221
DD
FFFFFFDD
-35
Offset ist nicht 0xFF sondern -35


So soll es sein. Ich habe nicht gewusst, dass die dezimale 
Representation so stark "bindet". Ich habe immer gedacht, dass die 
hexadezimale Schreibweise eindeutiger ist und ob jetzt -35 oder 221 
draus gemacht wird, ist beim Vergleich egal. Beides ist 0xDD.

Danke & VG
Metti

: Bearbeitet durch User
von Tim (Gast)


Lesenswert?

Du hast noch immer nichts verstanden.

Dezimal und hexadezimal sind gleichwertig. Da "bindet" gar nichts.

FF ist 255. Immer.

Nur kann ein int8_t eben keine 255 aufnehmen. Eine -1 geht aber 
natürlich.

Wie du auf die Idee kommst, -1 und 255 seien dasselbe, ist die viel 
interessantere Frage.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Tim schrieb:
> Wie du auf die Idee kommst, -1 und 255 seien dasselbe, ist die viel
> interessantere Frage.

 Weil sowohl int8_t = -1 und uint8_t = 255 mit 0xFF geschrieben
 werden. Dass ein int8_t niemals grösser als 127 sein kann, hat
 er wahrscheinlich übersehen, ist ja auch nicht sooo schlimm.

: Bearbeitet durch User
von Marc (Gast)


Lesenswert?

Bei int8_t bla = 0xFF ist FF aber nicht das Bitmuster des int8_t (und da 
gehst du übrigens schonmal von Zweierkomplement aus, was praktisch der 
Fall ist, aber keineswegs garantiert), sondern erstmal nur ein unsigned 
int mit Wert dezimal 255.

Der wird dann implizit gecastet nach int8_t, was wiederum meistens -1 
ergibt, aber das ist noch viel weniger garantiert. Signed overflow ist 
undefined, und in letzter Zeit haben ein paar Leute auch an diversen 
Stellen gemerkt, daß ihre Compiler da biestiger wurden als sie es 
historisch waren. Mit Bitmusterdarstellung hat das nichts zu tun, auch 
wenn es vom Ergebnis her so aussieht (und natürlich auch kein blanker 
Zufall ist).

Mir ist schon klar, daß diese beiden falschen Vorstellungen verbreitet 
sind. Sie sind aber nach wie vor falsch. Und wenn man verstehen will, 
was passiert und nicht immer nur im Dunkeln tappen, muß man diese 
Mechanismen verstehen.

Und deswegen ist dein flapsiges "-1 schreibt man als 0xFF" gefährlich. 
Das kann man so sagen, wenn alle Beteiligten wissen, wovon sie sprechen. 
95% der (auch langjährigen) C-Programmierer sind aber leider nicht auf 
dem Niveau.

von Matthias M. (mettwurstbrot)


Lesenswert?

Tim schrieb:
> Du hast noch immer nichts verstanden.
>
> Dezimal und hexadezimal sind gleichwertig. Da "bindet" gar nichts.
>
> FF ist 255. Immer.
>
> Nur kann ein int8_t eben keine 255 aufnehmen. Eine -1 geht aber
> natürlich.
>
> Wie du auf die Idee kommst, -1 und 255 seien dasselbe, ist die viel
> interessantere Frage.

Dass -1 und 255 nicht identisch sind, ist mir klar, sonst hätte ich 
keine Geldsorgen mehr.

Ich will ja auch gar nicht, dass int8_t den Dezimalwert 255 annimmt. Ich 
möchte ihn mit dem Inhalt einer 1 Byte großen Speicherzelle beschreiben, 
dessen Wert 0xFF ist. Und ich habe nunmal angenommen, dass in jeden 8 
Bit breiten Datentypen der Wert 0xFF reinpasst und sich dann auch 
verarbeiten lässt. Meine Art der Verarbeitung war offenbar falsch. 2er 
Komplement ist mir schon bekannt... Aber ich dachte eben, dass dies 
lediglich bei der dezimalen Representation zum Tragen kommt und nicht, 
wenn ich mit den echten Bits in Form von 0x arbeite. War offenbar 
falsch, 0b, 0, Dec und Hex sind gleichwertige Schreibweisen habe ich nun 
gelernt.

Danke.

von Matthias M. (mettwurstbrot)


Lesenswert?

Marcs Antwort kam, als ich den Hund gefüttert hab :-D

Danke nochmal für die Klarstellung.

Ich lese daraus, dass ich explizit sagen soll, wohin ich casten möchte, 
damit nichts schief geht.
Probiere ich nachher nochmal aus.

von Rolf M. (rmagnus)


Lesenswert?

Marc schrieb:
> Der wird dann implizit gecastet nach int8_t,

Wird er eigentlich nicht (mal abgesehen davon, dass es keine "impliziten 
Casts" gibt). Auch einen Überlauf gibt es hier nicht.
Stattdessen passiert hier die "Integer promotion". Bei dem Vergleich 
werden, wie an sehr vielen Stellen, alle Integer, die kleiner als int 
sind, erstmal auf int erweitert, also auf dem AVR auf 16 Bit. Und dort 
sind eben auch im Zweierkomplement die Bitpatterns für -1 und für 255 
nicht gleich.

Matthias M. schrieb:
> Ich lese daraus, dass ich explizit sagen soll, wohin ich casten möchte,
> damit nichts schief geht.

Besser wäre es, bei Rechenoperationen und Vergleichen vorzeichenlose und 
vorzeichenbehaftete Typen möglichst nicht zu mischen.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Matthias M. schrieb:
> Komplement ist mir schon bekannt... Aber ich dachte eben, dass dies
> lediglich bei der dezimalen Representation zum Tragen kommt und nicht,
> wenn ich mit den echten Bits in Form von 0x arbeite. War offenbar
> falsch, 0b, 0, Dec und Hex sind gleichwertige Schreibweisen habe ich nun
> gelernt.

 Lass dich nicht entmutigen, du liegst gar nicht so falsch.
1
uint8_t ub = 0;
2
3
 ub = -15;
4
 ub = 241;

 Wird klaglos übersetzt, mit 0xF1 als Wert.

1
uint8_t ub;
2
int16_t si;
3
4
 si = 0;
5
 ub = -15;
6
 si += ub;
7
8
 si = 0;
9
 ub = 241;
10
 si += ub;

 Und dann siehst du, dass sogar GCC deiner Meinung ist ;)

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Wenn du eine Speicherzelle mit dem Wert 0xFF in eine Variable vom Typ 
int8_t einliest, dann erhälst du den Wert -1.

Wenn du eine Speicherzelle mit dem Wert 0xFF in eine Variable vom Typ 
*u*int8_t einliest, dann erhälst du den Wert 255.

Dementsprechend musst du mit -1 bzw. 255 vergleichen.

Deine Schreibweise von 0xFF im Quelltext ist lediglich eine alternative 
Syntax. 0xFF entspricht 255, das wird Dir jeder Mathelehrer bestätitgen. 
Der Compiler würde niemals deine 0xFF im Quelltext nach -1 übersetzen, 
denn der Compiler hält sich soweit möglich an die Regeln der Mathematik. 
Oder genau gesagt, an die C Spezifikation.

C wurde designed, um Plattformübergreifend zu programmieren. Also wurde 
ganz bewusst vermieden, -1 als 255 zu behandeln. Denn das würde nur auf 
bestimmte CPU's zutreffen. Mittlerweilen sind das zwar IMHO alle, das 
das war nicht immer so und könnte sich im Zukunft auch wieder ändern.

von M. K. (sylaina)


Lesenswert?

Stefan U. schrieb:
> C wurde designed, um Plattformübergreifend zu programmieren. Also wurde
> ganz bewusst vermieden, -1 als 255 zu behandeln. Denn das würde nur auf
> bestimmte CPU's zutreffen. Mittlerweilen sind das zwar IMHO alle, das
> das war nicht immer so und könnte sich im Zukunft auch wieder ändern.

Hm, wann war denn das? Also dass z.B. bei einem int8 eine 255 nicht als 
-1 behandelt wurde? Das ergibt sich doch automatisch aus dem Binärcode 
von 255 dass das bei einem int8 der -1 entspricht.

von chris_ (Gast)


Lesenswert?

Hat jemand vielleicht einen Link auf die Spezifikation der 
Cast-Behandlung?

Wo ist die Spezifikation für den Wert in k:
1
int8_t k=0xFF;

von M. K. (sylaina)


Lesenswert?

chris_ schrieb:
> Wo ist die Spezifikation für den Wert in k:

Du meinst warum der 0xff ist zu beginn? Na, das steht im Datenblatt des 
Atmegas: Wird das eeprom gelöscht steht an jeder Speicherstelle ein 
0xff. Das hat mit der internen (invertierenden) Logik der 
Speicherbausteine zu tun ;)

von Dirk B. (dirkb2)


Lesenswert?

M. K. schrieb:
> Hm, wann war denn das? Also dass z.B. bei einem int8 eine 255 nicht als
> -1 behandelt wurde? Das ergibt sich doch automatisch aus dem Binärcode
> von 255 dass das bei einem int8 der -1 entspricht.

Nein.
Das ist bei der Zweier-Komplement-Darstellung so.
Die muss aber nicht verwendet werden. Solche Systeme gab es mal.

Wenn das Programm aber nur auf einem ATMega mit Zweier-Komplement läuft, 
braucht man darauf aber keine Rücksicht nehmen.

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Dirk B. schrieb:
> Solche Systeme gab es mal.

Keine Frage, die gabs und gibts sicher immer noch.

Dirk B. schrieb:
> Wenn das Programm aber nur auf einem ATMega mit Zweier-Komplement läuft

Ja, das war im Prinzip meine Frage: Wann war das auf dem Atmega denn mal 
anders? Muss lange her sein.

von chris_ (Gast)


Lesenswert?

>Hat jemand vielleicht einen Link auf die Spezifikation der
>Cast-Behandlung?
>>Du meinst warum der 0xff ist zu beginn? Na, das steht im Datenblatt des
>>Atmegas:

Nein, mich interessiert die prozessorunabhängige Spezifikation von "C".

von M. K. (sylaina)


Lesenswert?

chris_ schrieb:
> Nein, mich interessiert die prozessorunabhängige Spezifikation von "C".

Das ist nicht spezifiziert. Nach C wird mit der Deklaration
1
 uint8_t myUInt8;

myUInt8 ein Speicherbereich vom RAM zugewiesen. In myUInt8 steht dann 
das drin, was an dem Speicherbereich steht. Das kann 0 sein, das kann 23 
sein, im Fall des EEPROMs im Atmega steht da 0xFF drin, also -1 (wegen 
der schon genannten Zweierkomplementdarstellung im Atmega).

Erst mit
1
 myUInt8 = 0xff;

wird myUInt8 definiert auf 0xff gesetzt. Man kann natürlich Deklaration 
und Definition in eine Zeile packen in C damit das dann so aussieht:
1
uint8_t myUInt8 = 0xff;

und spart somit eine Zeile.

von Stefan F. (Gast)


Lesenswert?

>> Das ist bei der Zweier-Komplement-Darstellung so.
> Wann war das auf dem Atmega denn mal anders?

Noch nie.
Aber du verdrehst meine Aussage.

Ich habe gesagt, dass C plattformübergreifend designed wurde. Also eben 
nicht nur für AVR. Das trifft sogar ganz besonders auf den avr-gcc zu, 
der vom gcc abgeleitet wurde, der wiederum für zahlreiche Architekturen 
verwendet wird und auch dafür gedacht war.

von Mikro 7. (mikro77)


Lesenswert?

1
#include <stdint.h>
2
int8_t i = 0xff ; 
3
uint8_t j = -1 ;

Es ist meist eine gute Idee den gcc mit -Wconversion zu nutzen.
1
> gcc -Wconversion -c test1.c 
2
test1.c:2:1: warning: conversion to ‘int8_t’ alters ‘int’ constant value [-Wconversion]
3
test1.c:3:1: warning: negative integer implicitly converted to unsigned type [-Wsign-conversion]

Wer meinte hier, dass die Darstellung der Literale (ob Dezimal oder Hex) 
keine Rolle spielt? Manchmal schon... (amd64)
1
#include <stdint.h>
2
int i = 0xFFFFFFFF ;
3
int j = 4294967295 ;

Hex ist hier unsigned. Dezimal dagegen long int.
1
> gcc -Wconversion -c test2.c 
2
test2.c:2:1: warning: conversion of unsigned constant value to negative integer [-Wsign-conversion]
3
test2.c:3:1: warning: conversion to ‘int’ alters ‘long int’ constant value [-Wconversion]

Ich gerade eine Seite gesucht, die übersichtlich die Typen, die 
Fixed-width Types, Literale, Conversion und Promotion beschreibt, aber 
leider nix passendes gefunden. Vielleicht kennt ihr ja eine gute 
Übersicht (und ich meine nicht ANSI X3.159-1989 oder ISO/IEC 9899 ;-)

von M. K. (sylaina)


Lesenswert?

Stefan U. schrieb:
> Aber du verdrehst meine Aussage.

Hab ich das gemacht? Das wollte ich natürlich nicht, entschuldige.

Stefan U. schrieb:
> Ich habe gesagt, dass C plattformübergreifend designed wurde. Also eben
> nicht nur für AVR.

Dagegen hab ich ja auch nix gesagt. Hier gehts aber nunmal in Bezug auf 
AVRs also sollte man auch dabei bleiben finde ich. Man kann auch groß 
ausholen aber das würde sicher den Rahmen sprengen, den uns dieses Forum 
bieten will.

von Rolf M. (rmagnus)


Lesenswert?

M. K. schrieb:
> Hm, wann war denn das? Also dass z.B. bei einem int8 eine 255 nicht als
> -1 behandelt wurde? Das ergibt sich doch automatisch aus dem Binärcode
> von 255 dass das bei einem int8 der -1 entspricht.

Nur wenn man das Zweierkomplement benutzt. C läßt aber auch das 
Einerkomplement zu. Da entspricht die -1 der 254, oder 
sign/magnitude-Darstellung. Da würde die -1 der 129 entsprechen.

chris_ schrieb:
> Hat jemand vielleicht einen Link auf die Spezifikation der
> Cast-Behandlung?
>
> Wo ist die Spezifikation für den Wert in k:
> int8_t k=0xFF;

Das hat mit Casts nichts zu tun. 0xFF ist vom Typ int. Die unteren 8 Bit 
dieses int werden nach k kopiert. Der Wert von k ist dann derjenige, der 
rauskommt, wenn man 8 gesetzte Bits als int8_t interpretiert. Welcher 
das ist, ist implementationsabhängig.

Mikro 7. schrieb:
> Wer meinte hier, dass die Darstellung der Literale (ob Dezimal oder Hex)
> keine Rolle spielt? Manchmal schon... (amd64)
> #include <stdint.h>
> int i = 0xFFFFFFFF ;
> int j = 4294967295 ;
>
> Hex ist hier unsigned. Dezimal dagegen long int.

Ja, die Regeln, nach denen bei Integerkonstanten die Typen ausgewählt 
werden, sind für Hex und Dezimal leider unterschiedlich.

Bei dezimalen Konstanten ohne Suffix ist die Reihenfolge int, long, long 
long. Der für die Konstante verwendete Typ ist dann in dieser Reihe der 
erste, in den der Wert reinpasst. Bei hexadezimalen Konstanten heißt die 
Reihe dagegen int, unsigned int, long, unsigned long, long long, 
unsigned long long.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> M. K. schrieb:
>> Wo ist die Spezifikation für den Wert in k:
>> int8_t k=0xFF;
>
> Das hat mit Casts nichts zu tun. 0xFF ist vom Typ int.

0xff ist unsigned.

> Die unteren 8 Bit dieses int werden nach k kopiert.

Das Ergebnis ist implementation defined.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Johann L. schrieb:
> 0xff ist unsigned.

 LOL.
 Bestimmt nicht, aber das hängt einzig und alleine vom Compiler ab.
 Wie der Compiler diesen Wert interpretiert, wird bei der definition
 der Variable festgelegt.
1
    ldi  r16, 0xFF
 Was wird da geladen, -1 oder 255 ?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Marc V. schrieb:
> Johann L. schrieb:
>> 0xff ist unsigned.
>
>  LOL.
>  Bestimmt nicht,

Ja, erst wenn die Konstante groß genug ist: 0x8000 ist z.B. unsigned 
während 32768 signed ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

...bevor ich da noch weiter rumeiere :-)

C99 §6.4.4 Constants
1
.5 The type of an integer constant is the first of the corresponding
2
   list in which its value can be represented.
3
4
Suffix        Decimal               Oct or Hex
5
----------------------------------------------
6
  none            int                      int
7
             long int             unsigned int
8
        long long int                 long int
9
                             unsigned long int
10
                                 long long int
11
                        unsigned long long int
12
----------------------------------------------
13
...

0xff ist also signed (es sei denn, man übersetzt mit -mint8, dann ist es 
unsigned).

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Und zu "int8_t = 0xff"

C99 §6.3.1.3 Signed and unsigned integers conversions
1
.1 When a value with integer type is converted to another integer type
2
   other than _Bool, if the value can be represented by the new type,
3
   it is unchanged.
4
5
.2 Otherwise, if the new type is unsigned, the value is converted by
6
   repeatedly adding or subtracting one more than the maximum value that
7
   can be represented in the new type until the value is in the range
8
   of the new type.
9
10
.3 Otherwise, the new type is signed and the value cannot be represented
11
   in it; either the result is implementation-defined or an
12
   implementation-defined signal is raised.

GCC zu .3
1
o The result of, or the signal raised by, converting an integer to a
2
  signed integer type when the value cannot be represented in an object
3
  of that type (C90 §6.2.1.2, C99 and C11 §6.3.1.3). 
4
5
  For conversion to a type of width N, the value is reduced modulo 2^N
6
  to be within range of the type; no signal is raised.

Und zu Complement:
1
o Whether signed integer types are represented using sign and magnitude,
2
  two's complement, or one's complement, and whether the extraordinary
3
  value is a trap representation or an ordinary value
4
  (C99 and C11 §6.2.6.2). 
5
6
  GCC supports only two's complement integer types, and all bit patterns
7
  are ordinary values.

Marc V. schrieb:
>
1
>     ldi  r16, 0xFF
2
>
>  Was wird da geladen, -1 oder 255?

Das kann aus dem Assembler-Code nicht entnommen werden.  avr-as frisst 
übrigens genauso "ldi *, -1" ohne dass du einen unterschied merken 
wirst.  Es kann sich aber auch um einen fixed-point wert wie -0.008hr 
handeln (short _Fract) der intern so dargestellt wird.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Johann L. schrieb:
> ...bevor ich da noch weiter rumeiere :-)

 und ich auch ;)

 Es ging nur darum, ob man alleine aus dem Wert 0xFF sagen kann
 "das ist signed oder das ist unsigned".
 Kann man aber nicht.
 Alles andere was du angeführt hast ist richtig und ich stimme
 vollkommen mit dir überein.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Marc V. schrieb:
>  Es ging nur darum, ob man alleine aus dem Wert 0xFF sagen kann
>  "das ist signed oder das ist unsigned".
>  Kann man aber nicht.

Aber aus Beitrag "Re: Warum ist mein int8_t 4 Bytes groß? 0xFF != 0xFFFFFFFF" ergibt 
sich doch ganz klar, dass 0xff vom Typ int ist, und der ist signed.

von Carl D. (jcw2)


Lesenswert?

Und weil man auch
1
0xffU
schreiben darf, wenn man lieber unsigned hat.

von Rolf M. (rmagnus)


Lesenswert?

Johann L. schrieb:
> Marc V. schrieb:
>>>     ldi  r16, 0xFF
>> >  Was wird da geladen, -1 oder 255?
>
> Das kann aus dem Assembler-Code nicht entnommen werden.  avr-as frisst
> übrigens genauso "ldi *, -1" ohne dass du einen unterschied merken
> wirst.  Es kann sich aber auch um einen fixed-point wert wie -0.008hr
> handeln (short _Fract) der intern so dargestellt wird.

Genau da liegt der Unterschied in dieser Hinsicht zwischen C und 
Assembler. Letzterer kennt keine signed- oder unsigned-Datentypen. Was 
es ist, hängt einfach davon ab, wie du damit arbeitest und als was du es 
als Programmierer interpretierst. Dem Assembler selbst ist das völlig 
wurscht. Der C-Compiler behandelt signed und unsigned dagegen 
unterschiedlich.

: Bearbeitet durch User
von Ralf D. (doeblitz)


Lesenswert?

M. K. schrieb:
> Stefan U. schrieb:
>> C wurde designed, um Plattformübergreifend zu programmieren. Also wurde
>> ganz bewusst vermieden, -1 als 255 zu behandeln. Denn das würde nur auf
>> bestimmte CPU's zutreffen. Mittlerweilen sind das zwar IMHO alle, das
>> das war nicht immer so und könnte sich im Zukunft auch wieder ändern.
>
> Hm, wann war denn das? Also dass z.B. bei einem int8 eine 255 nicht als
> -1 behandelt wurde? Das ergibt sich doch automatisch aus dem Binärcode
> von 255 dass das bei einem int8 der -1 entspricht.

Über Wikipedia (Einerkomplement) zu 
http://www.fourmilab.ch/documents/univac/minuszero.html gekommen: die 
Univac 1100er waren offenbar solche Kisten.

von M. K. (sylaina)


Lesenswert?

Ralf D. schrieb:
> Über Wikipedia (Einerkomplement) zu
> http://www.fourmilab.ch/documents/univac/minuszero.html gekommen: die
> Univac 1100er waren offenbar solche Kisten.

M. K. schrieb:
> Ja, das war im Prinzip meine Frage: Wann war das auf dem Atmega denn mal
> anders? Muss lange her sein.

Am besten immer erst mal den Thread fertig lesen. Klar gibt es 
verschiedene Darstellungen aber ich meinte damit wann das auf dem Atmega 
mal anders war ;)

von Bernd K. (prof7bit)


Lesenswert?

if (offset == 0xFF) {

Du gehst fälschlicherweise davon aus (und genau darin liegt der Grund 
Deiner Verwirrung) daß zuerst die rechte Seite in das Datenformat der 
linken Seite gepresst wird (somit aus der 255 eine -1 wird) und dann 
erst der Vergleich durchgeführt wird.

Aber dem ist nicht so!

Gemäß C-Standard werden beide Seiten erstmal in ein int konvertiert 
und dann erst wird der Vergleich durchgeführt, also wird aus der 
linken Seite ein (int)-1 und aus der rechten Seite ein (int)255 und das 
ist ganz offensichtlich ungleich!

Du willst es lieber so schreiben wenn es funktionieren soll:

if (offset == -1) {

oder meinetwegen (unschön) auch

if (offset == (int8_t)0xff) {

oder um dem ganzen die Krone der Obskurität aufzusetzten castest Du das 
linke int8_t zuerst in ein uint8_t so daß es bei -1 den Wert 255 
annimmt:

if ((uint8_t)offset == 0xff) {

---

Da es jedoch wohl einen Grund gibt warum offset hier ausdrücklich als 
als signed definiert wurde, offenbar weil es wohl je nach Anwendung 
manchmal durchaus in der Natur der Sache liegt daß sowas wie ein 
"offset" auch mal negative Werte annehmen kann ist es einfacher zu lesen 
und zu verstehen wenn man negative Zahlen auch ganz unverblümt als 
negative Zahlen hinschreibt (also mit Vorzeichen und nicht etwa indirekt 
verklausuliert über wackelige Annahmen interner Zahlenrepräsentationen, 
Überläufe und Zweierkomplemente) und somit wäre der erste Vorschlag

if (offset == -1)

wohl mit Abstand der vernünftigste Weg.

: Bearbeitet durch User
von Ralf D. (doeblitz)


Lesenswert?

M. K. schrieb:
> Ralf D. schrieb:
>> Über Wikipedia (Einerkomplement) zu
>> http://www.fourmilab.ch/documents/univac/minuszero.html gekommen: die
>> Univac 1100er waren offenbar solche Kisten.
>
> M. K. schrieb:
>> Ja, das war im Prinzip meine Frage: Wann war das auf dem Atmega denn mal
>> anders? Muss lange her sein.
>
> Am besten immer erst mal den Thread fertig lesen. Klar gibt es
> verschiedene Darstellungen aber ich meinte damit wann das auf dem Atmega
> mal anders war ;)

Das war halt nicht nur auf dem Atmega lange her, sondern ganz allegmein 
schon so einige Jahrzehnte. ;-)

Ansonsten kannst du auch auf dem Atmega 1er-Komplement benutzen (man muß 
halt selbst für End-Around-Carry sorgen), für Divisionen könnte das 
praktisch sein. Prinzipiell auch nicht anders als z.B. 
Festkommaarithmetik zu benutzen.

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.