Czy matematyka zmiennoprzecinkowa jest zepsuta?

Rozważ następujący kod:

0.1 + 0.2 == 0.3  ->  false
0.1 + 0.2         ->  0.30000000000000004

Dlaczego zdarzają się te nieścisłości?

Author: Rann Lifshitz, 2009-02-26

27 answers

Binarny zmiennoprzecinkowy matematyka wygląda tak. W większości języków programowania oparty jest na standardzie IEEE 754 . JavaScript używa 64-bitowej reprezentacji zmiennoprzecinkowej, która jest taka sama jak double Javy. Sednem problemu jest to, że liczby są reprezentowane w tym formacie jako liczba całkowita razy potęga dwóch; liczby wymierne (takie jak 0.1, która jest 1/10), których mianownik nie jest potęgą dwóch, nie mogą być dokładnie reprezentowane.

Dla 0.1 w standardowy format binary64, reprezentacja może być zapisana dokładnie tak, jak

  • 0.1000000000000000055511151231257827021181583404541015625 w układzie dziesiętnym lub
  • 0x1.999999999999ap-4 wC99

W przeciwieństwie do liczby wymiernej 0.1, która jest 1/10, można zapisać dokładnie tak, jak

  • 0.1 w układzie dziesiętnym lub
  • 0x1.99999999999999...p-4 w analogu notacji szesnastkowej C99, gdzie ... jest nieskończoną sekwencją 9.]}

Stałe 0.2 i 0.3 w twoim programie będzie również przybliżeniem ich prawdziwych wartości. Zdarza się, że najbliższa double do 0.2 jest większa niż liczba wymierna 0.2, ale najbliższa double do 0.3 jest mniejsza niż liczba wymierna 0.3. Suma 0.1 i 0.2 jest większa od liczby wymiernej 0.3 i dlatego nie zgadza się ze stałą w Twoim kodzie.

Dość wszechstronne traktowanie zagadnień arytmetyki zmiennoprzecinkowej jest co każdy informatyk powinien wiedzieć O Arytmetyce Zmiennoprzecinkowej. Aby uzyskać łatwiejsze do strawienia Wyjaśnienie, zobacz floating-point-gui.de .

 1784
Author: Brian R. Bondy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-14 18:04:58

Perspektywa projektanta sprzętu

Uważam, że powinienem dodać do tego perspektywę projektanta sprzętu, ponieważ projektuję i buduję sprzęt zmiennoprzecinkowy. Znajomość pochodzenia błędu może pomóc w zrozumieniu tego, co dzieje się w oprogramowaniu, a ostatecznie mam nadzieję, że pomoże to wyjaśnić powody, dla których błędy zmiennoprzecinkowe zdarzają się i wydają się kumulować w czasie.

1. Przegląd

Z inżynieryjnego punktu widzenia, większość operacji zmiennoprzecinkowych będzie miał pewien element błędu, ponieważ sprzęt, który wykonuje obliczenia zmiennoprzecinkowe, musi mieć błąd mniejszy niż połowa jednej jednostki na ostatnim miejscu. W związku z tym wiele urządzeń zatrzyma się przy precyzji, która jest niezbędna tylko do uzyskania Błędu mniej niż połowa jednej jednostki w ostatnim miejscu dla pojedynczej operacji, co jest szczególnie problematyczne w dzieleniu zmiennoprzecinkowym. To, co stanowi pojedynczą operację, zależy od tego, ile operandów Jednostka bierze. Dla większości jest to dwa, ale niektóre jednostki przyjmują 3 lub więcej operandów. Z tego powodu nie ma gwarancji, że powtarzające się operacje spowodują pożądany błąd, ponieważ błędy sumują się w czasie.

2. Standardy

Większość procesorów stosuje standard IEEE-754, ale niektóre używają denormalizowanych lub innych standardów . Na przykład w IEEE-754 istnieje tryb denormalizowany, który umożliwia reprezentację bardzo małych liczb zmiennoprzecinkowych kosztem precyzji. Na następnie jednak obejmie znormalizowany tryb IEEE-754, który jest typowym trybem pracy.

W standardzie IEEE-754 projektanci sprzętu mogą mieć dowolną wartość error / epsilon, o ile jest ona mniejsza niż połowa jednej jednostki na ostatnim miejscu, a wynik musi być mniejszy niż połowa jednej jednostki na ostatnim miejscu dla jednej operacji. To wyjaśnia, dlaczego w przypadku powtarzających się operacji błędy sumują się. Dla podwójnej precyzji IEEE-754 jest to 54 bit, ponieważ 53 bity są używane do reprezentowania części numerycznej (znormalizowanej), zwanej również mantissa, liczby zmiennoprzecinkowej (np. 5.3 in 5.3e5). Kolejne sekcje szczegółowo opisują Przyczyny błędów sprzętowych w różnych operacjach zmiennoprzecinkowych.

3. Przyczyna błędu zaokrąglania w podziale

Główną przyczyną błędu w dzieleniu zmiennoprzecinkowym są algorytmy podziału używane do obliczania ilorazu. Większość systemów komputerowych oblicza podział za pomocą mnożenia przez odwrotnie, głównie w Z=X/Y, Z = X * (1/Y). Podział jest obliczany iteracyjnie, tzn. każdy cykl oblicza kilka bitów ilorazu, aż do osiągnięcia pożądanej precyzji, która dla IEEE-754 jest czymś z błędem mniejszym niż jedna jednostka na ostatnim miejscu. Tabela odwrotności y (1 / Y) jest znana jako ilorazowa tabela wyboru (QST) w wolnym podziale, a rozmiar w bitach ilorazowej tabeli wyboru jest zwykle szerokością radixa lub liczbą bitów ilorazu obliczonego w każdym z nich iteracja, plus kilka bitów ochronnych. Dla standardu IEEE-754, Podwójna precyzja (64-bit), byłaby to wielkość radix dzielnika, plus kilka bitów ochronnych k, gdzie k>=2. Na przykład, typowa tabela wyboru ilorazu dla dzielnika, który oblicza 2 bity ilorazu na raz (radix 4), to 2+2= 4 bity (plus kilka opcjonalnych bitów).

3.1 błąd zaokrąglania podziału: przybliżenie odwrotności

