常用功能可精确打印浮点数 [英] Commonly available functions for accurate floating-point number printing
问题描述
弗洛里安·洛伊奇(Florian Loitsch)的精彩作品叫做使用整数快速而准确地打印浮点数" .在将任何类型的double打印为字符串的情况下,这听起来很有用.我认为很多人直到最近才知道我的信息,例如使用 setprecisiton(n)
或 printf(%0.nf")
设置修复精度,并且经常选择 n
太大而导致浮点数不精确,或者太小而无法截取有价值的数据.我想知道C ++标准(包括即将发布的),boost,libc或易于访问和测试的地方是否有与此类似的东西?
There is wonderful work of Florian Loitsch called "Printing Floating-Point Numbers Quickly and Accurately with Integers". This sounds pretty useful in case of printing any kind of double into string. I think many people included me until recently only know like setting a fix precision with setprecisiton(n)
or printf("%0.nf")
and often either choosing n
too large and getting floating point imprecision or too small and cutting off valuable data. I wonder is there something similar to this in C++ standard (including upcoming), boost, libc, or somewhere easily reachable and tested?
推荐答案
我认为标准C ++库中没有任何真正好的解决方案.
I don't think there is any really good solution in the standard C++ library.
我使用一种技巧来尝试几种不同的格式,然后选择最短的和文本表示形式,并且该文本表示形式在往返时具有完全相同的值.我用来保存在文本文件(XML)输出中的内容.
I use the trick of trying several different formats, and then selecting the text representation that is the shortest and the text representation has the exact same value when round-tripped. Which I use to save in my text file (XML) output.
有时这意味着选择严格的十六进制浮点表示形式.(可能会破坏您的用例.)
Which sometimes means selecting the exacting hexadecimal floating point representation. (May be a deal breaker for your use case.)
这是代码,我添加了一个表格以生成一些示例,如果最短,则将它们标记为 *
,如果是最短的标记,则使用 warning 标记他们不会往返.
Here's the code, which I've added a table to generate some examples, and mark them as *
if they are the shortest, or with a warning marker if they do not round trip.
#include <iomanip>
#include <ios>
#include <iostream>
#include <sstream>
using std::cout;
using std::defaultfloat;
using std::fixed;
using std::hexfloat;
using std::istringstream;
using std::left;
using std::scientific;
using std::setw;
using std::string;
using std::stringstream;
enum class cap { title, middle, end };
static void print(const char* text, double num, cap c) {
stringstream ss;
ss << fixed << num;
auto s = ss.str();
double fn = stod(s);
size_t flen = s.length();
string falert = "";
if (fn != num) { falert = "/!\\"; flen += 100; }
ss.str("");
ss.clear();
ss << scientific << num;
s = ss.str();
double sn = stod(s);
size_t slen = s.length();
string salert = "";
if (sn != num) { salert = "/!\\"; slen += 100; }
ss.str("");
ss.clear();
ss << hexfloat << num;
s = ss.str();
double hn = stod(s);
size_t hlen = s.length();
string halert = "";
if (hn != num) { halert = "/!\\"; hlen += 100; }
ss.str("");
ss.clear();
ss << defaultfloat << num;
s = ss.str();
double dn = stod(s);
size_t dlen = s.length();
string dalert = "";
if (dn != num) { dalert = "/!\\"; dlen += 100; }
char gbuf[256];
sprintf(gbuf, "%g", num);
s = gbuf;
double gn = stod(s);
size_t glen = s.length();
string galert = "";
if (gn != num) { galert = "/!\\"; glen += 100; }
if (flen <= slen && flen <= hlen && flen <= dlen) falert += "*";
if (slen <= flen && slen <= hlen && slen <= dlen) salert += "*";
if (hlen <= flen && hlen <= slen && hlen <= dlen) halert += "*";
if (dlen <= flen && dlen <= hlen && dlen <= slen) dalert += "*";
if (glen <= dlen && glen <= flen && glen <= hlen && glen <= slen) galert += "*";
if (c == cap::title) cout <<
"┌──────────┬────────────┬──────────────────────────┐\n"
"│ number │ iomanip │ representation │\n"
"├──────────┼────────────┼──────────────────────────┤\n"
;
cout << left
<< "│ " << setw(8) << text << " │ fixed │ " << setw(24) << fixed << num << " │" << falert << "\n"
<< "│ " << setw(8) << text << " │ scientific │ " << setw(24) << scientific << num << " │" << salert << "\n"
<< "│ " << setw(8) << text << " │ hexfloat │ " << setw(24) << hexfloat << num << " │" << halert << "\n"
<< "│ " << setw(8) << text << " │ default │ " << setw(24) << defaultfloat << num << " │" << dalert << "\n"
<< "│ " << setw(8) << text << " │ %g │ " << setw(24) << gbuf << " │" << galert << "\n"
;
cout << (c != cap::end ?
"├──────────┼────────────┼──────────────────────────┤\n" :
"└──────────┴────────────┴──────────────────────────┘\n" );
}
static void table() {
print("0.0", 0.0, cap::title);
print("0.01", 0.01, cap::middle);
print("0.00001", 0.00001, cap::middle);
print("1e99" , 1.e+99, cap::middle);
print("0.1" , 0.1, cap::middle);
print("0.2" , 0.2, cap::middle);
print("0.3" , 0.3, cap::middle);
print("0.4" , 0.4, cap::middle);
print("0.5" , 0.5, cap::middle);
print("0.6" , 0.6, cap::middle);
print("0.7" , 0.7, cap::middle);
print("0.8" , 0.8, cap::middle);
print("0.9" , 0.9, cap::middle);
print("NTSC" , 30.0/1001.0, cap::middle);
print("1/3" , 1.0/3.0, cap::end);
}
int main() {
table();
}
这篇关于常用功能可精确打印浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!