Jak wydrukować podwójną wartość z pełną precyzją za pomocą cout?

331

Więc otrzymałem odpowiedź na moje ostatnie pytanie (nie wiem, dlaczego o tym nie pomyślałem). Drukowałem doubleużycie, coutktóre zaokrągliło się, gdy się nie spodziewałem. Jak mogę sprawić, by coutwydruk był w doublepełni precyzyjny?

Jason Punyon
źródło

Odpowiedzi:

390

Możesz ustawić precyzję bezpośrednio na std::couti użyć specyfikatora std::fixedformatu.

double d = 3.14159265358979;
cout.precision(17);
cout << "Pi: " << fixed << d << endl;

Możesz #include <limits>uzyskać maksymalną precyzję liczby zmiennoprzecinkowej lub podwójnej.

#include <limits>

typedef std::numeric_limits< double > dbl;

double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
Bill jaszczurka
źródło
46
Dlaczego wyraźnie zaleca się stosowanie fixed? Z double h = 6.62606957e-34;, fixeddaje mi 0.000000000000000i scientificwychodzi 6.626069570000000e-34.
Arthur
36
Precyzja musi wynosić 17 (lub std :: numeric_limits <double> :: digits10 + 2), ponieważ potrzebne są 2 dodatkowe cyfry podczas konwersji z dziesiętnego z powrotem na reprezentację binarną, aby zapewnić zaokrąglenie wartości do tej samej wartości początkowej. Oto artykuł z kilkoma szczegółami: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Mike Fisher
8
Czy naprawdę właściwa odpowiedź? Kiedy ręcznie używam dużej liczby, mogę wydrukować aż 51 cyfr przybliżonego e, ale z cout.precision(numeric_limits<double>::digits10 + 2);dostaję tylko 16 ....
Assimilater,
6
Dla tych, którzy wspominają 17 cyfr w artykule cytowanym przez MikeFishera, znajduje się pod Twierdzeniem 15.
Emile Cormier
15
@MikeFisher Masz rację, C ++ 11 wprowadzamax_digits10 to samo. Naprawiono odpowiedź, aby to odzwierciedlić.
legends2k
70

Posługiwać się std::setprecision :

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
Paul Beckingham
źródło
2
Czy istnieje jakieś makro lub wyliczenie MAX_PRECISION lub coś, co mogę przekazać do std :: setPrecision?
Jason Punyon
2
std :: setprecision (15) dla podwójnego (ok lub 16), log_10 (2 ** 53) ~ = 15,9
7116
14
std :: setprecision (std :: numeric_limits <double> :: digits10)
Éric Malenfant
6
Powinien być std::setprecision (17)podwójny, zobacz komentarze do odpowiedzi @Bill The Lizard.
Alec Jacobson,
9
aby std :: setprecision działał, należy dołączyć #include <iomanip>.
user2262504
24

Oto czego bym użył:

std::cout << std::setprecision (std::numeric_limits<double>::digits10 + 1)
          << 3.14159265358979
          << std::endl;

Zasadniczo pakiet limitów ma cechy wszystkich typów wbudowanych.
Jedną z cech liczb zmiennoprzecinkowych (liczba zmiennoprzecinkowa / podwójna / długa podwójna) jest atrybut digits10. To określa dokładność (nie pamiętam dokładnej terminologii) liczby zmiennoprzecinkowej w podstawie 10.

Zobacz: http://www.cplusplus.com/reference/std/limits/numeric_limits.html
Szczegółowe informacje na temat innych atrybutów.

Martin York
źródło
12
Ten nagłówek jest potrzebny do użycia std::setprecision(): #include <iomanip>
Martin Berger,
powinno być std::numeric_limits<double>zamiastnumberic_limits<double>
niklasfi
2
Dlaczego dodajesz 1do std::numeric_limits<double>::digits10?
Alessandro Jacopson
5
@LokiAstari Zamiast tego możesz użyć C + 11 max_digits10. Zobacz to .
legends2k
1
@AlecJacobson To raczej powinno być max_digits10, a nie dowolnedigits10+2 . W przeciwnym razie, w przypadku float, long double, boost::multiprecision::float128to nie uda, ponieważ należałoby +3zamiast +2.
Ruslan
14

Sposób iostreams jest trochę niezgrabny. Wolę używać, boost::lexical_castponieważ oblicza dla mnie odpowiednią precyzję. I to też jest szybkie .

#include <string>
#include <boost/lexical_cast.hpp>

using boost::lexical_cast;
using std::string;