To, jakie są wzajemności w tabeli wyboru ilorazów, zależy w metodzie podziału : powolny podział, taki jak SRT division, lub szybki podział, taki jak Goldschmidt division; każdy wpis jest modyfikowany zgodnie z algorytmem podziału w celu uzyskania najniższego możliwego błędu. W każdym razie, wszystkie wzajemności są przybliżeniami rzeczywistego wzajemności i wprowadzają pewien element błędu. Zarówno metody powolnego dzielenia, jak i szybkiego dzielenia obliczają iloraz iteracyjnie, tzn. oblicza się pewną liczbę bitów ilorazu każdy krok, a następnie wynik jest odejmowany od dywidendy, a dzielnik powtarza kroki, aż błąd będzie mniejszy niż połowa jednej jednostki na ostatnim miejscu. Metody powolnego dzielenia obliczają stałą liczbę cyfr ilorazu w każdym kroku i są zwykle tańsze do zbudowania, a metody szybkiego dzielenia obliczają zmienną liczbę cyfr na krok i są zwykle droższe do zbudowania. Najważniejszą częścią metod podziału jest to, że większość z nich opiera się na powtarzających się mnożenie przez przybliżenie odwrotności, więc są podatne na błędy.

4. Błędy zaokrąglania w innych operacjach: Truncation

Inną przyczyną błędów zaokrąglania we wszystkich operacjach są różne tryby obcinania ostatecznej odpowiedzi, na które pozwala IEEE-754. Jest to okrojenie, zaokrąglenie w kierunku zera, zaokrąglenie do najbliższej (domyślnie), zaokrąglenie w dół i zaokrąglenie w górę. Wszystkie metody wprowadzają element błędu mniejszy niż jedna jednostka na ostatnim miejscu dla pojedyncza operacja. Z biegiem czasu i powtarzających się operacji, obcinanie dodaje również kumulatywnie do wynikowego błędu. Ten błąd obcinania jest szczególnie problematyczny w wykładnictwie, który obejmuje pewną formę wielokrotnego mnożenia.

5. Operacje Powtarzane

Ponieważ sprzęt wykonujący obliczenia zmiennoprzecinkowe musi tylko dać wynik z błędem mniejszym niż połowa jednej jednostki w ostatnim miejscu dla pojedynczej operacji, błąd będzie rosnąć w stosunku do powtarzających się operacje, jeśli nie są obserwowane. Jest to powód, dla którego w obliczeniach, które wymagają błędu ograniczonego, matematycy używają metod takich jak użycie okrągłej do najbliższej parzystej cyfry w ostatnim miejscu IEEE-754, ponieważ z czasem błędy są bardziej prawdopodobne, aby się wzajemnie anulować, i arytmetyka interwałowaw połączeniu z wariacjami IEEE 754 tryby zaokrąglania do przewidywania błędów zaokrąglania i ich korygowania. Ze względu na niski błąd względny w porównaniu z innymi zaokrągleniami tryb zaokrąglania do najbliższej parzystej cyfry (na ostatnim miejscu) jest domyślnym trybem zaokrąglania IEEE-754.

Należy zauważyć, że domyślny tryb zaokrąglania, zaokrąglanie do najbliższej parzystej cyfry w ostatnim miejscu, gwarantuje błąd mniejszy niż połowa jednej jednostki w ostatnim miejscu dla jednej operacji. Użycie samych okrojenia, zaokrąglenia w górę i zaokrąglenia w dół może spowodować błąd, który jest większy niż połowa jednej jednostki na ostatnim miejscu, ale mniejszy niż jedna jednostka na ostatnim miejscu, więc te tryby nie są zalecane, chyba że są one używane w arytmetyce interwałowej.

6. Podsumowanie

Krótko mówiąc, podstawową przyczyną błędów w operacjach zmiennoprzecinkowych jest połączenie obcinania w sprzęcie i obcinania wzajemności w przypadku dzielenia. Ponieważ standard IEEE-754 wymaga tylko Błędu mniej niż połowa jednej jednostki w ostatnim miejscu dla pojedynczej operacji, błędy zmiennoprzecinkowe nad powtarzającymi się operacjami będą sumować się, chyba że zostaną poprawione.

 509
Author: KernelPanik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-13 16:42:24

Kiedy się nawrócisz .1 lub 1/10 do Bazy 2 (binary) otrzymujemy powtarzający się wzór po przecinku, podobnie jak próba reprezentowania 1/3 w bazie 10. Wartość nie jest dokładna, dlatego nie można wykonać dokładnej matematyki za pomocą zwykłych metod zmiennoprzecinkowych.

 367
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 22:07:00

Większość odpowiedzi tutaj odnosi się do tego pytania w bardzo suchych, technicznych terminach. Chciałbym odnieść się do tego w kategoriach, które normalny człowiek może zrozumieć.

Wyobraź sobie, że próbujesz kroić pizze. Masz zrobotyzowany przecinak do pizzy, który może ciąć plastry pizzy dokładnie na pół. Może zmniejszyć o połowę całą pizzę, lub może zmniejszyć o połowę istniejący kawałek, ale w każdym razie, połowa jest zawsze dokładna.

Ten przecinak do pizzy ma bardzo drobne ruchy, a jeśli zaczniesz od całego pizzy, a następnie zmniejszyć o połowę i kontynuować połowę najmniejszego kawałka za każdym razem, można zrobić połowę 53 razy zanim kawałek będzie zbyt mały, nawet dla jego zdolności wysokiej precyzji. W tym momencie nie możesz już zmniejszyć o połowę tego bardzo cienkiego plasterka, ale musisz go uwzględnić lub wykluczyć w takim stanie, w jakim jest.

Teraz, jak można podzielić wszystkie plastry w taki sposób, że można dodać do jednej dziesiątej (0,1) lub jedną piątą (0,2) pizzy? Zastanów się nad tym i spróbuj to rozpracować. Można nawet spróbować użyć prawdziwa pizza, jeśli masz pod ręką mityczny precyzyjny przecinak do pizzy. :-)


Większość doświadczonych programistów, oczywiście, zna prawdziwą odpowiedź, która jest taka, że nie ma sposobu, aby poskładać dokładnie dziesiątą lub piątą pizzę za pomocą tych plastrów, bez względu na to, jak drobno je pokroić. Możesz zrobić całkiem dobre przybliżenie, a jeśli dodasz przybliżenie 0.1 z przybliżeniem 0.2, otrzymasz całkiem dobre przybliżenie 0.3, ale nadal jest to po prostu, że / align = "left" /

Dla liczb o podwójnej precyzji (co jest precyzją, która pozwala na zmniejszenie o połowę Twojej pizzy 53 razy), liczby od razu mniejsze i większe niż 0,1 to 0,099999999999999999167332731531132594682276248931884765625 i 0.100000000000000055511151231257827021181583404541015625. Ten ostatni jest nieco bliżej 0.1 niż ten pierwszy, więc Parser liczbowy, biorąc pod uwagę wejście 0.1, będzie faworyzował ten drugi.

(różnica między tymi dwoma liczbami jest " najmniejsza slice" , które musimy zdecydować się albo włączyć, co wprowadza odchylenie w górę, lub wykluczyć, co wprowadza odchylenie w dół. Termin techniczny dla tego najmniejszego kawałka to ulp.)

