Eval i błąd precyzji

0

W equasion jest string z równaniem do obliczenia:

res = eval(equasion);
	
console.log("**=====**")
console.log(equasion);
console.log(res);

Oto console.log:

**=====**
100.3-20.1
80.19999999999999

Jak z tym walczyć...?

0

Zaokrąglać?

0

Nie da się.

Błąd nie ma też nic wspólnego z eval()em. Swoją drogą: unikaj eval()a niemal za wszelką cenę! Ja muszę go zastosować może raz na rok, a piszę w każdym projekcie tysiące linii JS.

Błąd zaokrągleń jest związany z tym, że w JavaScripcie nie ma typu Decimal, tj. typu stałoprzecinkowego o pełnej precyzji. Typ liczbowy implementuje standard IEEE 754 definiujący zapis liczb zmiennoprzecinkowych. W JavaScripcie używane są liczby zmiennoprzecinkowe o podwójnej precyzji, tj. 64-bitowe. Wspomniany standard, dość powszechnie używany, definiuje taką, a nie inną precyzję liczb i prowadzi ona do tak kuriozalnych przypadków, jak...

console.log(0.3 + 0.6); // 0.8999999999999999

Sorki!

edit: Oczywiście, de facto da się to ominąć. Zawsze można skorzystać z jakiegoś parsera liczb i wyrażeń arytmetycznych, np. operującego na stringach, i wszystkie takie obliczenia przepuszczać przez niego.

Gdy potrzebujesz wykonywać obliczenia o stałej precyzji, np. na pieniądzach, rozwiązaniem jest liczenie nie w złotówkach, tylko w groszach i całkowite uniknięcie przecinków.

0

jak już mówiono to nie wina eval a nawet javascriptu tylko kodowania liczb i ten problem występuje też w innych językach programowania

praktycznie żadna liczba nie jest zapisana w pamięci dokładnie tak jak powinna
użyj:

liczba.toPrecision(21);

żeby zobaczyć pełne dostępne rozwinięcie liczby
przykładowo:

(0.1).toPrecision(21) => "0.100000000000000005551"
(0.2).toPrecision(21) => "0.200000000000000011102"
(0.3).toPrecision(21) => "0.299999999999999988898"
(0.6).toPrecision(21) => "0.599999999999999977796"

jak widać ewentualne błędy się pojawiają zazwyczaj po 16tej cyfrze rozwinięcia
dlatego domyślnie Javascript zaokrągla wynik przy wyświetlaniu do pierwszych 16 cyfr po przecinku

no i tu jest haczyk taki że jeśli przykładowo dodajesz do siebie kilka liczb zapisanych z błędem (jak powyżej) to błędy się nasilają, kumulują i pojawiają się na znacznie wcześniejszych pozycjach co można zaobserwować na przykładzie:

0.3 + 0.6 = 0.299999999999999988898 + 0.599999999999999977796 = 0.899999999999999911182

po zaokrągleniu do 16 miejsca po przecinku: 0.8999999999999999

moim zdaniem powinno Cię zadowolić:

res = parseFloat(res.toPrecision(15));

które zaokrągli liczbę do 15 miejsca po przecinku (możesz wpisać w nawiasie mniejszą wartość jeśli błędy nadal się pojawiają lub jeśli po prostu nie potrzebujesz aż tak dokładnego rozwinięcia)

1 użytkowników online, w tym zalogowanych: 0, gości: 1