double d = 3.14159265358979;
cout << "Pi: " << lexical_cast<string>(d) << endl;

Wynik:

Pi: 3,14159265358979

Timothy003
źródło
Dokumentacja doładowania mówi: „Dla liczb, które mają odpowiednią specjalizację std :: numeric_limits, obecna wersja wybiera teraz precyzję dopasowania”. To wydaje się być najprostszym sposobem na uzyskanie maksymalnej precyzji. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo
11

Przez pełną precyzję zakładam, że oznacza wystarczającą precyzję, aby pokazać najlepsze przybliżenie do zamierzonej wartości, ale należy zauważyć, że doublejest przechowywana przy użyciu reprezentacji podstawy 2, a podstawa 2 nie może reprezentować czegoś tak trywialnego jak 1.1dokładnie. Jedynym sposobem na uzyskanie pełnej precyzji rzeczywistego podwójnego (bez BŁĘDU WYŁĄCZENIA ZAOKRĄGLENIA) jest wydrukowanie bitów binarnych (lub hex nybbles). Jednym ze sposobów jest zapisanie wartości „ doublea”, uniona następnie wydrukowanie wartości całkowitej bitów.

union {
    double d;
    uint64_t u64;
} x;
x.d = 1.1;
std::cout << std::hex << x.u64;

To da ci 100% dokładną precyzję podwójnego ... i będzie całkowicie nieczytelny, ponieważ ludzie nie potrafią odczytać podwójnego formatu IEEE! Wikipedia dobrze napisała, jak interpretować binarne bity.

W nowszym C ++ możesz to zrobić

std::cout << std::hexfloat << 1.1;
Mark Lakata
źródło
10

Oto jak wyświetlić podwójnie z pełną precyzją:

double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::setprecision(precision) << d << std::endl;

Wyświetla to:

100,0000000000005


max_digits10 to liczba cyfr niezbędnych do jednoznacznego przedstawienia wszystkich wyraźnych podwójnych wartości. max_digits10 reprezentuje liczbę cyfr przed i po przecinku.


Nie używaj set_precision (max_digits10) ze std :: fixed.
W stałej notacji set_precision () ustawia liczbę cyfr tylko po przecinku. Jest to niepoprawne, ponieważ max_digits10 reprezentuje liczbę cyfr przed kropką dziesiętną i po niej.

double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::fixed << std::setprecision(precision) << d << std::endl;

Wyświetla niepoprawny wynik:

100,00000000000049738

Uwaga: Wymagane pliki nagłówka

#include <iomanip>
#include <limits>
Daniel Laügt
źródło
4
Dzieje się tak, ponieważ 100.0000000000005nie jest reprezentowane dokładnie jako double. (Może się wydawać, że tak powinno być, ale tak nie jest, ponieważ ulega normalizacji , tj. Reprezentacji binarnej). Aby to zobaczyć, spróbuj: 100.0000000000005 - 100. Dostajemy 4.973799150320701e-13.
Evgeni Sergeev,
9

Jak wydrukować doublewartość z pełną precyzją za pomocą cout?

Użyj hexfloatlub
użyj scientifici ustaw dokładność

std::cout.precision(std::numeric_limits<double>::max_digits10 - 1);
std::cout << std::scientific <<  1.0/7.0 << '\n';

// C++11 Typical output
1.4285714285714285e-01

Zbyt wiele odpowiedzi dotyczy tylko jednej z 1) bazy 2) stałego / naukowego układu lub 3) precyzji. Zbyt wiele precyzyjnych odpowiedzi nie zapewnia odpowiedniej wymaganej wartości. Stąd odpowiedź na stare pytanie.

  1. Jaka baza

A doublejest z pewnością zakodowane przy użyciu bazy 2. Bezpośrednim podejściem w C ++ 11 jest drukowanie przy użyciu std::hexfloat.
Jeśli wyjście nie dziesiętne jest dopuszczalne, jesteśmy skończeni.

std::cout << "hexfloat: " << std::hexfloat << exp (-100) << '\n';
std::cout << "hexfloat: " << std::hexfloat << exp (+100) << '\n';
// output
hexfloat: 0x1.a8c1f14e2af5dp-145
hexfloat: 0x1.3494a9b171bf5p+144

  1. W przeciwnym razie: fixedlub scientific?

A doublejest typem zmiennoprzecinkowym , a nie stałym .

Czy nie używać std::fixedjako że nie drukować małe doublejak bynajmniej 0.000...000. W przypadku dużych doubledrukuje wiele cyfr, być może setki wątpliwej informatywności.

