我知道有很多方法可以使用书面库来读取IEEE 754浮点数的每一位。
我不希望这样,我希望能够手动将十进制浮点数转换为基于IEEE 754的二进制表示。
我理解IEEE 754是如何工作的,我只是想尝试应用它。
我在这里问这个问题只是想看看我的方式是正常的还是愚蠢的,我也想知道PC是如何快速完成的。
如果我在字符串中给出了十进制浮点数,我需要弄清楚 E 是什么以及 M 是什么。
获取两部分:整数部分i
和部分f
。
处理f
。我经常multiple 2
得到整数部分(0或1)并删除整数部分然后重复,直到它变为0
将i
转换为位。这很容易我只是mod 2
和div 2
来获取i
的所有位。
例如,转换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
。
现在,我有i
和f
的位。
如果i
的所有位都为0,那么在f
的位上,根据1
的{{1}},我将它移位直到第一个default hidden 1
消失。 }}。我得到M
,然后考虑M
的基线当然给出转移到E的值。
如果E
不为0,那么我连接两个位部分并计算我需要做多少shift_right才能使连接位为1,然后将此值赋给i
我猜我的所有步骤都没有错。但我觉得很麻烦。
有一种简单而干净的方式吗?
PC是如何做到的?
答案 0 :(得分:2)
我不明白你对这一部分的处理。如图所示,您正在进行小数分数算术,这将给出正确的结果,但会引入自己的实现困难。进行二元分数算术将取决于将分数转换为二进制分数,以便将其转换为二进制分数。
我认为完全使用二进制整数可能更简单,但你仍然需要一个扩展形式,比如BigInteger。
为此,首先记下小数点后的位数D
。将十进制数字字符串转换为整数N
,忽略小数点。值为N/10**D
,使用“**”表示功率。将10**D
计算为二进制整数。
通过二进制长除法计算N/10**D
,当结果中有F+2
个有效位时停止,其中F
是浮点格式的小数位数。请注意此结果中二进制点的位置。
如果数字在正常范围内,则不会使用最重要的一位。要正确向下舍入到F
小数位,您需要F+2
位的最低有效位,将其称为G
,并且还需要零位S
,如果,并且只有在余数为零的情况下。如果G
为0,则使用F
小数位不变。如果G
和S
都是一个,则需要进行整理。如果G
为1且S
为零,则确切的结果是两个可表示值之间的中间值,您应该舍入为偶数。
在处理由于向上舍入导致的任何进位后,从最高有效位相对于二进制点的位置计算指数。如果指数在范围内,那么你就完成了。如果它太位,则返回相应符号的无穷大。如果它太小,你需要反规范化。要获得正确的舍入,请从您要删除的位和G
的旧值重新计算S
和S
。
答案 1 :(得分:2)
查看Frama-C中的文件src/lib/floating_point.ml
和src/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
...