2009-06-15 15 views
9

Używam następujący kod do zaokrąglania do 2DP:Zaokrąglanie podwaja - 0,5 - sprintf

sprintf(temp,"%.2f",coef[i]); //coef[i] returns a double 

powodzeniem uzupełnia 6,666 do 6,67, ale to nie działa prawidłowo podczas omijania 5.555. Zwraca 5,55, podczas gdy powinno (przynajmniej moim zdaniem) zwrócić 5.56.

Jak mogę je zaokrąglić, gdy następna cyfra to 5? tj. powrót 5.56.

edytuj: Teraz zdaję sobie sprawę, że tak się dzieje, ponieważ po wpisaniu 5.555 przez cin otrzymuje się zapisany jako 5.554999997.

Mam zamiar spróbować zaokrąglić w dwóch etapach - najpierw do 3dp, a następnie do 2dp. jakiekolwiek inne (bardziej eleganckie) pomysły?

Odpowiedz

11

Wygląda na to, że do prawidłowego zaokrąglenia trzeba użyć funkcji matematycznej.

printf("%.2f %.2f\n", 5.555, round(5.555 * 100.)/100.); 

To daje następujące dane wyjściowe na moim komputerze:

5.55 5.56 
+0

To wydaje się nieco bardziej niezawodny niż idei dodanie 0,0005, ale oba będą pracować tak myślę. Dzięki za pomoc! – Meir

+0

Wygląda na to, że to błąd. 5.5551 zostanie poprawnie zaokrąglone! – ypnos

+6

To nie jest buggy, @ypnos, po prostu nie pogubiłeś się jeszcze w granicach zmiennoprzecinkowych. 5.5551 jest reprezentowany jako 5.555100000000000370 ... który zaokrągli w górę, 5.555 zostanie odrzucony jako 5.5549999999 ... który zaokrągli w dół. Możesz zobaczyć, że jest to problem reprezentacji, próbując kodu z 5.5500000000000000001 - oba wyjścia są nieprawidłowe w tym przypadku 5.55, ponieważ numer NIE JEST 5.55000 ... 1, gdy jest przechowywany. To 5.549999 ... – paxdiablo

11

Liczba 5.555 nie można przedstawić dokładnej liczby w IEEE 754. Drukowanie ciągłe 5.555 z "%.50f" wyniki w:

5.55499999999999971578290569595992565155029300000000 

więc będzie zaokrągla się w dół. Spróbuj użyć zamiast tego:

printf ("%.2f\n",x+0.0005); 

chociaż trzeba uważać z liczb, które może być reprezentowane dokładnie, ponieważ zostaną one zaokrąglane w górę błędnie przez tego wyrażenia.

Musisz zrozumieć ograniczenia reprezentacji zmiennoprzecinkowej. Jeśli jest ważne, aby uzyskać dokładność, możesz użyć (lub zakodować) BCD lub innej klasy dziesiętnej, która nie ma wadę reprezentacji IEEE754.

+0

Nie ma to wpływu na twoją odpowiedź, ale byłem po prostu ciekawy: jakiego języka używasz do drukowania 5.55499999999999971578290569595992565155029300000000? Powinno to być 5,55499999999999971578290569595992565155029296875000 –

+0

@Rick, najprawdopodobniej gcc pod CygWinem, ale jest to trudne do zapamiętania rok temu :-) Mam taki sam wynik jak ty w systemie Ubuntu10. Sądzę jednak, że podwójne liczby gwarantują tylko 15 dziesiętnych cyfr dokładności, więc może to być użycie szerszego formatu wewnętrznego (mam niejasne wspomnienie, że obliczenia na co najmniej jednym systemie używały 80 bitów wewnątrz, ale to było dawno temu - aby uzyskać 48 cyfry dziesiętne, jak w przypadku liczby, potrzeba około 160 bitów precyzji). – paxdiablo

+0

OK, dzięki. Wiedziałem, że nie może to być Visual C++ (nie można wydrukować dokładnie tych cyfr) i wydawało się, że ma zbyt mało cyfr dla gcc/glibc w systemie Linux (co pozwala na wydrukowanie ich wszystkich). W każdym razie można uzyskać tyle cyfr za pomocą standardowego podwójnego (53-bitowego) - jest to dokładna dziesiętna reprezentacja tego, co podwójnie się trzyma (nawet jeśli może to być tylko 15-cyfrowa aproksymacja tego, co zostało mu przypisane). –

-2

Można też to zrobić (oszczędność mnożenie/dzielenie):

printf("%.2f\n", coef[i] + 0.00049999); 
+0

To obejmie 5.549999 do 5.56 ;-) – ypnos

+0

@pnos, naprawdę musisz sprawdzić rzeczy zanim opublikujesz - to wcale nie daje ci 5.56. – paxdiablo

+1

Przepraszam za nieporozumienie. Powinieneś nadal widzieć mój punkt widzenia. 5 544 999 + 0,0005 zaokrąglono do 5,55. Należy go wyraźnie zaokrąglić do 5.54. Nie przyjmuje nauki o rakietach, aby uświadomić sobie, że dodanie uprzedzeń nie naprawi misternie problemu zaokrąglania bez wprowadzania nowych. – ypnos

1

To pytanie jest oznaczone C++, więc będę postępować zgodnie z tym założeniem. Zauważ, że strumienie C++ zaokrąglą się, w przeciwieństwie do rodziny printf C. Wszystko, co musisz zrobić, to zapewnić precyzję, którą chcesz i biblioteka strumieni będzie za Ciebie. Po prostu wyrzucam to na wypadek, gdybyś nie miał powodu, by nie używać strumieni.

2

Jak na ten temat dla innego możliwego rozwiązania:

printf("%.2f", _nextafter(n, n*2)); 

Chodzi o to, aby zwiększyć liczbę od zera (n * 2 otrzymuje znak po prawej) przez najmniejszą możliwą ilość reprezentowana przez zmiennoprzecinkowych matematyki.

Np

double n=5.555; 
printf("%.2f\n", n); 
printf("%.2f\n", _nextafter(n, n*2)); 
printf("%.20f\n", n); 
printf("%.20f\n", _nextafter(n, n*2)); 

Z wydajnością msvc:

5.55 
5.56 
5.55499999999999970000 
5.55500000000000060000