W przypadku 0,2 liczby są takie same, po prostu skalowane przez współczynnik 2. Ponownie faworyzujemy wartość nieco wyższą niż 0,2.

Zauważ, że w obu przypadkach przybliżenia dla 0,1 i 0,2 mają nieznaczne odchylenie w górę. Jeśli dodamy wystarczająco dużo tych uprzedzeń, będą naciskać liczba dalej i dalej od tego, czego chcemy, a w rzeczywistości, w przypadku 0.1 + 0.2, błąd jest na tyle wysoki, że liczba wynikowa nie jest już najbliższą liczbą 0.3.

W szczególności 0.1 + 0.2 to naprawdę 0.1000000000000055511151231257827021181583404541015625 + 0.20000000000000000011102230246251565404236316680908203125 = 0.3000000000000000444089209850062616169452667236328125, natomiast liczba najbliższa 0.3 to faktycznie 0.299999999999999988897769753748434595763683319091796875.


P. S. niektóre języki programowania oferują również przecinarki do pizzy, które potrafią podzielić plastry na dokładne dziesiąte. Chociaż takie przecinarki do pizzy są rzadkie, jeśli masz do nich dostęp, powinieneś go używać, gdy ważne jest, aby móc uzyskać dokładnie jedną dziesiątą lub jedną piątą kawałka.

(Originally posted on Quora.)

 239
Author: Chris Jester-Young,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-22 04:44:18

Błędy zaokrąglania zmiennoprzecinkowego. 0,1 nie może być tak dokładnie odwzorowane w bazie-2 jak w bazie-10 z powodu braku czynnika pierwszego wynoszącego 5. Podobnie jak 1/3 przyjmuje nieskończoną liczbę cyfr do reprezentowania w dziesiętnym, ale jest "0.1" w bazie-3, 0.1 przyjmuje nieskończoną liczbę cyfr w bazie-2, gdzie nie ma w bazie-10. A komputery nie mają nieskończonej ilości pamięci.

 200
Author: Devin Jeanpierre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 21:41:23

Oprócz innych poprawnych odpowiedzi warto rozważyć skalowanie wartości, aby uniknąć problemów z arytmetyką zmiennoprzecinkową.

Na przykład:

var result = 1.0 + 2.0;     // result === 3.0 returns true

... zamiast:

var result = 0.1 + 0.2;     // result === 0.3 returns false

Wyrażenie 0.1 + 0.2 === 0.3 zwraca false w JavaScript, ale na szczęście arytmetyka liczb całkowitych w zmiennoprzecinkowych jest dokładna, więc błędów reprezentacji dziesiętnej można uniknąć poprzez skalowanie.

Jako praktyczny przykład, aby uniknąć problemów zmiennoprzecinkowych, w których dokładność jest najważniejsza, jest Zalecane1 do obsługi pieniędzy jako liczba całkowita reprezentująca liczbę centów: 2550 centów zamiast 25.50 dolarów.


1 Douglas Crockford: JavaScript: the Good Parts : Appendix A - Awful Parts (page 105) .

 100
Author: Daniel Vassallo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-05 02:02:26

moja odpowiedź jest dość długa, więc podzieliłem ją na trzy części. Ponieważ pytanie dotyczy matematyki zmiennoprzecinkowej, położyłem nacisk na to, co maszyna faktycznie robi. Zrobiłem to również specyficzne dla podwójnej (64-bitowej) precyzji, ale argument odnosi się w równym stopniu do dowolnej arytmetyki zmiennoprzecinkowej.

Preambuła

An IEEE 754 double-precision binary floating-point format (binary64) liczba reprezentuje liczbę formularz

Value = (-1)^s *(1.na51na50...na2na1na0)2 * 2e-1023

W 64 bitach:

  • pierwszy bit to bit znaku : 1 jeśli liczba jest ujemna, 0 w przeciwnym razie1.
  • następne 11 bitów to wykładnik, który jest przesunięciem o 1023. Innymi słowy, po odczytaniu bitów wykładnika z liczby podwójnej precyzji, 1023 musi być odejmowany, aby uzyskać moc dwóch.
  • Pozostałe 52 bity to significand (lub mantissa). W mantissie "implikowane" 1. jest zawsze2 pominięte, ponieważ najbardziej znaczącym bitem dowolnej wartości binarnej jest 1.

1 - IEEE 754 pozwala na koncepcję podpisanego zera - +0 i -0 są traktowane inaczej: 1 / (+0) jest dodatnią nieskończonością; 1 / (-0) jest ujemną nieskończonością. Dla wartości zerowych mantysa i wykładnik wszystkie bity są zerowe. Uwaga: wartości zerowe (+0 i -0) nie są jawnie klasyfikowane jako denormalne2.

2 - nie jest tak w przypadku liczb denormalnych , które mają wykładnik przesunięcia równy zero (i implikowany 0.). Zakres denormalnych liczb podwójnej precyzji wynosi dmin ≤ |x / ≤ dmax , gdzie Dmin (najmniejsza reprezentowalna liczba niezerowa) wynosi 2-1023 - 51 (≈ 4.94 * 10-324) I d max (the największa liczba denormalna, dla której mantysa składa się w całości z 1 s) wynosi 2-1023 + 1 - 2-1023 - 51 (≈ 2.225 * 10-308).


Zamiana liczby podwójnej precyzji na binarną

Istnieje wiele konwerterów internetowych do konwersji liczby zmiennoprzecinkowej podwójnej precyzji na binarną(np. binaryconvert.com ), ale oto przykładowy kod C#, aby uzyskać reprezentację IEEE 754 dla liczby podwójnej precyzji (oddzielam trzy części z dwukropkami (:):

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

Przejdźmy do sedna: pierwotne pytanie

(przejdź do dołu dla wersji TL; DR)

Cato Johnston (pytający) zapytał dlaczego 0.1 + 0.2 != 0.3.

W 2007 roku, w ramach programu IEEE 754, wprowadzono nową wersję standardu IEEE 754, w której wprowadzono nową wersję standardu IEEE 754.]}
0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

Zauważ, że mantissa składa się z powtarzających się cyfr 0011. To jest klucz z powodu błędu w obliczeniach-0.1, 0.2 i 0.3 nie mogą być przedstawione w postaci binarnej dokładnie W skończonej Liczba bitów binarnych większa niż 1/9, 1/3 lub 1/7 może być przedstawiona dokładnie w postaci cyfr dziesiętnych.

W 2007 roku, po raz pierwszy w historii, w 2008 roku, w Polsce, w 2009 roku, w Polsce, w 2009 roku, w Polsce, w 2009 roku, w Polsce, w 2009 roku, w 2009 roku, w Polsce, w 2009 roku, w Polsce, w 2009 roku, w 2009 roku.]}
0.1 = 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 = 2^-3 * [1].1001100110011001100110011001100110011001100110011010

