Полная точность отображения чисел с плавающей точкой в ​​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 точности не обязательно одно и то же.

Другие вопросы по тегам