[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