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

joh*_*ose 0 .net c# floating-point precision

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

让我们从46.428292315077文章中的例子开始.

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

Sign   Exponent       Mantissa
0      10000000100    0111001101101101001001001000010101110011000100100011
Run Code Online (Sandbox Code Playgroud)

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

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

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

指数on double.MaxValue为308,可用9位(100110100或带偏差:)表示10100110100.最小的小数值是double.Epsilon(4.94065645841247E-324),其指数仍然可以用9位(101000100或带偏差:)表示00101000100.

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

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

Eri*_*ert 13

双重中没有浪费的比特.

让我们理解你的困惑.我们如何将比特的双重转化为数学值?让我们假设双重不是零,无穷大,负无穷大,NaN或非正规,因为这些都有特殊的规则.

你混淆的关键是将十进制数量与二进制数量混合在一起.对于这个答案,我将把所有二进制数量this formatting和十进制数量放在常规格式中.

我们采取的尾数的52位,我们把他们之后 1. 所以在你的榜样,这将是

1.0111001101101101001001001000010101110011000100100011
Run Code Online (Sandbox Code Playgroud)

那是二进制数.所以1 + 0/2 + 1/4 + 1/8 + 1/16 + 0/32 ......

然后我们取指数的11位,将其视为11位无符号整数,并从该值中减去1023.所以在你的例子中我们10000000100有无符号整数1028.减去1023,得到5.

现在我们将"小数位"(哈哈)移动5个位置:

101110.01101101101001001001000010101110011000100100011
Run Code Online (Sandbox Code Playgroud)

请注意,这相当于乘以2 5. 它不会乘以10 5!

现在,如果符号位是0,则将整个事物乘以1,如果符号位是,则将-1 乘以1.所以最后的答案是

101110.01101101101001001001000010101110011000100100011
Run Code Online (Sandbox Code Playgroud)

让我们看一个带负指数的例子.

假设指数已经存在01111111100.那是1020作为无符号整数.减去1023.我们得到-3,所以我们将向左移动三个位置,并获得:

0.0010111001101101101001001001000010101110011000100100011
Run Code Online (Sandbox Code Playgroud)

让我们看一个带有大指数的例子.如果指数已经11111111100怎么办?

解决它.那是十进制的2044.减去1023.那是1021.所以这个数字将是乘以1.01110011011011010010010010000101011100110001001000112 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/

Benford定律如何用于最小化表示错误:

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/