co może z aktualnie popularnych urządzeń i oprogramowania, jest
Kompilator kodowane jako 0x1.6666666666666p 0,7-1 (to jest w systemie szesnastkowym liczbowy 1,6666666666666 mnoży się przez 2 do potęgi -1) .2 jako 0x1.999999999999ap-3 i .1 jako 0x1.999999999999ap-4. Każda z nich jest liczbą reprezentowaną przez zmiennoprzecinkę, która jest najbliższa liczbie dziesiętnej, którą napisałeś.
Należy zauważyć, że każda z tych stałych szesnastkowych stała zmiennoprzecinkowych ma dokładnie 53 bity w swym znaczeniu (część "ułamek", często niedokładnie nazywana mantysą). Szesnastkowa liczba znaczników ma cyfrę "1" i trzynaście więcej cyfr szesnastkowych (cztery bity, 52, 53 włącznie z "1"), co zapewnia standard IEEE-754, dla 64-bitowego binarnego liczby punktowe.
Dodajmy liczby dla .7 i .2: 0x1.6666666666666p-1 i 0x1.999999999999ap-3. Najpierw przeskaluj wykładnik drugiej liczby, aby pasował do pierwszego. Aby to zrobić, pomnożymy wykładnik przez 4 (zmieniając "p-3" na "p-1") i pomnożymy znaczenie przez 1/4, podając 0x0.66666666666668p-1. Następnie dodaj 0x1.6666666666666p-1 i 0x0.66666666666668p-1, podając 0x1.ccccccccccccc8p-1. Zauważ, że liczba ta ma więcej niż 53 bity w znaczeniu: "8" to 14 cyfra po kropce. Zmienna zmienna nie może zwracać wyniku z tak wieloma bitami, więc musi być zaokrąglona do najbliższej reprezentowalnej liczby. W tym przypadku są dwie liczby, które są równie bliskie, 0x1.ccccccccccccp-1 i 0x1.ccccccccccccdp-1. W przypadku remisu stosuje się liczbę z zerem w najniższym bitwie znacznika. "c" jest parzyste, a "d" jest nieparzyste, więc użyte jest "c". Ostatecznym wynikiem dodania jest 0x1.cccccccccccccp-1.
Następnie dodaj numer dla .1 (0x1.999999999999ap-4). Znowu skalujemy, aby wykładniki pasowały do siebie, więc 0x1.999999999999ap-4 staje się 0x.33333333333334p-1. Następnie dodaj to do 0x1.cccccccccccccp-1, podając 0x1.fffffffffffff4p-1. Zaokrąglenie tego do 53 bitów daje 0x1.fffffffffffffp-1, i jest to końcowy wynik ".7 + .2 + .1".
Teraz rozważ ".7 + .1 + .2". Dla ".7 + .1" dodaj 0x1.6666666666666p-1 i 0x1.999999999999ap-4. Przypomnijmy, że ta ostatnia jest skalowana do 0x.3333333333333p-1. Wtedy dokładna suma to 0x1.99999999999994p-1. Zaokrąglenie tego do 53 bitów daje 0x1.9999999999999p-1.
Następnie dodaj numer dla .2 (0x1.999999999999ap-3), który jest skalowany do 0x0.66666666666668p-1. Dokładna suma to 0x2.00000000000008p-1. Symbole zmiennoprzecinkowe są zawsze skalowane, tak aby zaczynały się od 1 (z wyjątkiem szczególnych przypadków: zero, nieskończoność i bardzo małe liczby na dole reprezentowalnego zakresu), więc dostosowujemy to do 0x1.00000000000004p0.Na koniec zaokrąglamy do 53 bitów, podając 0x1.0000000000000p0.
Z powodu błędów występujących podczas zaokrąglania, ".7 + .2 + .1" zwraca 0x1.fffffffffffffp-1 (bardzo nieznacznie mniej niż 1), a ".7 + .1 + .2" zwraca 0x1.0000000000000p0 (dokładnie 1).
Twój przykładowy kod różni się * * sprawnością *, a nie * asocjatywnością *. Wersja wykazująca asocjację będzie miała postać '(0.7 + (0.1 + 0.2))' –
@MattMcNabb: + jest operacją binarną. Z operandami zmiennoprzecinkowymi jest to przemienne, ale nie asocjacyjne. Tak więc, jeśli masz dwa wyrażenia, które dają różne wyniki, nie możesz utworzyć jednego z drugiego, stosując tylko przemienność. – tmyklebu
@tmyklebu OK, więc to sprawdza powiązanie wtedy i tylko wtedy, gdy wiadomo, że przechowuje się komutatywność. (Standard C++ nie wydaje się gwarantować przemienności). –