ฉันจะพิมพ์ค่าสองเท่าด้วยความแม่นยำเต็มที่ใช้ 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
Arthur

36
ความแม่นยำต้องเป็น 17 (หรือ std :: numeric_limits <double> :: digit10 + 2) เนื่องจากต้องการตัวเลขพิเศษ 2 หลักเมื่อแปลงจากทศนิยมกลับเป็นตัวแทนไบนารีเพื่อให้แน่ใจว่าค่าจะถูกปัดเศษเป็นค่าดั้งเดิมเดียวกัน นี่คือกระดาษที่มีรายละเอียดบางอย่าง: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Mike Fisher

8
คำตอบที่ถูกต้องจริง ๆ หรือไม่ เมื่อฉันใช้ตัวเลขสูงด้วยตนเองฉันสามารถพิมพ์ออกมาได้มากถึง 51 หลักของ e โดยประมาณ แต่ด้วยcout.precision(numeric_limits<double>::digits10 + 2);ฉันจะได้รับเพียง 16 ....
Assimilater

6
สำหรับผู้ที่มองหาที่กล่าวถึง 17 หลักในกระดาษ @MikeFisher อ้างถึงมันอยู่ภายใต้ทฤษฎีบทที่ 15
Emile Cormier

15
@MikeFisher คุณพูดถูกC ++ 11 แนะนำmax_digits10เพื่อแสดงถึงสิ่งเดียวกัน แก้ไขคำตอบเพื่อสะท้อนสิ่งนี้
ตำนาน 2k

70

การใช้std::setprecision:

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;

2
มีมาโคร MAX_PRECISION หรือ enum หรืออะไรบางอย่างที่ฉันสามารถส่งไปยัง std :: setPrecision ได้บ้าง
Jason Punyon

2
std :: setprecision (15) สำหรับ double (ok หรือ 16), log_10 (2 ** 53) ~ = 15.9
user7116

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

6
ควรเป็นstd::setprecision (17)สองเท่าดูความคิดเห็นเกี่ยวกับคำตอบของ @Bill The Lizard
Alec Jacobson

9
สำหรับ std :: setprecision ให้ทำงาน #include <iomanip> ควรรวมอยู่ด้วย
user2262504

24

นี่คือสิ่งที่ฉันจะใช้:

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

โดยทั่วไปแพคเกจ จำกัด มีลักษณะสำหรับการสร้างในทุกประเภท
หนึ่งในลักษณะของเลขทศนิยม (ลอย / สองครั้ง / ยาวคู่) คือคุณลักษณะ สิ่งนี้กำหนดความถูกต้อง (ฉันลืมคำศัพท์ที่แน่นอน) ของเลขทศนิยมในฐาน 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?
Alessandro Jacopson

5
@LokiAstari คุณสามารถใช้ C + 11 max_digits10แทนได้ ดูนี่สิ
ตำนาน 2k

1
@AlecJacobson มันค่อนข้างควรจะไม่พลบางส่วนmax_digits10 digits10+2มิฉะนั้นในกรณีของfloat, long double, boost::multiprecision::float128นี้จะล้มเหลวเนื่องจากมีคุณจะต้องแทน+3 +2
Ruslan

14

วิธี iostreams เป็นประเภทของ clunky ฉันชอบที่จะใช้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


เอกสารเพิ่มบอกว่า "สำหรับตัวเลขที่มีความเชี่ยวชาญเฉพาะของ std :: numeric_limits ที่สอดคล้องกันรุ่นปัจจุบันจะเลือกความแม่นยำในการจับคู่" ดูเหมือนจะเป็นวิธีที่ง่ายที่สุดในการรับความแม่นยำสูงสุด ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/ ...... )
JDiMatteo

11