Aby dodać dwie liczby, wykładnik musi być taki sam, tj.:

0.1 = 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 = 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum = 2^-3 * 10.0110011001100110011001100110011001100110011001100111

Ponieważ suma nie ma formy 2 n * 1.{BBB} zwiększamy wykładnik o jeden i przesuwamy punkt dziesiętny (binarny ), aby otrzymać:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)

W mantysie są teraz 53 bity(53. jest w nawiasach kwadratowych w linii powyżej). Domyślnie tryb zaokrąglania dla IEEE 754 to "Round to Nearest " - tzn. jeśli liczba x mieści się pomiędzy dwiema wartościami a i b , wartość, w której najmniej znaczący wybierany jest bit równy zero.

a = 2^-2 * 1.0011001100110011001100110011001100110011001100110011
x = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)
b = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

Zauważ, że a i b różnią się tylko ostatnim bitem; ...0011 + 1 = ...0100. W tym przypadku wartość z najmniej znaczącym bitem zera wynosi b , więc suma wynosi:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

TL; DR

Zapisanie 0.1 + 0.2 w reprezentacji binarnej IEEE 754 (z dwukropkami oddzielającymi trzy części) i porównanie go z 0.3, jest to (umieściłem różne bity w nawiasach kwadratowych):

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

Wartości dziesiętne to:

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

Różnica jest dokładnie 2-54, czyli ~5.5511151231258 × 10-17 - nieistotne (dla wielu aplikacji) w porównaniu z oryginalnymi wartościami.

Porównywanie ostatnich kilku bitów liczby zmiennoprzecinkowej jest z natury niebezpieczne, ponieważ każdy, kto czyta słynne " to, co każdy informatyk powinien wiedzieć o arytmetyce zmiennoprzecinkowej " (która obejmuje wszystkie główne części tej odpowiedzi), wiedzieć.

Większość kalkulatorów używa dodatkowych wartych cyfr , aby obejść ten problem, czyli jak 0.1 + 0.2 da 0.3: kilka ostatnich bitów jest zaokrąglonych.

 82
Author: Wai Ha Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:18

Liczby zmiennoprzecinkowe przechowywane w komputerze składają się z dwóch części, liczby całkowitej i wykładnika, na które baza jest pobierana i mnożona przez część całkowitą.

Gdyby komputer działał w bazie 10, 0.1 byłby 1 x 10⁻¹, 0.2 będzie 2 x 10⁻¹, A 0.3 będzie 3 x 10⁻¹. Matematyka całkowita jest łatwa i dokładna, więc dodanie 0.1 + 0.2 oczywiście spowoduje 0.3.

Komputery zwykle nie pracują w bazie 10, tylko w bazie 2. Można jeszcze uzyskać dokładne wyniki dla niektórych wartości, dla przykład 0.5 to 1 x 2⁻¹ i 0.25 to 1 x 2⁻², a dodanie ich skutkuje 3 x 2⁻², lub 0.75. Dokładnie.

Problem dotyczy liczb, które można przedstawić dokładnie w bazie 10, ale nie w bazie 2. Liczby te należy zaokrąglić do ich najbliższego odpowiednika. Przy założeniu bardzo popularnego 64-bitowego formatu zmiennoprzecinkowego IEEE najbliższą liczbą 0.1 jest 3602879701896397 x 2⁻⁵⁵, a najbliższą liczbą 0.2 jest 7205759403792794 x 2⁻⁵⁵; dodanie ich razem daje 10808639105689191 x 2⁻⁵⁵ lub dokładną wartość dziesiętną 0.3000000000000000444089209850062616169452667236328125. Floating liczby punktów są zazwyczaj zaokrąglane do wyświetlania.

 51
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-20 05:00:08

Zmiennoprzecinkowy błąd zaokrąglania. Z Co Każdy Informatyk Powinien Wiedzieć O Arytmetyce Zmiennoprzecinkowej :

Wyciskanie nieskończenie wielu liczb rzeczywistych do skończonej liczby bitów wymaga przybliżonej reprezentacji. Chociaż istnieje nieskończenie wiele liczb całkowitych, w większości programów wynik obliczeń całkowitych może być przechowywany w 32 bitach. Natomiast, biorąc pod uwagę dowolną stałą liczbę bitów, większość obliczeń z liczbami rzeczywistymi wytworzy ilości, które nie mogą być dokładnie reprezentowane za pomocą wielu bitów. W związku z tym wynik obliczeń zmiennoprzecinkowych musi być często zaokrąglany, aby dopasować się z powrotem do jego skończonej reprezentacji. Ten błąd zaokrąglania jest charakterystyczną cechą obliczeń zmiennoprzecinkowych.

 42
Author: Brett Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 00:38:28

Moje obejście:

function add(a, b, precision) {
    var x = Math.pow(10, precision || 2);
    return (Math.round(a * x) + Math.round(b * x)) / x;
}

precision odnosi się do liczby cyfr, które chcesz zachować po przecinku podczas dodawania.

 30
Author: Justineo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-26 06:51:53

Opublikowano wiele dobrych odpowiedzi, ale chciałbym dodać jeszcze jedną.

Nie wszystkie liczby mogą być reprezentowane przez / doubles Na przykład liczba "0.2" będzie reprezentowana jako "0.200000003" w pojedynczej precyzji w standardzie IEEE754 punktu zmiennoprzecinkowego.

Model zapisywania liczb rzeczywistych pod maską przedstawia liczby float jako

Tutaj wpisz opis obrazka

Mimo, że możesz łatwo wpisać 0.2, FLT_RADIX i DBL_RADIX jest 2, a nie 10 dla komputera z FPU, który używa " standardu IEEE dla binarnej arytmetyki zmiennoprzecinkowej (ISO / IEEE Std 754-1985)".

Trudno więc dokładnie przedstawić takie liczby. Nawet jeśli określisz tę zmienną jawnie bez żadnych obliczeń pośrednich.
 27
Author: bruziuz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 06:59:19

Niektóre statystyki związane z tym słynnym pytaniem o podwójną precyzję.

Przy dodawaniu wszystkich wartości (a + b) za pomocą kroku 0.1 (od 0.1 do 100) mamy ~15% szans błędu dokładności . Zauważ, że błąd może spowodować nieco większe lub mniejsze wartości. Oto kilka przykładów:

0.1 + 0.2 = 0.30000000000000004 (BIGGER)
0.1 + 0.7 = 0.7999999999999999 (SMALLER)
...
1.7 + 1.9 = 3.5999999999999996 (SMALLER)
1.7 + 2.2 = 3.9000000000000004 (BIGGER)
...
3.2 + 3.6 = 6.800000000000001 (BIGGER)
3.2 + 4.4 = 7.6000000000000005 (BIGGER)

