แปลง float เป็น string ด้วยความแม่นยำและระบุจำนวนทศนิยม?


91

คุณจะแปลง float เป็นสตริงใน C ++ ได้อย่างไรในขณะที่ระบุความแม่นยำและจำนวนหลักทศนิยม

ตัวอย่างเช่น: 3.14159265359 -> "3.14"


ทำไมไม่สร้าง temp float ด้วยความแม่นยำที่คุณต้องการแล้วแปลงเป็นสตริง?
Gilad

@Gilad 'temp float' ไม่มี 'ความแม่นยำที่ระบุ' และมีหลายกรณีที่วิธีการดังกล่าวจะพังทลายลง คำถามนี้เหมือนกับการถามว่า "รูปแบบ '% .2f' เทียบเท่ากับอะไร?
user2864740

1
ดูstackoverflow.com/questions/14432043/c-float-formattingหากจำเป็นสำหรับ IO
user2864740

คำตอบ:


133

วิธีทั่วไปคือการใช้stringstream:

#include <iomanip>
#include <sstream>

double pi = 3.14159265359;
std::stringstream stream;
stream << std::fixed << std::setprecision(2) << pi;
std::string s = stream.str();

ดูคงที่

ใช้สัญกรณ์ทศนิยมคงที่

ตั้งfloatfieldธงรูปแบบสำหรับSTRfixedกระแสไป

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

และsetprecision


สำหรับการแปลงวัตถุประสงค์ทางเทคนิคเช่นการจัดเก็บข้อมูลในไฟล์ XML หรือ JSON C ++ 17 จะกำหนดกลุ่มฟังก์ชันto_chars

สมมติว่าคอมไพเลอร์ที่เข้ากันได้ (ซึ่งเราขาดในขณะที่เขียน) สิ่งนี้สามารถพิจารณาได้:

#include <array>
#include <charconv>

double pi = 3.14159265359;
std::array<char, 128> buffer;
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), pi,
                               std::chars_format::fixed, 2);
if (ec == std::errc{}) {
    std::string s(buffer.data(), ptr);
    // ....
}
else {
    // error handling
}

31

วิธีการทั่วไปสำหรับการทำสิ่งนี้คือ "พิมพ์เป็นสตริง" ใน C ++ หมายถึงการใช้std::stringstreamสิ่งต่างๆเช่น:

std::stringstream ss;
ss << std::fixed << std::setprecision(2) << number;
std::string mystring = ss.str();

13

อีกทางเลือกหนึ่งคือsnprintf:

double pi = 3.1415926;

std::string s(16, '\0');
auto written = std::snprintf(&s[0], s.size(), "%.2f", pi);
s.resize(written);

การสาธิต written < 0จัดการข้อผิดพลาดควรจะเพิ่มคือการตรวจสอบ


ทำไมจะsnprintfดีกว่าในกรณีนี้? กรุณาอธิบาย ... มันจะไม่เร็วขึ้นอย่างแน่นอนสร้างโค้ดน้อยลง [ฉันเขียนการใช้งาน printf มันค่อนข้างกะทัดรัดที่โค้ดต่ำกว่า 2,000 บรรทัด แต่การขยายมาโครจะอยู่ที่ประมาณ 2,500 บรรทัด]
Mats Petersson

1
@MatsPetersson ฉันเชื่ออย่างยิ่งว่ามันจะเร็วขึ้น นั่นเป็นเหตุผลที่ฉันตอบคำถามนี้ตั้งแต่แรก
Columbo

@MatsPetersson ฉันได้ทำการวัด (GCC 4.8.1, -O2) ที่บ่งชี้ว่า snprintf ใช้เวลาน้อยลงโดยปัจจัย 17/22 ฉันจะอัปโหลดรหัสในไม่ช้า
Columbo

@MatsPetersson เกณฑ์มาตรฐานของฉันอาจมีข้อบกพร่อง แต่เวอร์ชัน stringstream ใช้เวลานานเป็นสองเท่าของเวอร์ชัน snprintf ที่การทำซ้ำ 1000000 (เสียงดัง 3.7.0, libstdc ++, -O2)

ฉันทำการวัดบางอย่างด้วย - และดูเหมือน (อย่างน้อยก็บน Linux) ว่าทั้ง stringstream และ snprintf ใช้__printf_fpฟังก์ชันพื้นฐานเดียวกัน- ค่อนข้างแปลกที่ต้องใช้เวลานานกว่านี้มากstringstreamเพราะไม่ควรต้อง
Mats Petersson


8

นี่คือวิธีแก้ปัญหาโดยใช้มาตรฐานเท่านั้น อย่างไรก็ตามโปรดทราบว่านี่เป็นเพียงการปัดเศษเท่านั้น

    float number = 3.14159;
    std::string num_text = std::to_string(number);
    std::string rounded = num_text.substr(0, num_text.find(".")+3);

เพื่อroundedให้ผลตอบแทน:

3.14

โค้ดจะแปลง float ทั้งหมดเป็นสตริง แต่จะตัดอักขระทั้งหมด 2 ตัวต่อท้าย "."


สิ่งเดียวคือวิธีนี้คุณไม่ได้ปัดเศษตัวเลข
ChrCury78

1
@ ChrCury78 ตามที่ระบุไว้ในคำตอบของฉันเพียงปัดเศษลง หากคุณต้องการการปัดเศษตามปกติเพียงแค่เพิ่ม 5 ลงในดิจิเก็ตตามค่าของคุณ ตัวอย่างเช่นnumber + 0.005ในกรณีของฉัน
Sebastian R.

3

ที่นี่ฉันกำลังให้ตัวอย่างเชิงลบที่คุณต้องการหลีกเลี่ยงเมื่อแปลงตัวเลขลอยเป็นสตริง

float num=99.463;
float tmp1=round(num*1000);
float tmp2=tmp1/1000;
cout << tmp1 << " " << tmp2 << " " << to_string(tmp2) << endl;

คุณได้รับ

99463 99.463 99.462997

หมายเหตุ: ตัวแปร num อาจเป็นค่าใดก็ได้ที่ใกล้เคียงกับ 99.463 คุณจะได้รับงานพิมพ์เดียวกัน ประเด็นคือหลีกเลี่ยงฟังก์ชัน c ++ 11 "to_string" ที่สะดวก ฉันใช้เวลาสักพักในการออกจากกับดักนี้ วิธีที่ดีที่สุดคือวิธี stringstream และ sprintf (ภาษา C) C ++ 11 หรือใหม่กว่าควรระบุพารามิเตอร์ที่สองเป็นจำนวนหลักหลังจุดลอยตัวที่จะแสดง ตอนนี้ค่าเริ่มต้นคือ 6 ฉันกำลังวางสิ่งนี้เพื่อไม่ให้คนอื่นเสียเวลากับเรื่องนี้

ฉันเขียนเวอร์ชันแรกของฉันโปรดแจ้งให้เราทราบหากคุณพบข้อบกพร่องใด ๆ ที่ต้องได้รับการแก้ไข คุณสามารถควบคุมพฤติกรรมที่แน่นอนได้ด้วย iomanipulator ฟังก์ชันของฉันมีไว้สำหรับแสดงจำนวนหลักหลังจุดทศนิยม

string ftos(float f, int nd) {
   ostringstream ostr;
   int tens = stoi("1" + string(nd, '0'));
   ostr << round(f*tens)/tens;
   return ostr.str();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.