如何将float转换为double(都存储在IEEE-754表示中)而不会丢失精度?

时间:2012-09-17 20:38:07

标签: 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 1010"

以IEEE-754双精度编码的相同数字

我尝试使用以下算法将第一个字符串首先转换回十进制数,但它会失去精确度。

num in decimal = (sign) * (1 + frac * 2^(-23)) * 2^(exp - 127)

我在Windows平台上使用Qt C ++ Framework。

编辑:我必须道歉,也许我没有明确表达的问题。 我的意思是我不知道真正的值23.85,我只得到第一个字符串,我想将其转换为双精度表示而不会导致精度损失。

5 个答案:

答案 0 :(得分:3)

好吧:保持符号位,重写指数(减去旧偏差,再加上新偏差),然后用右边的零填充尾数......

(正如@Mark所说,你必须分别处理一些特殊情况,即当偏差指数为零或最大时。)

答案 1 :(得分:2)

首先,+1用于识别二进制输入。

其次,这个数字不代表23.85,但略低。如果您将其最后一位二进制数字从0翻转到1,则该数字仍然不能准确地表示23.85,而是稍微多一点。这些差异不能在浮点数中充分捕获,但它们可以大致捕获一次。

第三,你正在失去的想法被称为准确性,而不是精确度。数字的精度总是通过从单精度到双精度的转换而增长,而精度永远不会通过转换得到改善(您的不准确数字仍然不准确,但额外的精度使其更加明显)。

我建议在显示(或记录)数字之前转换为浮点数或舍入或添加一个非常小的值,因为视觉外观是通过提高精度而真正丢失的。

在演员表演后立即拒绝回合并在后续计算中使用舍入值 - 这在循环中特别危险。虽然这似乎可以纠正调试器中的问题,但累积的额外不准确性可能会使最终结果更加失真。

答案 2 :(得分:2)

IEEE-754(和一般浮点)不能表示具有完全精度的周期性二进制小数。事实上,即使它们是具有相对较小的整数分子和分母的有理数。有些语言提供了可以做到的理性类型(它们也是支持无界精度整数的语言)。

因此,您发布的这两个数字不是相同的数字。

他们实际上是:

10111.11011001100110011000000000000000000000000000000000000000 ... 10111.11011001100110011001100110011001100110011001101000000000 ...

其中...代表0 s的无限序列。

上面评论中的Stephen Canon给出了相应的小数值(没有检查它们,但我没有理由怀疑他是否正确)。

因此,无法完成您想要进行的转换,因为单个精确数字不具备您需要的信息(您无需知道该数字是否实际上是周期性的,或者只是看起来像是因为恰好存在重复)。

答案 3 :(得分:1)

最简单的方法是将字符串转换为实际的float,将其转换为double,然后将其转换回字符串。

答案 4 :(得分:-1)

二进制浮点通常不能精确地表示小数部分值。从小数小数值到二进制浮点的转换(参见&#34; Bellerophon&#34;在&#34;如何准确读取浮点数&#34;由William D.Clinger提供)和二进制浮点回到十进制值(参见&#34; Dragon4&#34;在&#34;如何准确打印浮点数&#34;由Guy L.Steele Jr.和Jon L.White提供)预期结果,因为一个将十进制数转换为最接近的可表示的二进制浮点,另一个控制错误以知道它来自哪个十进制值(两种算法都在改进,并且在David Gay的dtoa.c中变得更实用。算法是从std::numeric_limits<T>::digits10类型中存储的浮点值恢复T十进制数字(除了可能是尾随零)的基础。

不幸的是,将值float扩展到double会破坏该值:尝试格式化新数字在很多情况下不会产生十进制原始值,因为float用零填充不同于Bellerophon最接近的double会创造,因此Dragon4期望。然而,基本上有两种方法运作得相当好:

  1. 有人建议将float转换为字符串,并将此字符串转换为double。这不是特别有效,但可以证明可以产生正确的结果(当然,假设正确实现了非完全无关的算法)。
  2. 假设您的值在合理范围内,您可以乘以10的幂,使最低有效十进制数字为非零,将此数字转换为整数,此整数转换为double,最后将得到的双倍除以10的原始幂。我没有证据证明这产生了正确的数字,但对于我感兴趣的价值范围以及我想要准确存储的价值范围。 float,这很有效。
  3. 避免这个完全问题的一种合理方法是首先使用十进制浮点值,如Decimal TR中对C ++的描述。不幸的是,这些还不是标准的一部分,但我已向C ++标准化委员会提交了一份提案,以便对此进行更改。