最简单的方法是基于IEEE 754手动将十进制浮点数转换为位表示,而不使用任何库

时间:2014-04-09 12:07:19

标签: algorithm floating-point bit-manipulation ieee-754

我知道有很多方法可以使用书面库来读取IEEE 754浮点数的每一位。

我不希望这样,我希望能够手动将十进制浮点数转换为基于IEEE 754的二进制表示。

我理解IEEE 754是如何工作的,我只是想尝试应用它。

我在这里问这个问题只是想看看我的方式是正常的还是愚蠢的,我也想知道PC是如何快速完成的。


如果我在字符串中给出了十进制浮点数,我需要弄清楚 E 是什么以及 M 是什么。

  1. 获取两部分:整数部分i和部分f

  2. 处理f。我经常multiple 2得到整数部分(0或1)并删除整数部分然后重复,直到它变为0

  3. i转换为位。这很容易我只是mod 2div 2来获取i的所有位。

  4. 例如,转换f部分

    0.390625 * 2 = 0.78125 0
    0.78125 * 2 = 1.5625 1
    0.5625 * 2 = 1.125 1
    0.125 * 2 = 0.25 0
    0.25 * 2 = 0.5 0
    0.5 * 2 = 1 1
    0

    在这种情况下,0.390625的临时位是0 1 1 0 0 1


    现在,我有if的位。

    如果i的所有位都为0,那么在f的位上,根据1的{​​{1}},我将它移位直到第一个default hidden 1消失。 }}。我得到M,然后考虑M的基线当然给出转移到E的值。

    如果E不为0,那么我连接两个位部分并计算我需要做多少shift_right才能使连接位为1,然后将此值赋给i


    我猜我的所有步骤都没有错。但我觉得很麻烦。

    有一种简单而干净的方式吗?

    PC是如何做到的?

2 个答案:

答案 0 :(得分:2)

我不明白你对这一部分的处理。如图所示,您正在进行小数分数算术,这将给出正确的结果,但会引入自己的实现困难。进行二元分数算术将取决于将分数转换为二进制分数,以便将其转换为二进制分数。

我认为完全使用二进制整数可能更简单,但你仍然需要一个扩展形式,比如BigInteger。

为此,首先记下小数点后的位数D。将十进制数字字符串转换为整数N,忽略小数点。值为N/10**D,使用“**”表示功率。将10**D计算为二进制整数。

通过二进制长除法计算N/10**D,当结果中有F+2个有效位时停止,其中F是浮点格式的小数位数。请注意此结果中二进制点的位置。

如果数字在正常范围内,则不会使用最重要的一位。要正确向下舍入到F小数位,您需要F+2位的最低有效位,将其称为G,并且还需要零位S,如果,并且只有在余数为零的情况下。如果G为0,则使用F小数位不变。如果GS都是一个,则需要进行整理。如果G为1且S为零,则确切的结果是两个可表示值之间的中间值,您应该舍入为偶数。

在处理由于向上舍入导致的任何进位后,从最高有效位相对于二进制点的位置计算指数。如果指数在范围内,那么你就完成了。如果它太位,则返回相应符号的无穷大。如果它太小,你需要反规范化。要获得正确的舍入,请从您要删除的位和G的旧值重新计算SS

答案 1 :(得分:2)

查看Frama-C中的文件src/lib/floating_point.mlsrc/lib/floating_point.mli。它们实现了从十进制表示到浮点的单精度和双精度转换(由于double rounding问题,你不能从后者获得前者),没有任何外部库。这些文件由LGPL 2.1涵盖。此实现是从this one开始并继续this one的几篇博客文章的主题。

这可能接近于最简单的转换函数,就像编写这个函数一样,我没有性能限制,只希望保持代码尽可能简单和正确,而不需要依赖现有的库例如MPFR。

...
type parsed_float = {
  f_nearest : float ;
  f_lower : float ;
  f_upper : float ;
}

val single_precision_of_string: string -> parsed_float
val double_precision_of_string: string -> parsed_float
...