Po odjęciu wszystkich wartości (a-b Gdzie A > b) przy użyciu kroku 0,1 (od 100 do 0,1) mamy ~34% szans błędu dokładności. Oto kilka przykłady:

0.6 - 0.2 = 0.39999999999999997 (SMALLER)
0.5 - 0.4 = 0.09999999999999998 (SMALLER)
...
2.1 - 0.2 = 1.9000000000000001 (BIGGER)
2.0 - 1.9 = 0.10000000000000009 (BIGGER)
...
100 - 99.9 = 0.09999999999999432 (SMALLER)
100 - 99.8 = 0.20000000000000284 (BIGGER)

*15% i 34% są naprawdę ogromne, więc Zawsze używaj BigDecimal, gdy precyzja ma duże znaczenie. Przy 2 cyfrach dziesiętnych (krok 0.01) sytuacja pogarsza się nieco bardziej (18% i 36%).

 24
Author: Konstantinos Chalkias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-04 08:41:25

Nie, Nie złamane, ale większość ułamków dziesiętnych musi być przybliżona

Podsumowanie

Arytmetyka zmiennoprzecinkowa jest dokładna, niestety, nie pasuje dobrze do naszej zwykłej reprezentacji liczb bazowych 10, więc okazuje się, że często dajemy jej Dane wejściowe, które są nieco odbiegające od tego, co napisaliśmy.

Parzyste liczby proste, takie jak 0.01, 0.02, 0.03, 0.04 ... 0.24 nie są reprezentowalne dokładnie jako ułamki binarne. Jeśli policzysz 0.01, .02, .03 ..., dopiero gdy dojdziesz do 0.25 otrzymasz pierwszą frakcję reprezentowalną w bazie2. Jeśli spróbujesz tego używając FP, twój 0.01 byłby nieco wyłączony, więc jedyny sposób, aby dodać 25 z nich do ładnego dokładnego 0.25 wymagałby długiego łańcucha przyczynowości obejmującego bity ochronne i zaokrąglenia. Trudno to przewidzieć, więc podnosimy ręce i mówimy "FP jest niedokładne", ale to nie do końca prawda.

Stale dajemy sprzętowi FP coś, co wydaje się proste w bazie 10 ale jest ułamkiem powtarzającym się w bazie 2.

Jak to się stało?

Gdy zapisujemy dziesiętnym, każdy ułamek (konkretnie każdy kończący dziesiętny) jest liczbą wymierną postaci

           a / (2 n x 5 m)

W systemie binarnym otrzymujemy tylko 2n termin, czyli:

a / 2 n

Więc w układzie dziesiętnym nie możemy reprezentować 1/3. Ponieważ baza 10 zawiera 2 jako czynnik pierwszy, każda liczba, którą możemy zapisać jako ułamek binarny , również może być zapisana jako ułamek bazowy 10. Jednak prawie nic, co piszemy jako podstawę10 ułamek można przedstawić w postaci binarnej. W zakresie od 0,01, 0,02, 0,03 ... 0.99, tylko trzy liczby mogą być reprezentowane w naszym formacie FP: 0.25, 0.50 i 0.75, ponieważ są to liczby 1/4, 1/2 i 3/4, wszystkie liczby z czynnikiem pierwszym, używając tylko liczby 2 N term.

W bazie10 nie możemy reprezentować 1/3. Ale w binarnych, nie możemy zrobić 1/10lub 1/3.

Tak więc, podczas gdy każdy ułamek binarny może być zapisany w dziesiętnym, odwrotność nie jest prawdziwa. W rzeczywistości większość ułamków dziesiętnych powtarza się w postaci binarnej.

radzenie sobie z tym

Deweloperzy są zwykle poinstruowani, aby zrobić porównania, lepsze Rady może to być zaokrąglenie do wartości całkowych (w bibliotece C: round () i roundf (), tzn. pozostanie w formacie FP), a następnie porównanie. Zaokrąglanie do określonej długości ułamka dziesiętnego rozwiązuje większość problemów z wyjściem.

Ponadto, na rzeczywistych problemach chrupania liczb (problemy, dla których FP został wymyślony na wczesnych, przerażająco drogich komputerach) stałe fizyczne wszechświata i wszystkie inne pomiary są znane tylko stosunkowo niewielkiej liczbie znaczących liczb, więc cały problem kosmos i tak był "niedokładny". "Dokładność" FP nie stanowi problemu w tego typu aplikacjach.

Cały problem pojawia się naprawdę, gdy ludzie próbują używać FP do liczenia fasoli. Działa do tego, ale tylko wtedy, gdy trzymamy się wartości całkowych, co w pewnym sensie podważa sens jej używania. dlatego mamy wszystkie biblioteki oprogramowania ułamków dziesiętnych.

Uwielbiam Pizza Odpowiedz przez Chris , ponieważ opisuje rzeczywisty problem, a nie tylko zwykłe ręczne oszczędzanie o "niedokładność". Gdyby FP były po prostu "niedokładne", moglibyśmy to naprawić i zrobilibyśmy to kilkadziesiąt lat temu. Powodem, dla którego nie mamy, jest to, że format FP jest kompaktowy i szybki i jest to najlepszy sposób na schrupanie wielu liczb. Jest to również dziedzictwo z epoki kosmicznej i wyścigu zbrojeń oraz wczesne próby rozwiązania dużych problemów z bardzo powolnymi komputerami wykorzystującymi małe systemy pamięci. (Czasami pojedynczerdzenie magnetyczne do 1-bitowego przechowywania, ale to jest inna historia.)

Podsumowanie

Jeśli tylko liczysz fasolę w banku, rozwiązania programowe, które wykorzystują reprezentacje dziesiętne, działają doskonale. Ale nie można w ten sposób uprawiać chromodynamiki kwantowej ani aerodynamiki.

 22
Author: DigitalRoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-10 22:43:27

Próbowałeś taśmy klejącej?

Spróbuj określić, kiedy występują błędy i naprawić je krótkimi instrukcjami if, nie jest to ładne, ale w przypadku niektórych problemów jest to jedyne rozwiązanie i jest to jedno z nich.

 if( (n * 0.1) < 100.0 ) { return n * 0.1 - 0.000000000000001 ;}
                    else { return n * 0.1 + 0.000000000000001 ;}    

Miałem ten sam problem w projekcie symulacji naukowej w c#, i mogę ci powiedzieć, że jeśli zignorujesz Efekt motyla, to zmieni się w wielkiego tłustego smoka i ugryzie cię w a**

 20
Author: workoverflow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 18:50:36