โดยความแม่นยำเต็มที่ฉันถือว่าความแม่นยำเพียงพอที่จะแสดงการประมาณค่าที่ดีที่สุด แต่ควรชี้ให้เห็นว่าdoubleถูกเก็บไว้โดยใช้การแสดงฐาน 2 และฐาน 2 ไม่สามารถแสดงสิ่งที่เล็กน้อยได้อย่าง1.1แน่นอน วิธีเดียวที่จะได้รับความแม่นยำเต็มรูปแบบของ double double (ไม่มีข้อผิดพลาดรอบนอก) คือการพิมพ์บิตไบนารี่ (หรือ hex nybbles) วิธีหนึ่งในการทำเช่นนั้นคือการเขียนdoublea ไปยังunionจากนั้นพิมพ์ค่าจำนวนเต็มของบิต

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

สิ่งนี้จะให้ความแม่นยำที่ถูกต้อง 100% ของ double ... และไม่สามารถอ่านได้อย่างเต็มที่เนื่องจากมนุษย์ไม่สามารถอ่านรูปแบบ double IEEE ได้! Wikipediaมีการเขียนที่ดีเกี่ยวกับวิธีตีความบิตไบนารี่

ใน 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เราได้รับ
Evgeni Sergeev

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. ฐานอะไร

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?

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 53doubleที่แตกต่างกัน

แต่ถ้าพิมพ์รหัสในทศนิยมกับNเลขนัยสำคัญจำนวนรวมกัน [1.0 ... 10.0) เป็น 9/10 * 10 N

สิ่งที่เลือกN(ความแม่นยำ) จะไม่มีการแมปแบบหนึ่งต่อหนึ่งระหว่างdoubleข้อความทศนิยม หากเลือกค่าคงที่Nบางครั้งมันจะมากหรือน้อยกว่าที่จำเป็นจริงๆสำหรับdoubleค่าบางอย่าง เราอาจผิดพลาดน้อยเกินไป ( a)ด้านล่าง) หรือมากเกินไป ( b)ด้านล่าง)

3 ผู้สมัครN:

ก) ใช้Nดังนั้นเมื่อมีการแปลงจากแอปรับส่งข้อความdouble-text doubleเรามาถึงข้อความเดียวกันทั้งหมด

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

ข) ใช้Nดังนั้นเมื่อมีการแปลงจากdouble-text- doubleเรามาถึงในเวลาเดียวกันทั้งหมดdoubledouble

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

เมื่อmax_digits10ไม่สามารถใช้งานได้โปรดทราบว่าเนื่องจากแอตทริบิวต์ 2 ฐานและ 10 ฐานdigits10 + 2 <= max_digits10 <= digits10 + 3เราสามารถใช้digits10 + 3เพื่อพิมพ์ตัวเลขทศนิยมให้เพียงพอ

c) ใช้สิ่งNที่แตกต่างกันไปตามค่า

สิ่งนี้มีประโยชน์เมื่อโค้ดต้องการแสดงข้อความน้อยที่สุด ( N == 1) หรือค่าที่แน่นอนของ a double( N == 1000-ishในกรณีของdenorm_min) แต่เนื่องจากนี่คือ "งาน" และไม่น่าจะเป็นเป้าหมายของ OP ก็จะถูกตั้งสำรอง


โดยปกติจะเป็น b) ที่ใช้เพื่อ "พิมพ์doubleค่าด้วยความแม่นยำเต็มที่" แอปพลิเคชันบางอย่างอาจต้องการ a) ข้อผิดพลาดในการไม่ให้ข้อมูลมากเกินไป

ด้วย.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อาจแตกต่างกัน สิ่งที่สับสนควรระวัง
Simpleton

สำหรับรุ่นหลังนี่คือความยาวบัฟเฟอร์ที่ต้องการสำหรับการแทนค่าสตริงที่แน่นอนของตัวเลขสองตัวทั้งหมดในโหมดทางวิทยาศาสตร์โดยใช้ 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" ซึ่งอาจเป็นปัญหาในบริบทอนุกรม / deserialization คุณสามารถบังคับให้มันส่งออกจุดทศนิยมโดยใช้ "% #. * g" แต่มันมีข้อเสียเปรียบที่มันจะเพิ่มจำนวนของศูนย์ต่อท้ายซึ่งมันไม่ได้โดยไม่มี # ...
Simpleton

