2009-02-26 11 views
9

Zastanawiałem się, czy istnieje sposób przezwyciężania problem dokładności, który wydaje się być wynikiem wewnętrznej reprezentacji mojego urządzenia liczb zmiennoprzecinkowych:Radzenie sobie z problemami dokładności w liczbach zmiennoprzecinkowych

Dla dobra klarowność problem jest streścić:

// str is "4.600"; atof(str) is 4.5999999999999996 
double mw = atof(str) 

// The variables used in the columns calculation below are: 
// 
//     mw = 4.5999999999999996 
//     p = 0.2 
//     g = 0.2 
//     h = 1 (integer) 

int columns = (int) ((mw - (h * 11 * p))/((h * 11 * p) + g)) + 1; 

przed odlewaniem do liczby całkowitej wpisz wynik kolumn obliczeń jest +1,9999999999999996; tak blisko, ale tak daleko od pożądanego wyniku 2.0.

Wszelkie sugestie najbardziej mile widziane.

+0

to pytanie zostało zadane i odpowiedzi przed ... po prostu patrząc na to ... –

+0

Czytaj na temat analizy numerycznej, jest to duży problem w niektórych sytuacjach. Może użyć alternatywnych (ale wolniejszych) bibliotek matematycznych, takich jak BigDecimal, itd ... – JeeBee

Odpowiedz

4

Bardzo prosty i skuteczny sposób, aby zaokrąglić liczbę zmiennoprzecinkową do liczby całkowitej:

int rounded = (int)(f + 0.5); 

Uwaga: To działa tylko jeśli f jest zawsze dodatnia. (dzięki j losowy haker)

+0

Założenie, że f jest dodatnie. –

+0

Tak "kolumny" są zawsze dodatnie w tej aplikacji. – AndyUK

+0

@j_random_hacker - w teorii można użyć wartości absolutnej. – Moshe

1

Stosować dziesiętne: decNumber++

+2

Czy to rozwiązuje problem 3 * (1/3)? Czy tylko problem 10 * (1/10)? – MSalters

+1

-1, z dokładnie tego powodu, który dał MSalters. Liczby dziesiętne przydają się do pracy z pieniędzmi nie dlatego, że mają wyższą precyzję, ale dlatego, że twoje nieprecyzyjne obliczenia będą identyczne jak wszystkie elses ". Pod wszystkimi innymi względami liczby dziesiętne cierpią z powodu tych samych problemów. –

+0

Chociaż istnieją pewne biblioteki, które przechowują ułamki. 4,6 byłoby 4 + 3/5 w jednym z nich. Rozpadają się tylko wtedy, gdy otrzymają operację niemożliwą do zarządzania jako ułamek, np. Pomnożenie przez pi. –

2

Można przeczytać ten paper znaleźć to, czego szukasz.

można uzyskać wartość bezwzględną wyniku widziany here:

x = 0.2; 
y = 0.3; 
equal = (Math.abs(x - y) < 0.000001) 
11

Jeśli nie masz go odczytać tytuł this paper jest rzeczywiście poprawna. Rozważ czytanie, aby dowiedzieć się więcej na temat podstaw arytmetyki zmiennoprzecinkowej na współczesnych komputerach, pułapek i wyjaśnień, dlaczego zachowują się tak, jak oni.

11

Nie ma problemu z dokładnością.

Otrzymany wynik (1,9999999999999996) różnił się od wyniku matematycznego (2) o margines 1E-16. To całkiem dokładne, biorąc pod uwagę wejście "4.600".

Oczywiście, masz problem z zaokrągleniem. Domyślne zaokrąglenie w C++ to obcięcie; chcesz coś podobnego do rozwiązania Kipa. Szczegóły zależą od Twojej domeny. Czy spodziewasz się round(-x)== - round(x)?

+0

+1 za zauważenie prawdziwego problemu. –

5

Jeśli dokładność jest naprawdę ważna, należy rozważyć użycie liczb zmiennoprzecinkowych podwójnej precyzji, a nie tylko zmiennoprzecinkowych. Chociaż z twojego pytania wynika, że ​​już jesteś. Jednak nadal masz problem ze sprawdzaniem konkretnych wartości. Musisz kod wzdłuż linii (przy założeniu, że sprawdzając swoją wartość przed zero):

if (abs(value) < epsilon) 
{ 
    // Do Stuff 
} 

gdzie „epsilon” jest trochę małe, ale non wartość zero.

+1

Myślę, że masz na myśli "abs (computed_value - expected_value)

+1

Rzeczywiście - ale wspomniałem, że kod był przykładem sprawdzania od zera;) – ChrisF

3

Na komputerach liczby zmiennoprzecinkowe nigdy nie są dokładne.Są one zawsze tylko przybliżeniem. (1e-16 jest blisko.)

Czasami są ukryte fragmenty, których nie widać. Czasami podstawowe zasady algebry już nie obowiązują: a * b! = B * a. Czasami porównanie rejestru z pamięcią pokazuje te subtelne różnice. Lub użycie koprocesora matematycznego względem biblioteki zmiennoprzecinkowej środowiska wykonawczego. (Robiłem to waayyy tooo długo.)

C99 określa: (Spójrz w math.h)

double round(double x); 
float roundf(float x); 
long double roundl(long double x); 

.

Albo można toczyć własne:

template<class TYPE> inline int ROUND(const TYPE & x) 
{ return int((x > 0) ? (x + 0.5) : (x - 0.5)); } 

Dla zmiennoprzecinkowej równoważności, spróbuj:

template<class TYPE> inline TYPE ABS(const TYPE & t) 
{ return t>=0 ? t : - t; } 

template<class TYPE> inline bool FLOAT_EQUIVALENT(
    const TYPE & x, const TYPE & y, const TYPE & epsilon) 
{ return ABS(x-y) < epsilon; } 
Powiązane problemy