我最近一直在研究.NET中的浮点双精度问题。在阅读Jon Skeet的文章Binary floating points and .NET时,我有一个问题。
让我们从文章中的46.428292315077
示例开始。
表示为64位双精度,这相当于以下位:
Sign Exponent Mantissa
0 10000000100 0111001101101101001001001000010101110011000100100011
一位用于表示符号,11位用于表示指数,52位用于表示mantissa。注意双精度的偏差为1023(我假设是为了允许负指数 - 稍后会有更多内容)。
我的困惑在于代表指数的11位,以及它们对大数的使用(或缺乏),特别是double.MaxValue
(1.7976931348623157E+308
)。
对于指数,文章中引用了一些特殊值来帮助确定数字的值。全零表示0;所有的代表NaN和正/负无穷大。有11位可以使用:指数的第一位是偏差,所以我们可以忽略它。这给了我们10位控制指数的实际大小。
double.MaxValue
上的指数为308,可以用9位(100110100
或偏差:10100110100
)表示。最小的小数值为double.Epsilon
(4.94065645841247E-324
),其指数仍可以9位(101000100
或偏差:00101000100
)表示。
您可能会注意到偏见后的第一位似乎总是浪费掉。我对负指数的假设是否正确?如果是这样,为什么偏差浪费后的第二位呢?无论如何,我们可以表示的实际最大数字(在尊重特殊值和偏差后可能的符号位)111111111
(或基数为10 511
)。
如果偏差后的位实际上被浪费了,为什么我们不能用大于324的指数表示数字呢?我对此有何误解?
答案 0 :(得分:13)
双重中没有浪费的比特。
让我们解决你的困惑。我们如何将比特的双重转化为数学值?让我们假设双重不是零,无穷大,负无穷大,NaN或非正规,因为这些都有特殊的规则。
你困惑的关键是将十进制数量与二进制数量混合在一起。对于这个答案,我将所有二进制数量放在this formatting
和十进制数量的常规格式中。
我们取尾数的52位,然后在 1.
之后将它们放在中。所以在你的例子中,那将是
1.0111001101101101001001001000010101110011000100100011
那是二进制数。所以1 + 0/2 + 1/4 + 1/8 + 1/16 + 0/32 ......
然后我们取指数的11位,将其视为11位无符号整数,并从该值中减去1023。所以在你的例子中我们有10000000100
,这是无符号整数1028.减去1023,我们得到5。
现在我们改变"小数位" (哈哈)5个地方:
101110.01101101101001001001000010101110011000100100011
请注意,这相当于乘以2 5 。 不是乘以10 5 !
现在,如果符号位为0
,我们将整数乘以1,如果符号位为1
则为-1。所以最后的答案是
101110.01101101101001001001000010101110011000100100011
让我们看一个带负指数的例子。
假设指数为01111111100
。那个1020是无符号整数。减去1023.我们得到-3,所以我们将向左移动三个位置,然后得到:
0.0010111001101101101001001001000010101110011000100100011
让我们看一个带有大指数的例子。如果指数为11111111100
怎么办?
解决它。这是2044的十进制数。减去1023.那就是1021.所以这个数字是将1.0111001101101101001001001000010101110011000100100011
乘以2 1021 时得到的极大数字。
因此,该double的值完全等于
32603055608669827528875188998863283395233949199438288081243712122350844851941321466156747022359800582932574058697506453751658312301708309704448596122037141141297743099124156580613023692715652869864010740666615694378079258090383719888417882332809291228958035810952632190230935024250237637887765563383983636480
大约是3.26030556 x 10 307 。
现在清楚了吗?
如果这个主题让你感兴趣,可以进一步阅读:
将double解码为其部分的代码:
https://ericlippert.com/2015/11/30/the-dedoublifier-part-one/
一个简单的任意精度理性:
https://ericlippert.com/2015/12/03/the-dedoublifier-part-two/
将双重转化为完全合理的代码:
https://ericlippert.com/2015/12/07/the-dedoublifier-part-three/
花车的代表:
https://blogs.msdn.microsoft.com/ericlippert/2005/01/10/floating-point-arithmetic-part-one/
如何使用本福德定律来最小化表示错误:
https://blogs.msdn.microsoft.com/ericlippert/2005/01/13/floating-point-and-benfords-law-part-two/
我们使用什么算法将浮点数显示为十进制数?
当您尝试比较不同精度级别的相等浮点数时会发生什么?
标准算术的哪些属性在浮点中无法保存?
如何用零表示无穷大和分裂?
https://blogs.msdn.microsoft.com/ericlippert/2009/10/15/as-timeless-as-infinity/