C#双重表示中浪费的指数位

时间:2017-05-19 16:34:52

标签: c# .net floating-point precision

我最近一直在研究.NET中的浮点双精度问题。在阅读Jon Skeet的文章Binary floating points and .NET时,我有一个问题。

让我们从文章中的46.428292315077示例开始。

表示为64位双精度,这相当于以下位:

Sign   Exponent       Mantissa
0      10000000100    0111001101101101001001001000010101110011000100100011

一位用于表示符号,11位用于表示指数,52位用于表示mantissa。注意双精度的偏差为1023(我假设是为了允许负指数 - 稍后会有更多内容)。

我的困惑在于代表指数的11位,以及它们对大数的使用(或缺乏),特别是double.MaxValue1.7976931348623157E+308)。

对于指数,文章中引用了一些特殊值来帮助确定数字的值。全零表示0;所有的代表NaN和正/负无穷大。有11位可以使用:指数的第一位是偏差,所以我们可以忽略它。这给了我们10位控制指数的实际大小。

double.MaxValue上的指数为308,可以用9位(100110100或偏差:10100110100)表示。最小的小数值为double.Epsilon4.94065645841247E-324),其指数仍可以9位(101000100或偏差:00101000100)表示。

您可能会注意到偏见后的第一位似乎总是浪费掉。我对负指数的假设是否正确?如果是这样,为什么偏差浪费后的第二位呢?无论如何,我们可以表示的实际最大数字(在尊重特殊值和偏差后可能的符号位)111111111(或基数为10 511 )。

如果偏差后的位实际上被浪费了,为什么我们不能用大于324的指数表示数字呢?我对此有何误解?

1 个答案:

答案 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/2005/01/17/fun-with-floating-point-arithmetic-part-three/

当您尝试比较不同精度级别的相等浮点数时会发生什么?

https://blogs.msdn.microsoft.com/ericlippert/2005/01/18/fun-with-floating-point-arithmetic-part-four/

标准算术的哪些属性在浮点中无法保存?

https://blogs.msdn.microsoft.com/ericlippert/2005/01/20/fun-with-floating-point-arithmetic-part-five/

如何用零表示无穷大和分裂?

https://blogs.msdn.microsoft.com/ericlippert/2009/10/15/as-timeless-as-infinity/