Te dziwne liczby pojawiają się, ponieważ komputery używają systemu liczb binarnych (baza 2) do celów obliczeniowych, podczas gdy my używamy dziesiętnych(baza 10).

Istnieje większość liczb ułamkowych, których nie można dokładnie przedstawić ani w binarnym, ani w dziesiętnym, ani w obu. Wynik-zaokrąglona (ale precyzyjna) liczba wyników.

 15
Author: Piyush S528,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-14 16:45:17

Mogę tylko dodać; ludzie zawsze zakładają, że to jest problem z komputerem, ale jeśli liczysz rękami (baza 10), nie możesz dostać (1/3+1/3=2/3)=true, chyba że masz nieskończoność do dodania 0.333... do 0.333.. podobnie jak w przypadku problemu (1/10+2/10)!==3/10 w bazie 2, obciąć go do 0.333 + 0.333 = 0.666 i prawdopodobnie zaokrąglić go do 0.667, co również byłoby technicznie niedokładne.

Licz w trzech, a trzecie nie stanowią jednak problemu - może jakaś rasa z 15 palcami na każdej ręce zapytałaby, Dlaczego Twój dziesiętny Matematyka została złamana...

 14
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-26 22:00:40

Wiele z licznych duplikatów tego pytania Pyta o wpływ zaokrąglania zmiennoprzecinkowego na określone liczby. W praktyce łatwiej jest zorientować się, jak to działa, patrząc na dokładne wyniki obliczeń zainteresowania, a nie tylko czytając o tym. Niektóre języki oferują takie sposoby, jak konwersja float lub double na BigDecimal w Javie.

Ponieważ jest to pytanie językowo-agnostyczne, potrzebne są narzędzia językowo-agnostyczne, takie jak dziesiętne do Konwerter Zmiennoprzecinkowy .

Zastosowanie go do liczb w pytaniu, traktowanych jako podwójne:

0.100000000000055511151231257827021181583404541015625,

0.2000000000000011102230246251565404236316680908203125,

0.39999999999999988897769753748434595763683319091796875 i

0.3000000000000000000000000000444089209850062616169452667236328125

Dodanie pierwszego dwie liczby ręcznie lub w kalkulatorze dziesiętnym, takim jak Full Precision Calculator , pokazuje dokładną sumę rzeczywistych wejść wynosi 0.300000000000000000166533453693773481063544750213623046875.

Gdyby zaokrąglono go w dół do ekwiwalentu 0,3, błąd zaokrąglania wynosiłby 0,0000000000000000000000277555756156289135105907917022705078125. Zaokrąglenie do ekwiwalentu 0,3000000000000000004 daje również błąd zaokrąglenia 0,00000000000000000000277555756156289135105907917022705078125. Na stosuje się tie breaker od rundy do parzystej.

Wracając do konwertera zmiennoprzecinkowego, surowy szesnastkowy dla 0,30000000000000000004 to 3fd3333333333334, który kończy się parzystą cyfrą i dlatego jest poprawnym wynikiem.

 13
Author: Patricia Shanahan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-22 16:18:30

Biorąc pod uwagę, że nikt o tym nie wspomniał...

Niektóre języki wysokiego poziomu, takie jak Python i Java, mają narzędzia do przezwyciężania binarnych ograniczeń zmiennoprzecinkowych. Na przykład:

  • Python 's decimal module i Java' S BigDecimal klasa , która wewnętrznie reprezentuje liczby z notacją dziesiętną (w przeciwieństwie do notacji binarnej). Obie mają ograniczoną precyzję, więc nadal są podatne na błędy, jednak rozwiązują najczęstsze problemy z binarną zmiennoprzecinkową arytmetyka.

    10 centów plus 20 centów to zawsze dokładnie trzydzieści centów:]}
    >>> 0.1 + 0.2 == 0.3
    False
    >>> Decimal('0.1') + Decimal('0.2') == Decimal('0.3')
    True
    

    Moduł Pythona decimal jest oparty na standardzie IEEE 854-1987.

  • Python 's fractions module i Apache Common' s BigFraction klasa . Obie reprezentują Liczby wymierne jako pary (numerator, denominator) i mogą dawać dokładniejsze wyniki niż arytmetyka dziesiętna zmiennoprzecinkowa.

Żadne z tych rozwiązań jest doskonała (zwłaszcza jeśli patrzymy na wyniki, lub jeśli wymagamy bardzo wysokiej precyzji), ale mimo to rozwiązuje wiele problemów z binarną arytmetyką zmiennoprzecinkową.

 12
Author: Andrea Corbellini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-21 15:03:35

Rodzaj matematyki zmiennoprzecinkowej, która może być zaimplementowana w komputerze cyfrowym, wykorzystuje przybliżenie liczb rzeczywistych i operacje na nich. (Wersja standard obejmuje ponad pięćdziesiąt stron dokumentacji i ma komitet, który zajmie się jej erratą i dalszym udoskonalaniem.)

To przybliżenie jest mieszaniną przybliżeń różnego rodzaju, z których każda może być ignorowana lub starannie rozliczana ze względu na swój specyficzny sposób odchylenia od dokładność. Wiąże się to również z wieloma wyjątkowymi przypadkami zarówno na poziomie sprzętu, jak i oprogramowania, które większość ludzi przechodzi obok, udając, że nie zauważają.

Jeśli potrzebujesz nieskończonej precyzji (używając liczby π, na przykład, zamiast jednej z wielu krótszych podstawek), powinieneś napisać lub użyć symbolicznego programu matematycznego.

Ale jeśli nie masz nic przeciwko temu, że czasami matematyka zmiennoprzecinkowa jest rozmyta w wartości i logice, a błędy mogą szybko się kumulować, i możesz napisać swoje wymagania i testy, aby na to pozwolić, wtedy twój kod może często poradzić sobie z tym, co jest w Twoim FPU.

 9
Author: Blair Houghton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-03 07:45:02

Dla Zabawy, grałem z reprezentacji pływaków, zgodnie z definicjami ze standardu C99 i napisałem kod poniżej.

Kod drukuje binarną reprezentację pływaków w 3 oddzielonych grupach

SIGN EXPONENT FRACTION

A następnie wypisuje sumę, która po zsumowaniu z wystarczającą precyzją pokaże wartość, która naprawdę istnieje w sprzęcie.

Więc kiedy napiszesz float x = 999..., kompilator przekształci tę liczbę w reprezentację bitową wydrukowaną przez Funkcja xx taka, że suma wydrukowana przez funkcję yy jest równa podanej liczbie.

W rzeczywistości suma ta jest tylko przybliżeniem. Dla liczby 999,999,999 kompilator wstawia w reprezentacji bitowej float liczbę 1,000,000,000

Po kodzie dołączam sesję konsoli, w której obliczam sumę pojęć dla obu stałych (minus PI i 9999999999), które naprawdę istnieją w sprzęcie, wstawione tam przez kompilator.

