将浮点数转换为字符串而不会丢失精度
Posted
技术标签:
【中文标题】将浮点数转换为字符串而不会丢失精度【英文标题】:Convert float to string without losing precision 【发布时间】:2011-05-26 11:45:42 【问题描述】:我想在不丢失或添加任何单精度数字的情况下将浮点值存储到字符串中。
例如,如果我的浮点值为 23.345466467 ,我希望我的字符串具有 str = "23.345466467" 精确数字。
我尝试使用带有 %f 的 CString 格式函数。它只给出前6个精度。 或者如果我使用 %10 ,如果我的浮点值的精度小于 10,它会增加一些垃圾精度。我想在我的字符串中获取精确的浮点值。如何做到这一点?
【问题讨论】:
这取决于您的浮点值 23.345466467 是否完全可表示(可能不是) @Mitch:我也在想同样的事情。检查它是否是最简单的方法是什么? @Mitch Wheat:float
值始终可以表示为字符串,只要您转换为的基数可以被 2 整除。当然,23.345466467
不能用 float
表示第一名。
你的要求简直不可能把1/3完全表示成一个十进制字符串。
【参考方案1】:
这取决于您的浮点值 23.345466467 是否完全可表示(可能不是)
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Why Floating-Point Numbers May Lose Precision
我还想问你为什么需要这样做?你打算用字符串表示什么?你知道 double 和 decimal 类型吗?
[未经测试:您可以尝试转换为 double 然后使用 "%d" 也许这会引入额外的“保护”数字,但它仍然不会t 适用于所有值]
【讨论】:
感谢您的回复。我想将浮点值存储到 xml 中,为此我将其转换为字符串。我想读回确切的浮点值。我的问题是从浮点数转换为字符串时,我失去了精度。我想要准确的精度。如何做到这一点? 这说明了你在做什么,但不是为什么。 我正在从设备读取浮点值。在某个时间点后,我将向设备写入相同的值。如果单个精度发生变化,设备将不接受它。 如果您想要 100% 的精度,您可以将其存储为 32 位十六进制。编辑:哦,这正是pmg建议的。【参考方案2】:也许您使用 fprintf() (from ) 将您的浮点数转换为字符串。 但是在做作之后,精度可能会立即丢失,因为“flaot”和“double”的精度有限。必须有人确认。
【讨论】:
这甚至是一个答案吗?【参考方案3】:首先:您不能将浮动转换为字符串并返回,而不会丢失一两点。因为这不是一对一的转换,因为它们使用不同的基数。
为了保持浮点类型的最佳精度:
std::string convert(float value)
std::stringstream ss;
ss << std::setprecision(std::numeric_limits<float>::digits10+1);
ss << value;
return ss.str();
或者更通用的
template<typename FloatingPointType>
std::string convert(FloatingPointType value)
std::stringstream ss;
ss << std::setprecision(std::numeric_limits<FloatingPointType>::digits10+1);
ss << value;
return ss.str();
它会切断你不需要的数字,但尽可能保持最高精度。
【讨论】:
@Rajesh,这是因为您想要的数字不能由float
精确表示,请参阅您问题中的 cmets。如果它不能被表现出来,它根本就不能被表现出来,你几乎无能为力!您需要考虑另一种方法,可能是整数指数/尾数?
不应该是digits10+2吗?我写了一个快速测试程序,只有 +1 似乎不允许我通过字符串往返浮点数。不过,+2 似乎适用于大小数字。
这个答案是绝对错误的。是的,您可以将浮点数转换为字符串并返回而不会丢失“一两个”。这些是我们正在处理的计算机,而不是被划入沙子的近似值。对于 C/C++ 中的浮点数,您需要使用 %1.8e 或 %.9g printf。当您转换回来时,您将检索到完全相同的值。打印的值可能与您在调试器中看到的不匹配,并且可能既不代表确切的浮点值,但它们唯一地标识它。有关详细信息,请参阅我的博客文章:randomascii.wordpress.com/2013/02/07/…
@RajeshSubramanian 请将此答案的状态更改为正确答案。它的开场白是完全错误的。正如我在之前的评论中所说,往返是完全可能的。 Hugues 的回答看起来好多了。
这个答案是错误的(你不会不可避免地失去一两点),所以最好对其中一个正确的答案发表评论。但是,我不知道您所说的这种“非可读格式”是什么意思。十进制科学记数法非常易读。【参考方案4】:
按照@pgm 的建议,最好的方法是存储它的二进制表示。但是,您也可以将其存储为十进制,但使用句号,例如:
23.5095446(1545855463)
通过这种方式,它们可以以某种方式“无损”存储,但需要非常仔细的编码。
【讨论】:
【参考方案5】:C99 支持printf
中的%a
格式,允许在不损失精度的情况下输出双精度数的内容。
【讨论】:
【参考方案6】:您需要唯一代表任何float
的(十进制)位数根据定义为std::numeric_limits<float>::maxdigits10
。 float
格式无法区分 0.0
和 0.0000
,因此此常量是您需要的最大值。
现在,请注意我说的是唯一。这并不准确。这意味着如果你写两个不同的二进制值,你会得到两个不同的十进制表示。这也意味着如果你读回这样的十进制表示,它不会与任何其他值混淆,因此你会得到与之前完全相同的二进制值。通常,这两个保证就足够了。
[编辑]
std::numeric_limits<float>::digits10
代表相反的概念:保证十进制->二进制->十进制的最大十进制位数。
【讨论】:
这应该是max_digits10
(在 C++11 中引入),而不是 digits10
。 en.cppreference.com/w/cpp/types/numeric_limits/max_digits10【参考方案7】:
您是否存储它以便以后可以重新阅读并将其用作浮点数?或者你关心字符串说什么?正如其他人所说,您需要编写二进制表示而不是base10。或者你可以有点棘手,做这样的事情
float f=23.345466467
int i=*(int*)&f; //re-interpret the bits in f as an int
cout << i;
并阅读它
int i;
cin >> i;
float f=*(float&)&i;
[关于可移植性和确保 int 和 float 在您的平台上大小相同的标准免责声明。]
通过这种方式,您可以输出值并将其读回而不会丢失任何精度。但它在存储时不是人类可读的。
【讨论】:
【参考方案8】:其他人已经评论说 23.345466467 不作为浮点数存在,但如果您的目标只是对确实存在的浮点值进行往返转换,而不会意外地稍微改变它们的值,您可以使用 snprintf
和 strtod
至少有DECIMAL_DIG
位置(POSIX,但不是纯 ISO C,保证此往返是准确的)或以十六进制而不是十进制打印浮点数。 C99 具有 %a
格式说明符,用于以十六进制打印浮点数,但如果您不能依赖 C99,则很容易编写自己的。与十进制不同,打印十六进制浮点数的朴素算法工作得很好。它是这样的:
-
按 2 的幂进行缩放以将值置于
0x8 <= val < 0x10
范围内。 2 的幂将成为结果中的指数。
反复去掉val
的整数部分,乘以0x10
得到输出位数。
当val
达到 0 时,您就完成了。
反方向的转换同样容易。
【讨论】:
【参考方案9】:请参阅http://randomascii.wordpress.com/2012/03/08/float-precisionfrom-zero-to-100-digits-2/ 中的精彩详细讨论。
简短的回答是最小精度如下:
printf("%1.8e", d); // Round-trippable float, always with an exponent
printf("%.9g", d); // Round-trippable float, shortest possible
printf("%1.16e", d); // Round-trippable double, always with an exponent
printf("%.17g", d); // Round-trippable double, shortest possible
或者等效地,使用std::ostream& os
:
os << scientific << setprecision(8) << d; // float; always with an exponent
os << defaultfloat << setprecision(9) << d; // float; shortest possible
os << scientific << setprecision(16) << d; // double; always with an exponent
os << defaultfloat << setprecision(17) << d; // double; shortest possible
【讨论】:
更多详情请看这篇文章:randomascii.wordpress.com/2013/02/07/…【参考方案10】:OP 想要 C 或 C++,但如果您碰巧需要 C# 解决方案,请使用“R”数字格式字符串,如下所示:
float x = 23.345466467f;
string str = x.ToString("R");
请注意,“R”是“往返”的缩写。有关MSDN 的更多信息。
【讨论】:
-1,回答完全不同的语言。对 OP 一点帮助都没有。 也许不是,但它可以帮助那些在这个页面上搜索 C# 答案的人(就像我一样)。我很清楚 OP 想要 C/C++,所以我很清楚我说的是什么语言。以上是关于将浮点数转换为字符串而不会丢失精度的主要内容,如果未能解决你的问题,请参考以下文章
如何将浮点数转换为长度为 4 的字节数组(char* 数组)?