Frage

Wenn ich den folgenden C -Code auf einem Intel -Computer ausführe ...

float f = -512;
unsigned char c;

while ( f < 513 )
{
    c = f;
    printf( "%f -> %d\n", f, c );
    f += 64;
}

... Die Ausgabe ist wie folgt:

-512.000000 -> 0
-448.000000 -> 64
-384.000000 -> 128
-320.000000 -> 192
-256.000000 -> 0
-192.000000 -> 64
-128.000000 -> 128
-64.000000 -> 192
0.000000 -> 0
64.000000 -> 64
128.000000 -> 128
192.000000 -> 192
256.000000 -> 0
320.000000 -> 64
384.000000 -> 128
448.000000 -> 192
512.000000 -> 0

Wenn ich jedoch denselben Code auf einem ARM -Gerät ausführe (in meinem Fall ein iPad), sind die Ergebnisse sehr unterschiedlich:

-512.000000 -> 0
-448.000000 -> 0
-384.000000 -> 0
-320.000000 -> 0
-256.000000 -> 0
-192.000000 -> 0
-128.000000 -> 0
-64.000000 -> 0
0.000000 -> 0
64.000000 -> 64
128.000000 -> 128
192.000000 -> 192
256.000000 -> 0
320.000000 -> 64
384.000000 -> 128
448.000000 -> 192
512.000000 -> 0

Wie Sie sich vorstellen können, kann diese Art von Unterschied einführen entsetzlich Fehler in plattformübergreifenden Projekten. Meine Fragen sind:

  1. War ich falsch anzunehmen, dass das Zwingen eines Schwimmers in einen nicht signierten Zeichen auf allen Plattformen die gleichen Ergebnisse liefern würde?

  2. Könnte sein Compiler -Problem sein?

  3. Gibt es eine elegante Problemumgehung?

War es hilfreich?

Lösung

Der C -Standard hat keine sehr harten Regeln für das, was Sie versuchen. Hier ist der fragliche Absatz aus Abschnitt 6.3.1 Arithmetische Operanden (speziell Abschnitt 6.3.1.4 Real Floating und Ganzzahl):

Wenn ein endlicher Wert des realen schwebenden Typs in einen anderen Ganzzahltyp als umgewandelt wird _Bool, der fraktionelle Teil wird verworfen (dh der Wert wird in Richtung Null abgeschnitten). Wenn der Wert des integralen Teils nicht durch den Ganzzahltyp dargestellt werden kann, ist das Verhalten undefiniert.

Es gibt sogar eine spezifischere Fußnote über den genauen Fall, nach dem Sie fragen:

Der Restvorgang wird ausgeführt, wenn ein Wert des Ganzzahl -Typs in nicht signiertes Typ konvertiert wird, muss nicht ausgeführt werden, wenn ein Wert des realen schwimmenden Typs in nicht signiertes Typ konvertiert wird. Somit ist der Bereich der tragbaren realen schwebenden Werte (−1, Utype_MAX+1).

UtypeMAX+1 Denn Ihr Fall ist 256. Ihre nicht übereinstimmenden Fälle sind alles negative Zahlen. Nach der Kürzung sind sie immer noch negativ und befinden sich außerhalb des Bereichs (-1, 256), sodass sie fest in der Zone „undefiniertes Verhalten“ sind. Sogar einige der von Ihnen gezeigten Übereinstimmungsfälle, in denen die schwebende Punktzahl größer ist als oder gleich 256, nicht garantiert zu funktionieren - Sie haben nur Glück.

Die Antworten auf Ihre nummerierten Fragen, deshalb:

  1. Ja, du hast dich geirrt.
  2. Es ist ein Compiler -Problem in dem Sinne, dass Ihre verschiedenen Compiler unterschiedliche Ergebnisse liefern, aber da sie von der Spezifikation zu dürfen, würde ich das die Schuld des Compilers nicht wirklich nennen.
  3. Es hängt davon ab, was Sie tun möchten - wenn Sie das besser erklären können, ist jemand in der Community fast mit Sicherheit in der Lage, Ihnen zu helfen.

Andere Tipps

Ich werde in meiner eigenen Frage auf 3 antworten, aber ich werde das nicht als die akzeptierte Antwort markieren. Der Trick scheint eine einfache Besetzung im Zwang zu sein:

c = (char) f;

Die Verwendung von (int) oder (kurz) funktioniert auch. Ich bin immer noch interessiert herauszufinden, wo die Ursache für dieses Problem liegt: Compiler oder Prozessor.

Das spezifische Problem, mit dem Sie es zu tun haben, sieht für mich nach Endianness aus. Versuchen Sie, die eine oder andere Implementierung durch zu ersetzen c = *((char *)&f + sizeof(float) - 1); Oder etwas Ähnliches, um das letzte Byte des Float zu bekommen und zu sehen, ob es dem Ergebnis für die andere Plattform entspricht.

Im Allgemeinen hängt das Verhalten von Endianess, Wortlänge und schwebenden Punktemähungen des Prozessors ab und wie der Compiler darauf abzielt. Arm ist ein Bi-Endian, daher kann er IA-Byte-Bestellung übereinstimmen oder nicht. Es scheint auch keine allgemeine Garantie dafür zu sein, dass eine C -Implementierung das gleiche schwimmende Punktformat wie ein anderes unterstützt: Floßenpunkttypen mit fester Größe .

Verwenden Sie dies im Produktionscode? Ich würde mich sehr hart darüber aussehen, warum dies getan werden muss. Der eine oder andere Typ wird wahrscheinlich nicht wie beabsichtigt verwendet. Problemumgehungen werden nicht elegant sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top