#include <stdio.h>
#include <limits.h>

void
xx(float *x)
{
    unsigned char i = sizeof(*x)*CHAR_BIT-1;
    do {
        switch (i) {
        case 31:
             printf("sign:");
             break;
        case 30:
             printf("exponent:");
             break;
        case 23:
             printf("fraction:");
             break;

        }
        char b=(*(unsigned long long*)x&((unsigned long long)1<<i))!=0;
        printf("%d ", b);
    } while (i--);
    printf("\n");
}

void
yy(float a)
{
    int sign=!(*(unsigned long long*)&a&((unsigned long long)1<<31));
    int fraction = ((1<<23)-1)&(*(int*)&a);
    int exponent = (255&((*(int*)&a)>>23))-127;

    printf(sign?"positive" " ( 1+":"negative" " ( 1+");
    unsigned int i = 1<<22;
    unsigned int j = 1;
    do {
        char b=(fraction&i)!=0;
        b&&(printf("1/(%d) %c", 1<<j, (fraction&(i-1))?'+':')' ), 0);
    } while (j++, i>>=1);

    printf("*2^%d", exponent);
    printf("\n");
}

void
main()
{
    float x=-3.14;
    float y=999999999;
    printf("%lu\n", sizeof(x));
    xx(&x);
    xx(&y);
    yy(x);
    yy(y);
}

Tutaj jest sesją konsoli, w której obliczam rzeczywistą wartość float, która istnieje w sprzęcie. Użyłem bc aby wydrukować sumę pojęć wypisanych przez główny program. Można wstawić tę sumę w Pythonie repl lub coś podobnego.

-- .../terra1/stub
@ qemacs f.c
-- .../terra1/stub
@ gcc f.c
-- .../terra1/stub
@ ./a.out
sign:1 exponent:1 0 0 0 0 0 0 fraction:0 1 0 0 1 0 0 0 1 1 1 1 0 1 0 1 1 1 0 0 0 0 1 1
sign:0 exponent:1 0 0 1 1 1 0 fraction:0 1 1 0 1 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 1 0 0 0
negative ( 1+1/(2) +1/(16) +1/(256) +1/(512) +1/(1024) +1/(2048) +1/(8192) +1/(32768) +1/(65536) +1/(131072) +1/(4194304) +1/(8388608) )*2^1
positive ( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
-- .../terra1/stub
@ bc
scale=15
( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
999999999.999999446351872

To jest to. Wartość 999999999 jest w rzeczywistości

999999999.999999446351872

Możesz również sprawdzić za pomocą bc, że -3,14 również jest zaburzone. Nie zapomnij ustawić współczynnika scale w bc.

Wyświetlana suma jest tym, co wewnątrz sprzętu. Wartość uzyskasz obliczając to zależy od ustawionej skali. Ustawiłem współczynnik scale na 15. Matematycznie, z nieskończoną precyzją, wydaje się, że jest to 1.000.000.000.

 9
Author: alinsoar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 02:00:04

Aby zaoferować najlepsze rozwiązanie mogę powiedzieć, że odkryłem następującą metodę:

parseFloat((0.1 + 0.2).toFixed(10)) => Will return 0.3
Pozwól mi wyjaśnić, dlaczego to najlepsze rozwiązanie. Jak inni wspomniani w powyższych odpowiedziach, dobrym pomysłem jest użycie gotowej do użycia funkcji Javascript Tofixed() do rozwiązania problemu. Ale najprawdopodobniej napotkasz pewne problemy.

Wyobraź sobie, że dodasz dwie liczby float, takie jak 0.2 i 0.7 Oto jest: 0.2 + 0.7 = 0.8999999999999999.

Twój oczekiwany wynik był 0.9 to znaczy w tym przypadku potrzebujesz wyniku z 1-cyfrową precyzją. Więc powinieneś użyć (0.2 + 0.7).tofixed(1) ale nie możesz po prostu podać określonego parametru tofixed (), ponieważ zależy on od podanej liczby, na przykład

`0.22 + 0.7 = 0.9199999999999999`

W tym przykładzie potrzebujesz 2 cyfr precyzji, więc powinna być toFixed(2), więc jaki powinien być parametr, aby pasował do każdej podanej liczby float?

Można powiedzieć Niech będzie 10 w każdej sytuacji wtedy:

(0.2 + 0.7).toFixed(10) => Result will be "0.9000000000"
Cholera! Co zrobisz z tymi niechcianymi zerami po 9? Jest to czas, aby przekształcić go w pływak, aby zrobić go tak, jak chcesz: {]}
parseFloat((0.2 + 0.7).toFixed(10)) => Result will be 0.9

Teraz, gdy znalazłeś rozwiązanie, lepiej zaoferować je jako funkcję taką jak ta:

function floatify(number){
           return parseFloat((number).toFixed(10));
        }

Spróbujmy sam:

function floatify(number){
       return parseFloat((number).toFixed(10));
    }
 
function addUp(){
  var number1 = +$("#number1").val();
  var number2 = +$("#number2").val();
  var unexpectedResult = number1 + number2;
  var expectedResult = floatify(number1 + number2);
  $("#unexpectedResult").text(unexpectedResult);
  $("#expectedResult").text(expectedResult);
}
addUp();
input{
  width: 50px;
}
#expectedResult{
color: green;
}
#unexpectedResult{
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="number1" value="0.2" onclick="addUp()" onkeyup="addUp()"/> +
<input id="number2" value="0.7" onclick="addUp()" onkeyup="addUp()"/> =
<p>Expected Result: <span id="expectedResult"></span></p>
<p>Unexpected Result: <span id="unexpectedResult"></span></p>

Możesz go użyć w ten sposób:

var x = 0.2 + 0.7;
floatify(x);  => Result: 0.9

Ponieważ W3SCHOOLS sugeruje, że istnieje również inne rozwiązanie, możesz mnożyć i dzielić, aby rozwiązać powyższy problem:

var x = (0.2 * 10 + 0.1 * 10) / 10;       // x will be 0.3

Należy pamiętać, że (0.2 + 0.1) * 10 / 10 w ogóle nie będzie działać, chociaż to wygląda tak samo! Wolę pierwsze rozwiązanie, ponieważ mogę je zastosować jako funkcję, która konwertuje pływak wejściowy na dokładny pływak wyjściowy.

 7
Author: Mohammad lm71,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-13 08:27:16

Inny sposób na to: używane są 64 bity do reprezentowania liczb. W konsekwencji nie ma możliwości, aby więcej niż 2**64 = 18,446,744,073,709,551,616 różnych liczb można dokładnie przedstawić.

