In mijn vroegere vraagwas ik een double
met behulp van cout
die werd afgerond toen ik het niet verwachtte. Hoe kan ik cout
een double
laten afdrukken met volledige precisie?
Antwoord 1, autoriteit 100%
Je kunt de precisie rechtstreeks instellen op std::cout
en de std::fixed
formaatspecificatie.
double d = 3.14159265358979;
cout.precision(17);
cout << "Pi: " << fixed << d << endl;
Je kunt #include <limits>
gebruiken om de maximale precisie van een float of double te krijgen.
#include <limits>
typedef std::numeric_limits< double > dbl;
double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
Antwoord 2, autoriteit 18%
Gebruik std::setprecision
:
#include <iomanip>
std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
Antwoord 3, autoriteit 6%
Dit is wat ik zou gebruiken:
std::cout << std::setprecision (std::numeric_limits<double>::digits10 + 1)
<< 3.14159265358979
<< std::endl;
In principe heeft het limietenpakket eigenschappen voor alle ingebouwde typen.
Een van de eigenschappen voor getallen met drijvende komma (float/double/long double) is het digits10-attribuut. Dit definieert de nauwkeurigheid (ik ben de exacte terminologie vergeten) van een getal met drijvende komma in grondtal 10.
Zie: http://www.cplusplus.com/reference/std/ limieten/numeric_limits.html
Voor details over andere kenmerken.
Antwoord 4, autoriteit 3%
De manier van iostreams is nogal onhandig. Ik gebruik liever boost::lexical_cast
omdat het berekent voor mij de juiste precisie. En het is ook snel.
#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;
Uitvoer:
Pi: 3.14159265358979
Antwoord 5, autoriteit 2%
Hier leest u hoe u een dubbel met volledige precisie weergeeft:
double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::setprecision(precision) << d << std::endl;
Dit toont:
100.0000000000005
max_digits10 is het aantal cijfers dat nodig is om alle afzonderlijke dubbele waarden op unieke wijze weer te geven. max_digits10 staat voor het aantal cijfers voor en achter de komma.
Gebruik set_precision(max_digits10) niet met std::fixed.
Bij vaste notatie stelt set_precision() het aantal cijfers alleen nade komma in. Dit is onjuist omdat max_digits10 staat voor het aantal cijfers vooren nade komma.
double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::fixed << std::setprecision(precision) << d << std::endl;
Dit geeft een onjuist resultaat weer:
100.00000000000049738
Opmerking: headerbestanden vereist
#include <iomanip>
#include <limits>
6, Autoriteit 2%
In C++ 20 kun je std::format
om dit te doen:
std::cout << std::format("{}", M_PI);
Uitvoer (uitgaande van IEEE754 double
):
3.141592653589793
De standaard drijvende-kommanotatie is de kortste decimale weergave met een retourgarantie. Het voordeel van deze methode ten opzichte van de setprecision
I/O-manipulator is dat er geen onnodige cijfers worden afgedrukt.
In de tussentijd kun je de {fmt} bibliotheek, std::format
is gebaseerd op. {fmt} biedt ook de functie print
die dit nog eenvoudiger en efficiënter maakt (godbolt):
fmt::print("{}", M_PI);
Disclaimer: ik ben de auteur van {fmt} en C++20 std::format
.
Antwoord 7, autoriteit 2%
Met volledige precisie veronderstel ik voldoende precisie om de beste benadering van de beoogde waarde te tonen, maar er moet op worden gewezen dat double
wordt opgeslagen met basis 2 representatie en basis 2 niet kan vertegenwoordigen zoiets triviaals als 1.1
precies. De enige manier om de volledige-precisie van de daadwerkelijke dubbele (zonder AFRONDENFOUT) te krijgen, is door de binaire bits (of hex nybbles) af te drukken.
Een manier om dat te doen is door een union
te gebruiken om de double
in te typen tot een geheel getal en vervolgens het gehele getal af te drukken, aangezien gehele getallen niet onderhevig zijn aan truncatie of afronding -uit problemen. (Dit soort woordspelingen wordt niet ondersteund door de C++-standaard, maar wel in C. De meeste C++-compilers zullen echter waarschijnlijk toch de juiste waarde afdrukken. Ik denk dat g++ dit ondersteunt.)
union {
double d;
uint64_t u64;
} x;
x.d = 1.1;
std::cout << std::hex << x.u64;
Dit geeft je de 100% nauwkeurige precisie van de dubbele… en is volkomen onleesbaar omdat mensen het IEEE-dubbelformaat niet kunnen lezen! Wikipediaheeft een goede beschrijving van hoe de binaire bits moeten worden geïnterpreteerd.
In nieuwere C++ kunt u dat doen
std::cout << std::hexfloat << 1.1;
Antwoord 8
IEEE 754drijvende-kommawaarden worden opgeslagen met basis 2-representatie. Elk getal met grondtal 2 kan met volledige precisie worden weergegeven als een decimaal (grondtal 10). Geen van de voorgestelde antwoorden echter. Ze allekappen de decimale waarde af.
Dit lijkt te wijten te zijn aan een verkeerde interpretatie van wat std::numeric_limits<T>::max_digits10
staat voor:
De waarde van
std::numeric_limits<T>::max_digits10
is het aantal grondtal-10 cijfers dat nodig is om alle afzonderlijke waarden van het typeT
.
Met andere woorden: het is het (in het slechtste geval) aantal cijfers dat nodig is om uit te voeren als u van binair naar decimaal naar binair wilt gaan, zonder enige informatie te verliezen. Als u ten minste max_digits10
decimalen uitvoert en een drijvende-kommawaarde reconstrueert, krijgt u gegarandeerd exact dezelfde binaire weergave waarmee u begon.
Wat belangrijk is: max_digits10
levert in het algemeen niet de kortste decimaal op, en het is ook niet voldoende om de volledige precisie weer te geven. Ik ben niet op de hoogte van een constante in de C++ Standard Library die het maximale aantal decimale cijfers codeert dat nodig is om de volledige precisie van een drijvende-kommawaarde te bevatten. Ik geloof dat het ongeveer 767 is voor double
s1. Een manier om een drijvende-kommawaarde met volledige precisie uit te voeren, is door een voldoende grote waarde voor de precisie te gebruiken, zoals zo2, en laat de bibliotheek alle nullen verwijderen:
#include <iostream>
int main() {
double d = 0.1;
std::cout.precision(767);
std::cout << "d = " << d << std::endl;
}
Dit levert de volgende uitvoer op, die de volledige precisie bevat:
d = 0.1000000000000000055511151231257827021181583404541015625
Merk op dat dit aanzienlijk meer decimalen heeft dan max_digits10
zou suggereren.
Terwijl dat de vraag beantwoordt die werd gevraagd, zou een veel meer gemeenschappelijk doel zijn om het kortste decimale weergave van een bepaalde drijvende puntwaarde te krijgen, die alle informatie behoudt. Nogmaals, ik ben niet op de hoogte van een manier om de standaard I / O-bibliotheek te instrueren om die waarde uit te voeren. Beginnend met C++ 17 De mogelijkheid om die conversie te doen is eindelijk aangekomen in C++ in de vorm van std::to_chars
. Standaard produceert het de kortste decimale weergave van een bepaalde drijvende puntwaarde die de volledige informatie behoudt.
De interface is een beetje clunky en u wilt waarschijnlijk dit in een functiesjabloon wikkelen die iets retourneert dat u kunt uitvoeren naar std::cout
(zoals een std::string
), bijv.
#include <charconv>
#include <array>
#include <string>
#include <system_error>
#include <iostream>
#include <cmath>
template<typename T>
std::string to_string(T value)
{
// 24 characters is the longest decimal representation of any double value
std::array<char, 24> buffer {};
auto const res { std::to_chars(buffer.data(), buffer.data() + buffer.size(), value) };
if (res.ec == std::errc {})
{
// Success
return std::string(buffer.data(), res.ptr);
}
// Error
return { "FAILED!" };
}
int main()
{
auto value { 0.1f };
std::cout << to_string(value) << std::endl;
value = std::nextafter(value, INFINITY);
std::cout << to_string(value) << std::endl;
value = std::nextafter(value, INFINITY);
std::cout << to_string(value) << std::endl;
}
Dit zou worden afgedrukt (met behulp van Microsoft’s C++ Standard Library):
0.1
0.10000001
0.10000002
1Uit de CppCon 2019-lezing van Stephan T. Lavavej getiteld Floating -Punt <charconv>: uw code 10x sneller maken met de eindbaas van C++17. (De hele talk is het bekijken waard.)
2Hiervoor zou ook een combinatie van scientific
en fixed
nodig zijn, welke van beide korter is. Ik ben niet op de hoogte van een manier om deze modus in te stellen met behulp van de C++ Standard I/O-bibliotheek.
Antwoord 9
C++20 std::format
Deze geweldige nieuwe C++-bibliotheekfunctie heeft het voordeel dat het de status van std::cout
niet beïnvloedt zoals std::setprecision
doet:
#include <format>
#include <string>
int main() {
std::cout << std::format("{:.2} {:.3}\n", 3.1415, 3.1415);
}
Verwachte uitvoer:
3.14 3.145
Zoals vermeld op https://stackoverflow.com/a/65329803/895245als u de precisie drukt het expliciet de kortste decimale weergave af met een retourgarantie. TODO begrijpt in meer detail hoe het zich verhoudt tot: dbl::max_digits10
zoals getoond op https://stackoverflow. com/a/554134/895245met {:.{}}
:
#include <format>
#include <limits>
#include <string>
int main() {
std::cout << std::format("{:.{}}\n",
3.1415926535897932384626433, dbl::max_digits10);
}
Zie ook:
- Standaard floating point-printprecisie terugzetten in C++voor het herstellen van de initiële precisie in pre-c++20
- std::string-opmaak zoals sprintf
- https://en.cppreference.com/w/cpp /utility/format/formatter#Standard_format_specification
Antwoord 10
printf("%.12f", M_PI);
%.12f betekent drijvende komma, met een precisie van 12 cijfers.
Antwoord 11
Meeste draagbaar…
#include <limits>
using std::numeric_limits;
...
cout.precision(numeric_limits<double>::digits10 + 1);
cout << d;
Antwoord 12
In deze vraagstaat een beschrijving hoe je een double zonder verlies omzet in string (in Octave, maar het kan gemakkelijk te reproduceren in C++). Het idee is om een korte, voor mensen leesbare beschrijving van de vlotter te hebben en een verliesloze beschrijving in hexa-vorm, bijvoorbeeld: pi -> 3.14{54442d18400921fb}.
Antwoord 13
Hier is een functie die werkt voor elk type met drijvende komma, niet alleen double
, en de stream ook terugzet zoals deze later werd gevonden. Helaas zal het niet goed communiceren met threads, maar dat is de aard van iostreams. U hebt deze nodig aan het begin van uw bestand:
#include <limits>
#include <iostream>
Hier is de functie, je zou hem in een header-bestand kunnen zetten als je hem veel gebruikt:
template <class T>
void printVal(std::ostream& os, T val)
{
auto oldFlags = os.flags();
auto oldPrecision = os.precision();
os.flags(oldFlags & ~std::ios_base::floatfield);
os.precision(std::numeric_limits<T>::digits10);
os << val;
os.flags(oldFlags);
os.precision(oldPrecision);
}
Gebruik het als volgt:
double d = foo();
float f = bar();
printVal(std::cout, d);
printVal(std::cout, f);
Als u de normale invoegoperator <<
wilt gebruiken, kunt u deze extra wrappercode gebruiken:
template <class T>
struct PrintValWrapper { T val; };
template <class T>
std::ostream& operator<<(std::ostream& os, PrintValWrapper<T> pvw) {
printVal(os, pvw.val);
return os;
}
template <class T>
PrintValWrapper<T> printIt(T val) {
return PrintValWrapper<T>{val};
}
Nu kun je het als volgt gebruiken:
double d = foo();
float f = bar();
std::cout << "The values are: " << printIt(d) << ", " << printIt(f) << '\n';
14
Hiermee wordt de waarde weergegeven tot twee decimalen na de punt.
#include <iostream>
#include <iomanip>
double d = 2.0;
int n = 2;
cout << fixed << setprecision(n) << d;
Zie hier: opgelost -point notatie
Gebruik vaste drijvende notatie Stelt de vlag van de floatfield-indeling voor
de str-stroom opgelost.Wanneer floatfield op vaste, zijn drijvende-puntwaarden geschreven
met behulp van een notatie van vaste punt: de waarde is vertegenwoordigd met precies zoals
Veel cijfers in het decimale deel zoals gespecificeerd door het precisieveld
(precisie) en zonder exponent onderdeel.
Set Decimal Precision Stelt de decimale precisie in die moet worden gebruikt om te formatteren
drijvende-puntwaarden op uitvoeroperaties.
Als u bekend bent met de IEEE-standaard voor het weergeven van de drijvende punten, zou u weten dat het onmogelijk is om drijvende punten te tonen met volledige precisie uit de reikwijdte van de standaard , Dat wil zeggen, het zal altijd resulteren in een afronding van de echte waarde.
U moet eerst controleren of de waarde binnen de scope , indien ja, gebruikt:
cout << defaultfloat << d ;
Gebruik standaard drijvende notatie Stelt de vlag van het Floatfield-formaat in
voor de str-stroom naar defaultFloat.Wanneer floatfield is ingesteld op de standaardfloat, zijn drijvende-puntwaarden
Geschreven met behulp van de standaard notatie: de weergave gebruikt zoveel mogelijk
zinvolle cijfers zoals nodig is tot de decimale precisie van de stroom
(Precisie), zowel de cijfers voor als na de decimale tellen
punt (indien aanwezig).
Dat is ook het standaardgedrag van cout
, wat betekent dat u het niet expliciet gebruikt.
15
Met Ostream :: Precision (INT)
cout.precision( numeric_limits<double>::digits10 + 1);
cout << M_PI << ", " << M_E << endl;
levert
3.141592653589793, 2.718281828459045
Waarom u “+1” moet zeggen, ik heb geen aanwijzing, maar het extra cijfer dat u eruit haalt, is correct.