Ten problem nie jest spowodowany konwersją z długich podwójnych na podwójne. Przyczyną może być niedokładność procedury sin
w bibliotece matematycznej.
Instrukcja jest podana w celu uzyskania wyniku w zakresie 1 ULP (w długim podwójnym formacie) dla operandów w swoim zakresie (według Intel 64 i IA-32 Architectures Software Developer's Manual, październik 2011, tom 1, 8.3.10), w trybie od okrągłego do najbliższego. Na Intel Core i7, fsin
wartości pytający użytkownika, -5.07121364272633190495298549649305641651153564453125 lub -0x1.448ec3aaa278dp + 2, produkuje 0xe.fb206c69b0ba402p-4. Z tej heksadecymalnej liczby można łatwo zauważyć, że ostatnie 11 bitów to 100 0000 0010. Są to bity, które zostaną zaokrąglone podczas konwersji z długiego podwójnego. Jeśli są większe niż 100 0000 0000, liczba zostanie zaokrąglona w górę. Są więksi. Dlatego wynikiem konwersji tej długiej podwójnej wartości na double jest 0xe.fb206c69b0ba8p-4, co równa się 0x1.df640d8d36175p-1 i 0.93631021832247418590355891865328885614871978759765625. Zauważ również, że nawet jeśli wynik byłby o jeden ULP niższy, ostatnie 11 bitów wciąż byłby większy niż 100 0000 0000 i nadal zaokrąglałby. Dlatego wynik ten nie powinien się różnić w przypadku procesorów Intela zgodnych z powyższą dokumentacją.
Porównaj to do obliczania podwójnej precyzji sinus bezpośrednio, wykorzystując idealne sin
rutynę, która produkuje poprawnie zaokrąglone wyniki. Sinus wartości wynosi około 0,93631021832247413051857150785044253634581268961333520518023697738674775240815140702992025520721336793516756640679315765619707343171517531053811196321335899848286682535203710849065933755262347468763562 (obliczony dla Maple 10). Podwójne najbliżej tego jest 0x1.df640d8d36175p-1. Jest to ta sama wartość, którą uzyskaliśmy, przekształcając wynik na 22,000.
Dlatego rozbieżność nie jest spowodowana przez konwersję długich podwójnych na podwójne; przekształcenie podwójnego wyniku o podwójnej wartości daje wynik dokładnie taki sam, jak w przypadku idealnej rutyny podwójnej precyzji o dokładności sin
.
Nie mamy specyfikację dokładności rutyny sin
używanego przez pakiet Visual Studio pytający użytkownika. W bibliotekach komercyjnych powszechne jest zezwalanie na błędy rzędu 1 ULP lub kilku ULP. Obserwuj, jak blisko jest sinus do punktu, w którym wartość podwójnej precyzji jest zaokrąglona: Jest to 0,498864 ULP (podwójna precyzja ULP) z dala od podwójnego, czyli wynosi 0,001136 ULP od punktu, w którym zaokrąglenia się zmieniają. Dlatego nawet bardzo niewielka niedokładność w procedurze sin
spowoduje, że zwróci 0x1.df640d8d36174p-1 zamiast bliższego 0x1.df640d8d36175p-1.
Dlatego przypuszczam, że źródłem rozbieżności jest bardzo mała niedokładność w procedurze sin
.
Jedna operacja może odbywać się całkowicie w rejestrach zmiennoprzecinkowych. Druga powoduje utratę precyzji, gdy 80-bitowy rejestr jest zapisany w 64-bitowym adresie pamięci. Dokumentacja FSTP mówi, że "podczas zapisywania wartości w pamięci, wartość jest konwertowana na format pojedynczy lub podwójny." –
W podejściu 'fsin' zastosowano jednostkę FP87 z 80-bitową precyzją, implementacja' sin' w MSVC (używam 2010) wydaje się używać SSE z 128-bitowymi rejestrami xmm *. (Zobacz także [to pytanie] (http://stackoverflow.com/questions/2284860/how-does-c-compute-sin-and-other-math-functions-)). – DCoder