Jednak Matematyka mówi, że istnieje nieskończenie wiele miejsc po przecinku między 0 a 1. IEEE 754 definiuje kodowanie, aby efektywnie używać tych 64 bitów dla znacznie większej przestrzeni liczbowej plus NaN i + / - nieskończoności, więc istnieją luki między dokładnie reprezentowanymi liczbami wypełnionymi tylko liczbami / align = "left" /

Niestety 0.3

 3
Author: noiv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-19 22:48:08

Ponieważ ten wątek rozwinął się nieco w ogólną dyskusję nad bieżącymi implementacjami zmiennoprzecinkowymi, dodam, że istnieją projekty naprawiające ich problemy.

Spójrz na https://posithub.org na przykład / , który prezentuje Typ liczby o nazwie posit (i jego poprzednik unum), który obiecuje oferować lepszą dokładność przy mniejszej liczbie bitów. Jeśli moje zrozumienie jest poprawne, to również rozwiązuje tego rodzaju problemy w pytaniu. Dość ciekawy projekt, osoba za nim stojąca jest matematykiem it dr John Gustafson . Całość jest open source, z wieloma rzeczywistymi implementacjami w C / C++, Pythonie, Julii i C # ( https://hastlayer.com/arithmetics).

 3
Author: Piedone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-12 17:26:20

Matematyka.sum (javascript ) .... rodzaj zamiennika operatora

.1 + .0001 + -.1 --> 0.00010000000000000286
Math.sum(.1 , .0001, -.1) --> 0.0001

Object.defineProperties(Math, {
    sign: {
        value: function (x) {
            return x ? x < 0 ? -1 : 1 : 0;
            }
        },
    precision: {
        value: function (value, precision, type) {
            var v = parseFloat(value), 
                p = Math.max(precision, 0) || 0, 
                t = type || 'round';
            return (Math[t](v * Math.pow(10, p)) / Math.pow(10, p)).toFixed(p);
        }
    },
    scientific_to_num: {  // this is from https://gist.github.com/jiggzson
        value: function (num) {
            //if the number is in scientific notation remove it
            if (/e/i.test(num)) {
                var zero = '0',
                        parts = String(num).toLowerCase().split('e'), //split into coeff and exponent
                        e = parts.pop(), //store the exponential part
                        l = Math.abs(e), //get the number of zeros
                        sign = e / l,
                        coeff_array = parts[0].split('.');
                if (sign === -1) {
                    num = zero + '.' + new Array(l).join(zero) + coeff_array.join('');
                } else {
                    var dec = coeff_array[1];
                    if (dec)
                        l = l - dec.length;
                    num = coeff_array.join('') + new Array(l + 1).join(zero);
                }
            }
            return num;
         }
     }
    get_precision: {
        value: function (number) {
            var arr = Math.scientific_to_num((number + "")).split(".");
            return arr[1] ? arr[1].length : 0;
        }
    },
    diff:{
        value: function(A,B){
            var prec = this.max(this.get_precision(A),this.get_precision(B));
            return +this.precision(A-B,prec);
        }
    },
    sum: {
        value: function () {
            var prec = 0, sum = 0;
            for (var i = 0; i < arguments.length; i++) {
                prec = this.max(prec, this.get_precision(arguments[i]));
                sum += +arguments[i]; // force float to convert strings to number
            }
            return Math.precision(sum, prec);
        }
    }
});

Idea polega na używaniu operatorów matematycznych, aby uniknąć błędów float

Math.diff(0.2, 0.11) == 0.09 // true
0.2 - 0.11 == 0.09 // false
Zauważ również, że matematyka.diff i matematyka.sum automatycznie wykrywa precyzję użycia Matematyka.suma przyjmuje dowolną liczbę argumentów
 2
Author: bortunac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-21 12:13:46

Inne pytanie zostało nazwane jako DUPLIKAT tego:

W C++, dlaczego wynik cout << x różni się od wartości, którą debugger pokazuje dla x?

x w pytaniu jest zmienną float.

Przykładem może być

float x = 9.9F;

Debuger pokazuje 9.89999962, Wyjście operacji cout to 9.9.

Okazuje się, że domyślna precyzja cout dla float wynosi 6, więc zaokrągla się do 6 dziesiętnych cyfry.

Zobacz tutaj dla odniesienia

 2
Author: Arkadiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-15 13:26:07

Sine Python 3.5 możesz użyć funkcji math.isclose() w Warunkach if

import math

if math.isclose(0.1 + 0.2, 0.3, abs_tol=0.01):
    pass
 2
Author: nauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-08 08:47:36

to było właściwie zamierzone jako odpowiedź na to pytanie -- które zostało zamknięte jako DUPLIKAT to pytanie , podczas gdy składałem tę odpowiedź, więc teraz nie mogę jej tam umieścić... więc napiszę tutaj!


Podsumowanie pytania:

Na arkuszu 10^-8/1000 i 10^-11 oceniają jako równe podczas gdy w VBA nie.

W arkuszu kalkulacyjnym, liczby są domyślne do Notacja Naukowa.

Jeśli zmienisz komórki na format liczby (Ctrl+1) Z Number z 15 przecinkami dziesiętnymi otrzymujemy:

=10^-11 returns 0.000000000010000
=10^(-8/1000) returns 0.981747943019984

Tak więc, na pewno nie są takie same... jeden to prawie zero, a drugi tylko około 1.

Excel nie został zaprojektowany do radzenia sobie z ekstremalnie małymi liczbami - przynajmniej nie z instalacją zapasów. Istnieją dodatki, które pomagają poprawić precyzję numerów.


Excel został zaprojektowany w zgodnie ze STANDARDEM IEEE dla binarnej arytmetyki zmiennoprzecinkowej ( IEEE 754 ). Standard określa, w jaki sposób liczby zmiennoprzecinkowe są przechowywane i obliczane. Standard IEEE 754 jest powszechnie stosowany, ponieważ pozwala na przechowywanie liczb zmiennoprzecinkowych w rozsądnej ilości miejsca, a obliczenia mogą następować stosunkowo szybko.

Zaletą reprezentacji zmiennoprzecinkowej jest to, że może obsługiwać szerszy zakres wartości. Na przykład, a reprezentacja fixed-point, która ma 5 cyfr dziesiętnych z punktem dziesiętnym umieszczonym za trzecią cyfrą może reprezentować liczby 123.34, 12.23, 2.45, itd. natomiast reprezentacja zmiennoprzecinkowa z 5-cyfrową precyzją może reprezentować 1.2345, 12345, 0.00012345 itd. Podobnie, reprezentacja zmiennoprzecinkowa umożliwia również obliczenia w szerokim zakresie wielkości przy zachowaniu precyzji. Na przykład,

img


Inne Bibliografia:

 2
Author: ashleedawg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-02 03:42:06