Also habe ich ungefähr diesen Code:
uint32_t s1 = 0xFFFFFFFFU;
uint32_t s2 = 0xFFFFFFFFU;
uint32_t v;
...
v = s1 * s2; /* Only need the low 32 bits of the result */
Im Folgenden gehe ich davon aus, dass der Compiler keine vorgefassten Meinungen über den Bereich von haben konnte s1
oder s2
die Initialisierer dienen oben nur als Beispiel.
Wenn ich dies auf einem Compiler mit einer Integer-Größe von 32 Bit kompiliert habe (z. B. beim Kompilieren für x86), kein Problem. Der Compiler würde einfach verwenden s1
und s2
wie uint32_t
typisierte Werte (nicht in der Lage, sie weiter zu fördern), und die Multiplikation würde einfach das Ergebnis liefern, wie der Kommentar sagt (modulo UINT_MAX + 1
was in diesem Fall 0x100000000 ist).
Wenn ich dies jedoch auf einem Compiler mit einer ganzzahligen Größe von 64 Bit (z. B. für x86-64) kompiliert habe, kann es zu undefiniertem Verhalten kommen, was ich aus dem C-Standard ableiten kann. Integer-Promotion würde sehen uint32_t
befördert werden kann int
(64 Bit mit Vorzeichen), die Multiplikation würde dann versuchen, zwei zu multiplizieren int
‘s, die, wenn sie zufällig die im Beispiel gezeigten Werte haben, einen ganzzahligen Überlauf verursachen würden, was ein undefiniertes Verhalten ist.
Liege ich damit richtig und wenn ja, wie würden Sie es auf vernünftige Weise vermeiden?
Ich habe diese Frage entdeckt, die ähnlich ist, aber C++ abdeckt: Was ist der beste C++-Weg, um vorzeichenlose Ganzzahlen sicher modular zu multiplizieren?. Hier möchte ich eine auf C anwendbare Antwort erhalten (vorzugsweise C89-kompatibel). Ich würde jedoch nicht in Betracht ziehen, eine schlechte 32-Bit-Maschine zu machen, die möglicherweise eine 64-Bit-Multiplikation ausführt, eine akzeptable Antwort (normalerweise in Code, in dem dies von Bedeutung wäre, könnte die 32-Bit-Leistung kritischer sein, da dies normalerweise die langsameren Maschinen sind).
Beachten Sie, dass das gleiche Problem für 16-Bit-Ints ohne Vorzeichen auftreten kann, wenn sie mit einem Compiler mit einer Int-Größe von 32 Bit kompiliert werden, oder für unsigned Chars, wenn sie mit einem Compiler mit einer Int-Größe von 16 Bit kompiliert werden (letzteres kann bei Compilern für 8-Bit-CPUs üblich sein). : Der C-Standard erfordert, dass Ganzzahlen mindestens 16 Bit lang sind, daher ist ein konformer Compiler wahrscheinlich betroffen).
int
ist 32 Bit, selbst auf den meisten modernen 64-Bit-Architekturen en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models– phuklv
18. November 2014 um 18:56 Uhr
Ach komm schon. Ist irgendetwas sogar in C definiert?
– Harald
18. November 2014 um 19:09 Uhr
@harold, ja:
__STDC__
ist.– fuz
18. November 2014 um 19:14 Uhr
@Tim: Ich habe es extra dafür gepostet, obwohl ich jetzt sogar merke, dass ich es eigentlich könnte brauchen durch ein ganzes Projekt zu schlendern, das so eingerichtet ist, dass es unabhängig von der Compiler-Int-Größe ist (derzeit sowohl auf 32- als auch auf 64-Bit gut funktioniert), um nach solchen Multiplikationen und anderen Dingen zu suchen, die ich für selbstverständlich hielt. Beweist, dass man nie alle Bestien kennen kann, die in den Schatten von C lauern …
– Jubatisch
18. November 2014 um 19:55 Uhr
@TimSeguine Die Beförderung von uint zu int ist akzeptabel, wenn einer der Operanden ein breiteres int ist, aber zwei uints, die in int konvertiert werden, sind böse.
– 2501
18. November 2014 um 19:56 Uhr