Как вывести двойное значение с полной точностью, используя cout?

332

Итак, я получил ответ на мой последний вопрос (я не знаю, почему я не подумал об этом). Я печатал doubleиспользование, coutкоторое округлилось, когда я не ожидал этого. Как я могу сделать coutпечать с doubleиспользованием полной точности?

Джейсон Пуньон
источник

Ответы:

391

Вы можете установить точность непосредственно std::coutи использовать std::fixedспецификатор формата.

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

Вы можете #include <limits>получить максимальную точность поплавка или удвоения.

#include <limits>

typedef std::numeric_limits< double > dbl;

double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
Билл Ящерица
источник
46
Почему вы явно советуете использовать fixed? С double h = 6.62606957e-34;, fixedдает мне 0.000000000000000и scientificвыводы 6.626069570000000e-34.
Артур
36
Точность должна быть 17 (или std :: numeric_limits <double> :: digits10 + 2), поскольку при преобразовании из десятичного значения обратно в двоичное представление требуется 2 дополнительных цифры, чтобы обеспечить округление значения до того же исходного значения. Вот документ с некоторыми подробностями: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Майк Фишер,
8
Это действительно правильный ответ? Когда я вручную использую большое число, я могу распечатать до 51 цифры приблизительного значения e, но при этом cout.precision(numeric_limits<double>::digits10 + 2);получаю только 16 ....
Assimilater
6
Для тех, кто ищет, где упоминается 17 цифр в цитируемой статье @MikeFisher, это согласно теореме 15.
Эмиль Кормиер
15
@MikeFisher Вы правы, C ++ 11 вводит,max_digits10 чтобы обозначить то же самое. Исправлен ответ, чтобы отразить это.
legends2k
70

Используйте std::setprecision:

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
Пол Бекингем
источник
2
Есть ли какой-нибудь макрос или перечисление MAX_PRECISION или что-то, что я могу передать в std :: setPrecision?
Джейсон Пуньон
2
std :: setprecision (15) для двойного (нормально или 16), log_10 (2 ** 53) ~ = 15,9
user7116
14
std :: setprecision (std :: numeric_limits <double> :: digits10)
Эрик Маленфант
6
Должен быть std::setprecision (17)удвоен, см. Комментарии к ответу @Bill The Lizard.
Алек Якобсон
9
для работы std :: setprecision необходимо включить #include <iomanip>.
user2262504
24

Вот что я бы использовал:

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

По сути, пакет limit имеет черты для всех встроенных типов.
Одной из особенностей чисел с плавающей запятой (float / double / long double) является атрибут digits10. Это определяет точность (я забыл точную терминологию) числа с плавающей запятой в базе 10.

См. Http://www.cplusplus.com/reference/std/limits/numeric_limits.html
Подробнее о других атрибутах.

Мартин Йорк
источник
12
Этот заголовок необходим для использования std::setprecision(): #include <iomanip>
Martin Berger
это должно быть std::numeric_limits<double>вместоnumberic_limits<double>
niklasfi
2
Почему вы добавляете 1в std::numeric_limits<double>::digits10?
Алессандро Якопсон
5
@LokiAstari Вы можете использовать max_digits10вместо C + 11 . Смотрите это .
legends2k
1
@AlecJacobson Скорее max_digits10, а не произвольно digits10+2. В противном случае, в случае float, long double, boost::multiprecision::float128это не получится , так как вы должны были бы +3вместо +2.
Руслан
14

Путь iostreams довольно неуклюж. Я предпочитаю использовать, boost::lexical_castпотому что он рассчитывает правильную точность для меня. И это тоже быстро .

#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;

Вывод:

Pi: 3.14159265358979

Timothy003
источник
В расширенной документации говорится: «Для чисел, имеющих соответствующую специализацию std :: numeric_limits, текущая версия теперь выбирает точность для соответствия». Это кажется самым простым способом получить максимальную точность. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo
11

Под полной точностью я предполагаю достаточную точность, чтобы показать наилучшее приближение к предполагаемому значению, но следует указать, что doubleоно хранится с использованием представления базы 2, а база 2 не может представлять что-то столь же тривиальное, как 1.1точное. Единственный способ получить полную-полную точность фактического двойного значения (без NO ROUND OFF ERROR) - это распечатать двоичные биты (или шестнадцатеричные биты). Один из способов сделать это - записать в doublea, unionа затем распечатать целочисленное значение битов.

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

