C++ 如何将float转换为double(都存储在IEEE-754表示中),而不丢失精度?
我的意思是,例如,我用IEEE-754单精度编码了以下数字:C++ 如何将float转换为double(都存储在IEEE-754表示中),而不丢失精度?,c++,qt,floating-point,double,ieee-754,C++,Qt,Floating Point,Double,Ieee 754,我的意思是,例如,我用IEEE-754单精度编码了以下数字: "0100 0001 1011 1110 1100 1100 1100 1100" (approximately 23.85 in decimal) 上面的二进制数存储在文本字符串中 问题是,如何将该字符串转换为IEEE-754双精度表示(有点像下面的一个,但值不同),而不丢失精度 "0100 0000 0011 0111 1101 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001
"0100 0001 1011 1110 1100 1100 1100 1100" (approximately 23.85 in decimal)
上面的二进制数存储在文本字符串中
问题是,如何将该字符串转换为IEEE-754双精度表示(有点像下面的一个,但值不同),而不丢失精度
"0100 0000 0011 0111 1101 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010"
这与IEEE-754双精度编码的数字相同
num in decimal = (sign) * (1 + frac * 2^(-23)) * 2^(exp - 127)
我曾尝试使用以下算法首先将第一个字符串转换回十进制数,但它失去了精度
num in decimal = (sign) * (1 + frac * 2^(-23)) * 2^(exp - 127)
<>我在Windows平台上使用Qt+C++框架。
编辑:我必须道歉,也许我没有把问题表达清楚。
我的意思是,我不知道真实值23.85,我只得到了第一个字符串,我想将其转换为双精度表示,而不损失精度。好的:保留符号位,重写指数(减去旧偏差,再加上新偏差),并在尾数的右边加上零
(正如@Mark所说,您必须单独处理一些特殊情况,即当有偏指数为零或最大值时)。将字符串转换为实际浮点,将其转换为双精度,然后将其转换回字符串可能是最简单的方法。首先,+1用于标识二进制输入 第二,这个数字并不代表23.85,但略低。如果将其最后一个二进制数字从
0
翻转到1
,则该数字仍不能准确表示23.85,但略高一些。这些差异无法在浮点中充分捕获,但可以在双精度浮点中大致捕获
第三,你认为你正在失去的是准确性,而不是精确性。数字的精度总是通过从单精度到双精度的转换而提高,而精度永远不会通过转换而提高(不准确的数字仍然不准确,但额外的精度使其更加明显)
我建议在显示(或记录)数字之前转换为浮点或四舍五入或添加一个非常小的值,因为增加精度确实会损失视觉外观
num in decimal = (sign) * (1 + frac * 2^(-23)) * 2^(exp - 127)
请抵制在强制转换后立即取整的诱惑,并在后续计算中使用取整的值-这在循环中尤其危险。虽然这似乎可以纠正调试器中的问题,但累积的额外误差可能会进一步扭曲最终结果。IEEE-754(以及一般的浮点)不能以全精度表示周期性二进制小数。事实上,即使它们是具有相对较小整数分子和分母的有理数,也不行。一些语言提供了一种可能做到这一点的rational类型(它们也是支持无界精度整数的语言)
因此,你发布的这两个数字不是同一个数字
事实上,它们是:
10111.110110011000000000000000000000000000000000。。。
10111.11011001100110000000000
其中,…
表示0
s的无限序列
Stephen Canon在上面的评论中给出了相应的十进制值(没有检查它们,但我没有理由怀疑他是否正确)
因此,您想要进行的转换无法完成,因为单精度数字没有您需要的信息(您无法知道该数字是否是周期性的,或者只是因为存在重复而看起来像周期性的)。一般来说,二进制浮点数不能,精确表示小数点的值。从小数到二进制浮点数的转换(参见William D.Clinger的“如何准确读取浮点数”中的“Bellerophon”)以及从二进制浮点数到小数的转换(参见Guy L.Steele Jr.和Jon L.White的“如何准确打印浮点数”中的“Dragon4”)产生预期的结果,因为一个将十进制数转换为最接近的可表示二进制浮点,另一个控制错误以知道它来自哪个十进制值(David Gay对这两种算法进行了改进,使其更加实用。这些算法是从存储在类型
T
中的浮点值恢复std::numeric\u limits::digits10
十进制数字(可能的尾随零除外)的基础
不幸的是,将float
扩展为double
会破坏该值:尝试格式化新数字在许多情况下不会产生十进制原始值,因为用零填充的float
与最近的double
Bellerophon将创建不同,因此,Dragon4期望然而,有两种方法相当有效:
float
转换为字符串,并将该字符串转换为double
。这不是特别有效,但可以证明能够产生正确的结果(当然,假设正确实现了不完全是琐碎的算法)双精度
,最后将得到的双精度除以原来的幂10。我没有证据证明这会产生正确的数字,但是对于我感兴趣并希望准确存储在float
中的值范围,这是可行的