Полная точность отображения чисел с плавающей точкой в C++?
Я прочитал несколько тем об отображении чисел с плавающей запятой в C++ и не смог найти удовлетворительного ответа.
Мой вопрос: как отобразить все значащие цифры чисел с плавающей запятой в C++ в научном формате (мантисса / экспонента)?
Проблема в том, что все числа не имеют одинакового количества значащих цифр в базе 10.
Например, double
имеет точность от 15 до 17 значащих десятичных цифр, но std::numeric_limits<double>::digits10
возвращает 15 и, следовательно, для некоторых чисел я потеряю 2 дополнительные десятичные цифры точности.
3 ответа
Значение std::numeric_limits<double>::digits10
предоставляет количество десятичных цифр, которые могут быть безопасно восстановлены, т. е. количество десятичных цифр, которые выживают при прохождении туда и обратно ->double
-> десятичное. Предполагать, что больше десятичных цифр правильно, не полезно. Если вы хотите организовать поездку туда и обратно double
->decimal->double
вы бы использовали std::numeric_limits<double>::max_digits10
,
Если вы хотите, чтобы точные значения вы использовали std::numeric_limits<double>::digits
но он будет отображать числа, преобразованные из десятичных значений в забавной форме, поскольку они обычно являются округленными значениями. Это также причина, почему max_digits10
бесполезно при представлении цифр для потребления человеком: последние несколько цифр обычно не соответствуют ожиданиям читателя.
В C++20 вы сможете использовать сделать это:
std::cout << std::format("{}", M_PI);
Выход (при условии, что IEEE754
double
):
3.141592653589793
Формат с плавающей запятой по умолчанию - это кратчайшее десятичное представление с гарантией приема-передачи. Преимущество этого метода по сравнению с использованием точности
max_digits10
из
std::numeric_limits
в том, что он не печатает ненужные цифры. Например:
std::cout << std::setprecision(
std::numeric_limits<double>::max_digits10) << 0.3;
отпечатки
0.29999999999999999
пока
std::cout << std::format("{}", 0.3);
отпечатки
0.3
( Godbolt ).
А пока вы можете использовать библиотеку {fmt} , основанную на. {fmt} также предоставляет
print
функция, которая делает это еще проще и эффективнее ( Godbolt ):
fmt::print("{}", M_PI);
Отказ от ответственности : я являюсь автором {fmt} и C++20
std::format
.
Вы смотрели на std::max_digits10
?
Из контекста:
Значение
std::numeric_limits<T>::max_digits10
это число из 10 цифр, которые необходимы для уникального представления всех различных значений типаT
, такие как необходимые для сериализации / десериализации в текст. Эта константа имеет смысл для всех типов с плавающей точкой.
Смысл этого (и как я его использую) заключается в том, что вывод текста может быть скопирован / вставлен в другую программу, и число будет представлять то же число.
Теперь я должен сказать, что мой формат рабочей лошади всегда оправдан %23.16E
и я использую инженерное суждение для последних нескольких цифр. Мне это нравится, потому что достаточно для знака, экспоненты и шестнадцати цифр.
-----------------------
-1.1234567812345678E+12
Теперь обратите внимание, что digits
точности и decimal digits
точности не обязательно одно и то же.