Это даст вам 100% точность двойного ... и будет совершенно нечитаемым, потому что люди не могут читать двойной формат IEEE! В Википедии есть хорошая статья о том, как интерпретировать двоичные биты.

В более новом C ++ вы можете сделать

std::cout << std::hexfloat << 1.1;
Марк Лаката
источник
10

Вот как отобразить двойной с полной точностью:

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

Это отображает:

+100,0000000000005


max_digits10 - количество цифр, необходимое для уникального представления всех различных двойных значений. max_digits10 представляет количество цифр до и после десятичной точки.


Не используйте set_precision (max_digits10) с std :: fixed.
В фиксированной записи set_precision () устанавливает количество цифр только после десятичной точки. Это неверно, так как max_digits10 представляет количество цифр до и после десятичной точки.

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

Это отображает неверный результат:

+100,00000000000049738

Примечание. Требуются заголовочные файлы.

#include <iomanip>
#include <limits>
Даниэль Лёгт
источник
4
Это происходит потому, что 100.0000000000005не представляется точно как double. (Может показаться, что так и должно быть, но это не так, потому что он нормализуется , т.е. его двоичное представление). Чтобы убедиться в этом, попробуйте: 100.0000000000005 - 100. Мы получаем 4.973799150320701e-13.
Евгений Сергеев
9

Как напечатать doubleзначение с полной точностью, используя cout?

Используйте hexfloatили
используйте scientificи установите точность

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

Слишком много ответов касаются только одного из 1) основ 2) фиксированной / научной схемы или 3) точности. Слишком много ответов с точностью не дают нужного значения. Отсюда и ответ на старый вопрос.

  1. Какая база?

A double, безусловно, кодируется с использованием базы 2. Прямой подход к C ++ 11 заключается в печати с использованием std::hexfloat.
Если не десятичный вывод приемлем, мы закончили.

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. В противном случае: fixedили scientific?

A double- это тип с плавающей запятой , а не с фиксированной запятой .

Как не использовать , std::fixedкак это не удается напечатать маленький , doubleкак угодно , но 0.000...000. В целом doubleон печатает много цифр, возможно, сотни сомнительной информативности.

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 

Для печати с полной точностью сначала используйте команду, std::scientificкоторая будет «записывать значения с плавающей запятой в научной нотации». Обратите внимание, что по умолчанию 6 цифр после десятичной точки, недостаточное количество, обрабатывается в следующей точке.

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. Сколько точности (сколько всего цифр)?

doubleКодируется с использованием двоичного основанием 2 кодируют такую же точность между различными полномочиями 2. Это часто 53 бит.

[1.0 ... 2.0) есть 2 53 разных double,
[2.0 ... 4.0) есть 2 53 разных double,
[4.0 ... 8.0) есть 2 53 разных double,
[8.0 ... 10.0) есть 2 / 8 * 2 53 разных double.

Тем не менее , если код печатает в десятичном с Nзначащими цифрами, число комбинаций [1,0 ... 10,0) составляет 9/10 * 10 N .

Независимо от того, какой N(точность) выбран, не будет взаимно-однозначного сопоставления между doubleдесятичным текстом. Если Nвыбрано фиксированное значение , иногда оно будет немного больше или меньше, чем необходимо для определенных doubleзначений. Мы могли бы ошибиться на слишком мало ( a)ниже) или слишком много ( b)ниже).

3 кандидата N:

а) Используйте Nтак при преобразовании из doubleтекстового текста мы получаем один и тот же текст для всех double.

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

б) Используйте Nтак при преобразовании из double-text-, doubleмы получаем то же самое doubleдля всех double.

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

Когда max_digits10недоступно, обратите внимание, что из-за атрибутов base 2 и base 10 digits10 + 2 <= max_digits10 <= digits10 + 3, мы можем использовать, digits10 + 3чтобы обеспечить печать достаточного количества десятичных цифр.

c) Используйте значение, Nкоторое зависит от значения.

Это может быть полезно, когда код хочет отобразить минимальный текст ( N == 1) или точное значение double( N == 1000-ishв случае denorm_min). Тем не менее, поскольку это «работа» и маловероятная цель ОП, она будет отложена.


