Як друкувати подвійне значення з повною точністю за допомогою cout?


331

Тож я отримав відповідь на своє останнє запитання (не знаю, чому я не думав про це). Я друкував doubleвикористання, coutяке було округлене, коли я не очікував цього. Як я можу зробити coutдрук з doubleвикористанням повної точності?

Відповіді:


390

Ви можете встановити точність безпосередньо 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 ....
Асимілятор

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 макрос або enum чи щось, що я можу передати std :: setPrecision?
Джейсон Пуньйон

2
std :: setprecision (15) для подвійного (ok або 16), log_10 (2 ** 53) ~ = 15.9
user7116

14
std :: setprecision (std :: numeric_limits <double>: digits10)
Éric Malenfant

6
Має бути std::setprecision (17)подвійним, дивіться коментарі до відповіді @Bill The Lizard.
Алек Якобсон

9
для std :: setprecision для роботи, #include <iomanip> слід включити.
користувач2262504

24

Ось що я б використав:

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

В основному пакет обмежень має риси для всіх типів збірки.
Однією з ознак чисел з плаваючою комою (float / double / long double) є атрибут digits10. Це визначає точність (я забуваю точну термінологію) числа з плаваючою комою в базі 10.

Дивіться: http://www.cplusplus.com/reference/std/limits/numeric_limits.html
Докладніше про інші атрибути.


12
Цей заголовок потрібен для використання std::setprecision(): #include <iomanip>
Мартін Бергер

це має бути std::numeric_limits<double>замістьnumberic_limits<double>
niklasfi

2
Чому ви додаєте 1в std::numeric_limits<double>::digits10?
Алессандро Якопсон

5
@LokiAstari Натомість ви можете використовувати C + 11 max_digits10. Дивіться це .
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;

Вихід:

Пі: 3.14159265358979


Документація підсилення говорить "Для числових даних, які мають відповідну спеціалізацію std :: numeric_limits, поточна версія тепер вибирає точність відповідності". Це здається найпростішим способом отримати максимальну точність. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo

11

З повною точністю я припускаю, що середня точність відображає найкраще наближення до передбачуваного значення, але слід зазначити, що doubleзберігається за допомогою представлення бази 2, а база 2 не може представляти щось настільки тривіальне, як 1.1саме. Єдиний спосіб отримати повну точність фактичного подвійного (без ПОМИЛКИ ВИМКУВАННЯ КРУГЛИ) - роздрукувати двійкові біти (або шістнадцяткові nybbles). Один із способів цього зробити - це doubleзаписати на a 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 :: fix.
У фіксованій нотації 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:

a) Використовуйте 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недоступно, зауважте, що через атрибути бази 2 та бази 10 digits10 + 2 <= max_digits10 <= digits10 + 3ми можемо використати digits10 + 3для того, щоб надрукувати достатню кількість десяткових цифр.

в) Використовуйте значення, 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 питання


Чудова відповідь! Кілька зауважень, хоча: Ви маєте рацію, що 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


0

Найбільш портативно ...

#include <limits>

using std::numeric_limits;

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

16
Мені цікаво: чому "+1"?
Ерік Маленфант

0

З ostream :: точність (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;

Дивіться тут: Позначення з фіксованою точкою

std :: виправлено

Використовувати фіксовану нотацію з плаваючою комою Встановлює прапор формату floatfield для потоку str на фіксований.

Коли для плаваючого поля встановлено фіксований, значення з плаваючою комою записуються за допомогою позначення з фіксованою точкою: значення подається рівно стільки цифр у десятковій частині, скільки визначено полем точності (точності) і без частини експонента.

std :: точність встановлення

Встановити десяткову точність Встановлює десяткову точність, яка буде використовуватися для форматування значень з плаваючою комою у вихідних операціях.

Якщо ви знайомі зі стандартом IEEE для представлення плаваючих точок, ви знаєте, що неможливо показувати плаваючі точки з точністю поза рамками стандарту , тобто це завжди призведе до округлення реальної величини.

Спочатку потрібно перевірити, чи є значення в межах , якщо так, то використати:

cout << defaultfloat << d ;

std :: defaultfloat

Використовувати нотацію з плаваючою комою за замовчуванням Встановлює прапор формату floatfield для потоку str на defaultfloat.

Коли для floatfield встановлено значення defaultfloat, значення з плаваючою комою записуються за допомогою позначення за замовчуванням: представлення використовує стільки значущих цифр, скільки потрібно до десяткової точності (точності) потоку, підраховуючи обидві цифри до і після десяткової крапки (якщо такі є ).

Це також поведінка за замовчуванням cout, що означає, що ви не використовуєте це явно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.