std::cout << "std::fixed: " << std::fixed << exp (-100) << '\n';
std::cout << "std::fixed: " << std::fixed << exp (+100) << '\n';
// output
std::fixed: 0.000000
std::fixed: 26881171418161356094253400435962903554686976.000000 

Aby drukować z pełną precyzją, najpierw użyj, std::scientificktóry „zapisze wartości zmiennoprzecinkowe w notacji naukowej”. Zauważ, że domyślnie 6 cyfr po przecinku, niewystarczająca ilość, jest obsługiwane w następnym punkcie.

std::cout << "std::scientific: " << std::scientific << exp (-100) << '\n';  
std::cout << "std::scientific: " << std::scientific << exp (+100) << '\n';
// output
std::scientific: 3.720076e-44
std::scientific: 2.688117e+43

  1. Ile precyzji (ile cyfr ogółem)?

ZA doubleZakodowane przy użyciu bazowej binarnych 2 koduje sama precyzja pomiędzy różnymi uprawnieniami od 2. Jest to często 53 bitów.

[1,0 ... 2,0) są 2 53 różne double,
[2,0 ... 4,0) są 2 53 różne double,
[4,0 ... 8,0) są 2 53 różne double,
[8,0 ... 10,0) są 2 / 8 * 2 53 różnedouble .

Jednak jeśli kod jest drukowany w systemie dziesiętnym z Ncyframi znaczącymi, liczba kombinacji [1,0 ... 10,0) wynosi 9/10 * 10 N. .

Bez względu na Nwybraną (precyzję) doubletekst nie będzie mapowany jeden na jeden między tekstem dziesiętnym a dziesiętnym. Jeśli Nzostanie wybrana wartość stała , czasami będzie ona nieco bardziej lub mniej niż rzeczywiście potrzebna dla niektórych doublewartości. Możemy popełnić błąd na zbyt małej ( a)poniżej) lub zbyt dużej ( b)poniżej).

3 kandydatów N :

a) Użyj Nso podczas konwersji z doubletekstu-tekstu, aby uzyskać ten sam tekst dla wszystkich double.

std::cout << dbl::digits10 << '\n';
// Typical output
15

b) Użyj Nso podczas konwersji z double-tekstu - doublemy osiągamy to samo doubledla wszystkich double.

// C++11
std::cout << dbl::max_digits10 << '\n';
// Typical output
17

Gdy max_digits10nie jest dostępny, należy pamiętać, że ze względu na atrybuty base 2 i base 10 digits10 + 2 <= max_digits10 <= digits10 + 3możemy użyćdigits10 + 3 aby zapewnić wydrukowanie wystarczającej liczby cyfr dziesiętnych.

c) Użyj Nzmiennej zależnej od wartości.

Może to być przydatne, gdy kod chce wyświetlać minimalny tekst ( N == 1) lub dokładną wartość wartość a double( N == 1000-ishw przypadku denorm_min). Ponieważ jednak jest to „praca” i mało prawdopodobny cel PO, zostanie on odłożony na bok.


Zwykle jest to b) używane do „wydrukowania a double wartości z pełną precyzją”. Niektóre aplikacje mogą preferować: a) błąd przy niedostarczeniu zbyt dużej ilości informacji.

Z .scientific, .precision()zestawy liczba cyfr do wydrukowania po przecinku, więc 1 + .precision()cyfry są drukowane. Kod potrzebuje max_digits10więc cyfr całkowitych.precision() jest nazywany za pomocą max_digits10 - 1.

typedef std::numeric_limits< double > dbl;
std::cout.precision(dbl::max_digits10 - 1);
std::cout << std::scientific <<  exp (-100) << '\n';
std::cout << std::scientific <<  exp (+100) << '\n';
// Typical output
3.7200759760208361e-44
2.6881171418161356e+43
//1234567890123456  17 total digits

Podobne pytanie C.