Обычно б) используется для «печати doubleзначения с полной точностью». Некоторые приложения могут предпочесть а) ошибку при не слишком большом количестве информации.

С .scientific, .precision()устанавливает количество цифр для печати после десятичной точки, поэтому 1 + .precision()цифры печатаются. Коду нужно max_digits10общее количество цифр, поэтому .precision()он называется с 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

Подобный C вопрос

Chux - Восстановить Монику
источник
Отличный ответ! Несколько замечаний: вы правы, precision()устанавливая количество знаков после запятой для научного режима. Без указания scientificон устанавливает общее количество цифр, исключая показатель степени. Вы все равно можете получить научный вывод, в зависимости от вашего числового значения, но тогда вы также можете получить меньше цифр, чем вы указали. Пример: cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"результаты printfмогут быть разными. Непонятные вещи, которые следует знать.
Простак
Для потомков вот необходимая длина буфера для гарантированного точного строкового представления всех двойных чисел в научном режиме с использованием printf: char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);дополнительные символы для: знак, десятичная точка, завершающий ноль, e [+ | -], 3 цифры для показателя степени ( DBL_MAX_10_EXP = 308). Следовательно, общее количество необходимых символов - 25.
Simpleton
Не могу отредактировать мой первый комментарий, поэтому здесь мы снова идем: Другая проблема с научным режимом заключается в том, что он может решить не использовать экспоненциальный вывод, он даже может решить вообще не использовать вывод с плавающей запятой. То есть он будет выводить 1.0 как «1», что может быть проблемой в контексте сериализации / десериализации. Вы можете заставить его выводить десятичную точку, используя «% #. * G», но у этого недостатка есть то, что он добавляет число конечных нулей, чего не бывает без # ...
Simpleton
3
printf("%.12f", M_PI);

% .12f означает с плавающей запятой с точностью до 12 цифр.

Майстер
источник
11
Это не "использование cout".
Johnsyweb
2
12 цифр не "полная точность"
Роланд Иллиг
0

Наиболее переносимо ...

#include <limits>

using std::numeric_limits;

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

источник
16
Мне любопытно: почему «+1»?
Эрик Маленфант
0

С ostream :: precision (int)

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

даст

3.141592653589793, 2.718281828459045

Почему вы должны сказать «+1», я понятия не имею, но дополнительная цифра, которую вы получаете из этого, правильна.

Янн
источник
3
numeric_limits <unsigned char> :: digits10 равно 2. Поскольку оно может содержать любое десятичное число из двух цифр 0..99. Он также может содержать 255 .. но не 256, 257 ... 300 и т. Д., Поэтому цифры10 не 3! Я думаю, что "+1" добавлен, чтобы преодолеть что-то подобное.
Дмитрий Юрченко
0

Это покажет значение до двух десятичных знаков после точки.

#include <iostream>
#include <iomanip>

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

Смотрите здесь: Запись с фиксированной точкой

станд :: фиксированной

Использовать фиксированную запись с плавающей точкой Устанавливает фиксированный флаг формата float для потока str.

Если для поля с плавающей запятой задано фиксированное значение, значения с плавающей запятой записываются с использованием записи с фиксированной запятой: значение представляется с точно таким количеством цифр в десятичной части, которое указано в поле точности (точность), и без экспоненты.

станд :: setprecision

Установить десятичную точность Устанавливает десятичную точность, которая будет использоваться для форматирования значений с плавающей запятой в выходных операциях.

Если вы знакомы со стандартом IEEE для представления чисел с плавающей запятой, вы должны знать, что невозможно вывести числа с плавающей запятой с полной точностью вне области действия стандарта , то есть это всегда приведет к округление реальной стоимости.

Вы должны сначала проверить, находится ли значение в области , если да, тогда используйте:

cout << defaultfloat << d ;

станд :: defaultfloat

Использовать стандартную нотацию с плавающей точкой Устанавливает флаг формата float для потока str в defaultfloat.

Если для параметра float установлено значение defaultfloat, значения с плавающей точкой записываются с использованием нотации по умолчанию: представление использует столько значащих цифр, сколько необходимо, до десятичной точности (точности) потока, считая как цифры до, так и после десятичной точки (если есть) ).

Это также поведение по умолчанию cout, что означает, что вы не используете его явно.

emmmphd
источник