3
printf("%.12f", M_PI);

% .12f หมายถึงจุดลอยตัวที่มีความแม่นยำ 12 หลัก


11
นี่ไม่ใช่ "ใช้ cout"
Johnsyweb

2
12 หลักไม่ใช่ "ความแม่นยำเต็มที่"
Roland Illig

0

ส่วนใหญ่พกพา ...

#include <limits>

using std::numeric_limits;

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

16
ฉันอยากรู้: ทำไม "+1"
Éric Malenfant

0

ด้วย ostream :: ความแม่นยำ (int)

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

จะให้ผลผลิต

3.141592653589793, 2.718281828459045

ทำไมคุณต้องพูดว่า "+1" ฉันไม่มีเงื่อนงำ แต่ตัวเลขพิเศษที่คุณได้รับนั้นถูกต้อง


3
numeric_limits <char ที่ไม่ได้ลงชื่อ> :: digit10 เท่ากับ 2 เพราะมันสามารถมีจำนวนทศนิยมสองหลักใด ๆ 0..99 มันสามารถมี 255 .. แต่ไม่ใช่ 256, 257 ... 300 เป็นต้นนี่คือเหตุผลที่เลข 10 ไม่ใช่ 3! ฉันคิดว่า "+1" ถูกเพิ่มเข้ามาเพื่อเอาชนะสิ่งนี้
Dmitriy Yurchenko

0

สิ่งนี้จะแสดงค่าสูงสุดทศนิยมสองตำแหน่งหลังจุด

#include <iostream>
#include <iomanip>

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

ดูที่นี่: สัญกรณ์จุดคงที่

มาตรฐาน :: คงที่

ใช้สัญกรณ์จุดลอยตัวแบบคงที่ตั้งค่าสถานะรูปแบบ floatfield สำหรับ str สตรีมเป็นคงที่

เมื่อมีการตั้งค่า floatfield เป็นค่าคงที่ค่าจุดลอยตัวจะถูกเขียนโดยใช้เครื่องหมายจุดคงที่: ค่าจะถูกแสดงด้วยตัวเลขหลายหลักในส่วนทศนิยมตามที่ระบุโดยเขตความแม่นยำ (ความแม่นยำ) และไม่มีส่วนเลขชี้กำลัง

มาตรฐาน :: setprecision

ตั้งค่าความแม่นยำทศนิยมตั้งค่าความแม่นยำทศนิยมที่จะใช้ในการจัดรูปแบบค่าทศนิยมในการดำเนินงานการส่งออก

หากคุณคุ้นเคยกับมาตรฐาน IEEE สำหรับการแสดงคะแนนแบบลอยตัวคุณจะรู้ว่าเป็นไปไม่ได้ที่จะแสดงคะแนนแบบลอยที่มีความแม่นยำเต็มรูปแบบนอกขอบเขตของมาตรฐานนั่นคือการบอกว่ามันจะส่งผลเสมอ การปัดเศษของมูลค่าที่แท้จริง

คุณต้องตรวจสอบก่อนว่าค่านั้นอยู่ในขอบเขตหรือไม่ถ้าใช่ให้ใช้:

cout << defaultfloat << d ;

มาตรฐาน :: defaultfloat

ใช้สัญกรณ์จุดลอยตัวเริ่มต้นตั้งค่าสถานะการจัดรูปแบบ floatfield สำหรับ str สตรีมเป็น defaultfloat

เมื่อ floatfield ถูกตั้งค่าเป็น defaultfloat ค่าจุดลอยตัวจะถูกเขียนโดยใช้สัญกรณ์เริ่มต้น: การแทนค่าใช้ตัวเลขที่มีความหมายมากเท่าที่ต้องการจนถึงความแม่นยำทศนิยมของสตรีม (ความแม่นยำ) นับทั้งตัวเลขก่อนและหลังจุดทศนิยม )

นั่นเป็นพฤติกรรมเริ่มต้นของcoutซึ่งหมายความว่าคุณไม่ได้ใช้อย่างชัดเจน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.