chux - Przywróć Monikę
źródło
Świetna odpowiedź! Kilka uwag: masz rację, która precision()określa liczbę miejsc po przecinku dla trybu naukowego. Bez określania scientificokreśla całkowitą liczbę cyfr, z wyłączeniem wykładnika wykładniczego. Wciąż możesz uzyskać wyniki naukowe, w zależności od wartości liczbowej, ale wtedy możesz również otrzymać mniej cyfr niż podałeś. Przykład: cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"wyniki dla printfmogą być inne. Mylące rzeczy, o których należy pamiętać.
Simpleton
Dla potomności, oto wymagana długość bufora dla gwarantowanej dokładnej reprezentacji ciągu wszystkich podwójnych liczb w trybie naukowym za pomocą printf: char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);Dodatkowe znaki to: znak, kropka dziesiętna, końcowe zero, e [+ | -], 3 cyfry wykładnika ( DBL_MAX_10_EXP = 308). Stąd całkowita liczba wymaganych znaków wynosi 25.
Simpleton
Nie mogę edytować mojego pierwszego komentarza, więc zaczynamy jeszcze raz: Kolejnym problemem związanym z trybem naukowym jest to, że może zdecydować o niestosowaniu wyniku wykładniczego, a nawet może zdecydować o niestosowaniu wyniku zmiennoprzecinkowego. Oznacza to, że wyświetli 1.0 jako „1”, co może stanowić problem w kontekście serializacji / deserializacji. Możesz zmusić go do wypisania kropki dziesiętnej za pomocą „% #. * G”, ale ma to tę wadę, że dodaje liczbę końcowych zer, co nie jest pozbawione # ...
Simpleton
3
printf("%.12f", M_PI);

% .12f oznacza zmiennoprzecinkowy, z dokładnością do 12 cyfr.

Maister
źródło
11
To nie jest „używanie cout”.
Johnsyweb
2
12 cyfr nie jest „pełną precyzją”
Roland Illig
0

Najbardziej przenośny ...

#include <limits>

using std::numeric_limits;

    ...
    cout.precision(numeric_limits<double>::digits10 + 1);
    cout << d;

źródło
16
Jestem ciekawy: dlaczego „+1”?
Éric Malenfant,
0

Z ostream :: precyzja (int)

cout.precision( numeric_limits<double>::digits10 + 1);
cout << M_PI << ", " << M_E << endl;

ustąpi

3.141592653589793, 2.718281828459045

Dlaczego musisz powiedzieć „+1” Nie mam pojęcia, ale dodatkowa cyfra, którą z niej wyciągasz, jest poprawna.

Jann
źródło
3
numeric_limits <unsigned char> :: digits10 jest równe 2. Ponieważ może zawierać dowolną liczbę dziesiętną dwóch cyfr 0..99. Może również zawierać 255 .. ale nie 256, 257 ... 300 itd. Właśnie dlatego cyfry 10 to nie 3! Myślę, że „+1” zostało dodane, aby przezwyciężyć coś takiego.
Dmitriy Yurchenko
0

Spowoduje to wyświetlenie wartości do dwóch miejsc po przecinku po kropce.

#include <iostream>
#include <iomanip>

double d = 2.0;
int n = 2;
cout << fixed << setprecison(n) << d;

Zobacz tutaj: Notacja punktu stałego

std :: naprawiono

Użyj stałej notacji zmiennoprzecinkowej Ustawia flagę formatu floatfield dla strumienia str na naprawioną.

Gdy zmiennoprzecinkowe jest ustawione na stałe, wartości zmiennoprzecinkowe są zapisywane przy użyciu notacji stałoprzecinkowej: wartość jest reprezentowana za pomocą dokładnie tylu cyfr w części dziesiętnej, jak określono w polu dokładności (precyzja) i bez części wykładniczej.

std :: setprecision

Ustaw precyzję dziesiętną Ustawia precyzję dziesiętną używaną do formatowania wartości zmiennoprzecinkowych operacji wyjściowych.

Jeśli znasz standard IEEE do przedstawiania liczb zmiennoprzecinkowych, wiedziałbyś, że niemożliwe jest pokazanie zmiennoprzecinkowych z pełną precyzją poza zakresem normy , to znaczy zawsze spowoduje to zaokrąglenie wartości rzeczywistej.

Najpierw musisz sprawdzić, czy wartość mieści się w zakresie , jeśli tak, to użyj:

cout << defaultfloat << d ;

std :: defaultfloat

Użyj domyślnej notacji zmiennoprzecinkowej Ustawia flagę formatu floatfield dla strumienia str na defaultfloat.

Gdy zmiennoprzecinkowe jest ustawione na defaultfloat, wartości zmiennoprzecinkowe są zapisywane przy użyciu domyślnej notacji: reprezentacja używa tyle znaczących cyfr, ile potrzeba, aż do precyzji dziesiętnej (precyzji) strumienia, licząc obie cyfry przed i po przecinku (jeśli występują) ).

Jest to również domyślne zachowanie cout, co oznacza, że ​​nie używasz go jawnie.

emmmphd
źródło