PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : c++: Division Double und Integer Konvertierung... Plattformunterschiede


mekakic
2013-07-29, 17:04:25
Ich mache eine einfache double Division und int Konvertierung...

a_ = 100000;
int t = int(a_ / 0.02);

/// gcc 4.6 t= 4999999;
/// vs2003&vs2010: t= 5000000;

Allerdings bekomme ich unterschiedliche Ergebnisse, ob ich das ganze unter VisualStudio oder unter GCC kompiliere. Ich könnte hier jetzt das Divisionsergebnis runden, aber warum verhält sich das hier unterschiedlich?

del_4901
2013-07-29, 17:09:23
Ich mache eine einfache double Division und int Konvertierung...

a_ = 100000;
int t = int(a_ / 0.02);

/// gcc 4.6 t= 4999999;
/// vs2003&vs2010: t= 5000000;

Allerdings bekomme ich unterschiedliche Ergebnisse, ob ich das ganze unter VisualStudio oder unter GCC kompiliere. Ich könnte hier jetzt das Divisionsergebnis runden, aber warum verhält sich das hier unterschiedlich?
Schau dir ASM an warscheinlich ist eines die FPU und das andere SSE oder die Genauigkeit der FPU ist verstellt.

mekakic
2013-07-29, 17:43:01
Danke, aber das verstehe ich jetzt noch weniger. Unter Linux kommt kein SSE zum Einsatz -march steht auf "i586", irritierend finde ich aber das Verhalten:

double d = a_ / 0.02;
int t = int(d); /// 5000000
t = int32(a_ / 0.02); /// 4999999

Zuerst kommt das korrekte Ergenis raus, dann das "falsche". Ich habe mir den ASM output angeschaut, da fehlt mir allerdings die Erfahrung. Auf den ersten Blick sieht das aber ok aus:

49 double d = a_ / 0.02;
00167f7c: mov 0x8(%ebp),%eax
00167f7f: fldl 0x4(%eax)
00167f82: fldl -0x14724(%ecx)
00167f88: fdivrp %st,%st(1)
00167f8a: fstpl -0x10(%ebp)
50 int32 t = int32(d);
00167f8d: fldl -0x10(%ebp)
00167f90: fnstcw -0x12(%ebp)
00167f93: mov -0x12(%ebp),%ax
00167f97: mov $0xc,%ah
00167f99: mov %ax,-0x14(%ebp)
00167f9d: fldcw -0x14(%ebp)
00167fa0: fistpl -0x18(%ebp)
00167fa3: fldcw -0x12(%ebp)
00167fa6: mov -0x18(%ebp),%eax
00167fa9: mov %eax,-0x8(%ebp)
51 t = int32(a_ / 0.02);
00167fac: mov 0x8(%ebp),%eax
00167faf: fldl 0x4(%eax)
00167fb2: fldl -0x14724(%ecx)
00167fb8: fdivrp %st,%st(1)
00167fba: fldcw -0x14(%ebp)
00167fbd: fistpl -0x18(%ebp)
00167fc0: fldcw -0x12(%ebp)
00167fc3: mov -0x18(%ebp),%eax
00167fc6: mov %eax,-0x8(%ebp)

Trap
2013-07-29, 18:47:48
Da gibt es mehrere Teilschritte:
1) 0.02 ist kein von einem double repräsentierbarer Wert. Man bekommt also (0.02+x) mit kleinem x ungleich 0 (in welcher Bitbreite eigentlich? Müssen das 64 sein, oder sind auch mehr erlaubt?)
2) Das Ergebnis von a_/(0.02+x) wird ermittelt (bei x86-FPU eventuell mit 80-bit)
3a) Es wird in einer double Variable gespeichert (wie dabei konvertiert wird weiß ich nicht sicher, wahrscheinlich runden)
4) Es wird per truncate auf eine ganzzahl abgebildet

Milchkanne
2013-07-29, 20:48:34
Vielleicht hilft das hier:
http://stackoverflow.com/questions/752738/why-is-the-result-of-this-explicit-cast-different-from-the-implicit-one

Marscel
2013-07-29, 21:36:17
Wenn ich die Bits richtig lese: erster Cast (FSTP) ist Round-to-nearest-even. Dann wird an der FPU-Config herumgespielt sodass beim zweiten Cast gilt Round-towards-zero.

mekakic
2013-08-01, 10:05:35
Danke... das hat mir sehr geholfen (insbesondere der Stackoverflow Artikel).

Nakai
2013-08-01, 15:46:04
Da gibt es mehrere Teilschritte:
1) 0.02 ist kein von einem double repräsentierbarer Wert. Man bekommt also (0.02+x) mit kleinem x ungleich 0 (in welcher Bitbreite eigentlich? Müssen das 64 sein, oder sind auch mehr erlaubt?)
2) Das Ergebnis von a_/(0.02+x) wird ermittelt (bei x86-FPU eventuell mit 80-bit)
3a) Es wird in einer double Variable gespeichert (wie dabei konvertiert wird weiß ich nicht sicher, wahrscheinlich runden)
4) Es wird per truncate auf eine ganzzahl abgebildet

1) Kommt drauf an, welche Aritmethik verwendet wird. Grundsätzlich sinds bei Double 64Bit. Wird jedoch X87 verwendet, kann intern auf einer bestimmten Genauigkeit gestellt werden(32(Single), 64(Double), 80(Extended)). Standard it es 80Bit, also Extendend. Dann kommt es auf den Rundungsmodi an, welcher IEEE754-"kompatibel" ist. Konvertierung ins interne Format -> Berechnung -> Konvertierung ins Ausgangsformat(Single, Double, Int). Je nach Rundungsmodi wird es korrekt konvertiert. Das ist zwar nicht mehr IEEE754-kompliant, daher kann es zu Fehlern kommen. Deswegen, wenn man sicher gehen will, SSE verwenden(32 oder 64), oder die X87 auf den richtigen Modi stellen.
2) Wird nix anderes für dne Thread eingestellt -> Extended(80bit)
3) Je nach Rundungs-Modi der X87-FPU
4) Mal zum nachlesen: http://www.fizyka.umk.pl/~daras/mtm/25_IA32-1-x87.pdf