[th/gcc4] apps fixing guide #1 - signed overflow.
Pawe³ Sikora
pluto w agmk.net
Sob, 22 Kwi 2006, 11:24:48 CEST
ostatnio widzę, że dużo na bugzilli gcc ludzie zgłaszają dużo
błędów w stylu "moja aplikacja na gcc4 robi kuku, a na 3.x było ok".
bardzo często są ta programy po prostu błędne podług standardów,
stąd też postanowiłem nieco przybliżyć ten temat deweloperom pld,
by szybciej i łatwiej w Th naprawiać niespodziewane zachowanie
aplikacji, tudzież kuku zwane potocznie GPF-em :)
rozważmy pierwszy załączony przykład:
$ g++ signed_overflow_1.cpp; ./a.out
b = (0x80000000) -2147483648
10 - b = (0x8000000a) -2147483638 < 0 is true.
b - 10 = (0x7ffffff6) +2147483638 < 0 is true.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
tu zapewne niektórym zapalił się pytajnik
i myslą sobie jak do cholery to się mogło stać :)
pozwole sobię zacytować standard:
[ cite C++ standard / $5.5 ]
if during the evaluation of an expression the result isn't mathematically
defined nor in the range of representable values for its type the behaviuor
is undefined, unless such an expression is a constant expression, in which
case the program is ill-formed. (...)
[ /cite ]
w naszym przypadku b - 10, to 0xff(...)7ffffff6, co nie mieści się
w 32 bitach typu integer i kwalifikuje siÄ™ na overflow oraz niezdefiniowane
zachowanie, czyli wyniki z grubsza zależne od natężenia plam na słońcu
i szumów w krzemie :)
poprzednie wersje gcc oraz inne kompilatory zlewajÄ… nieco w.w. punkt
standardu i po cichu "zawijajÄ…" takie wyniki na granicy rozmiaru typu.
stare (błedne zachowanie) można przywrócić za pomocą opcji -fwrapv.
$ g++ signed_overflow_1.cpp -fwrapv; ./a.out
b = (0x80000000) -2147483648
10 - b = (0x8000000a) -2147483638 < 0 is true.
b - 10 = (0x7ffffff6) +2147483638 < 0 is false.
nowsze wersje gcc są już bardziej zbieżne ze standardem
i robią z niego użytek.
skompilujmy dla przykładu drugi załącznik, który nie ma już UB,
ale produkuje rózne wyniki:
$ g++ signed_overflow_2.cpp -O2
$ ./a.out
foo(1) = 0
foo(2147483647) = 0
0000000000000000 <foo(int)>:
0: 31 c0 xor %eax,%eax
2: c3 retq
miodzio i zgodnie z matematyką, bo nigdy i + 1 nie będzie mniejsze od i.
jednak, gdy skompilujemy ów fragment z -fwrapv, to kompilator podda
matematykę rzeczywistości i dostaniemy:
$ g++ signed_overflow_2.cpp -O2 -fwrapv
$ ./a.out
foo(1) = 0
foo(2147483647) = 1
0000000000000000 <foo(int)>:
0: 8d 47 01 lea 0x1(%rdi),%eax
3: 39 c7 cmp %eax,%edi
5: 0f 9f c0 setg %al
8: 0f b6 c0 movzbl %al,%eax
b: c3 retq
jak widać badanie signed overflow może dać duże korzyści przy
optymalizacji kodu, ale również może wpędzić nas w "ukryte" błędy,
bądź niezdefiniowane zachowanie w miejscach, gdzie polega się na
zabawach z bitami.
-------------- nastêpna czê¶æ ---------
Załącznik, który nie był tekstem został usunięty...
Name: signed_overflow_1.cpp
Type: text/x-c++src
Size: 532 bytes
Desc: nie znany
Url : /mailman/pipermail/pld-devel-pl/attachments/20060422/3ada2d9b/attachment.bin
-------------- nastêpna czê¶æ ---------
Załącznik, który nie był tekstem został usunięty...
Name: signed_overflow_2.cpp
Type: text/x-c++src
Size: 196 bytes
Desc: nie znany
Url : /mailman/pipermail/pld-devel-pl/attachments/20060422/3ada2d9b/attachment-0001.bin
Wiêcej informacji o li¶cie dyskusyjnej pld-devel-pl