如何检测双精度的全部损失
本文关键字:双精度 全部 损失 检测 何检测 | 更新日期: 2023-09-27 17:54:38
当我运行以下代码时,我在两行上都打印了0:
Double a = 9.88131291682493E-324;
Double b = a*0.1D;
Console.WriteLine(b);
Console.WriteLine(BitConverter.DoubleToInt64Bits(b));
我希望得到Double。如果操作结果超出范围,则为NaN。结果是0。看起来,为了能够检测到这种情况,我必须检查:
- 操作前检查是否有操作数为零
- 操作完成后,如果两个操作数都不为零,检查结果是否为零。如果没有,就让它运行。如果为零,则赋值Double。NaN来表示它不是一个真正的零,它只是一个不能在这个变量中表示的结果。
这是相当笨拙的。有没有更好的办法?双。NaN的设计目的是什么?我假设某些操作必须返回它,设计师肯定没有把它放在那里以防万一?有可能这是BCL中的一个bug吗?(我知道不太可能,但是,这就是为什么我想了解那双。NaN应该可以工作)
顺便说一下,这个问题并不是针对double的。十进制显示的都是一样的:
Decimal a = 0.0000000000000000000000000001m;
Decimal b = a* 0.1m;
Console.WriteLine(b);
结果也是0
在我的例子中,我需要 double,因为我需要它们提供的范围(我正在进行概率计算),我不太担心精度。
我需要的是能够检测到我的结果何时停止意味着任何东西,即当计算值下降到如此之低时,它不能再用double表示。
是否有一种实用的方法来检测这个?
Double
完全按照浮点数规范IEEE 754工作。所以,不,这不是BCL中的错误-这只是IEEE 754浮点的工作方式。
decimal
,它是一个精确的十进制数,不像float
/double
。在浮点数中有一些特殊值,它们具有不同的含义:
- Infinity -例如
1f / 0f
. - - infinity -例如
-1f / 0f
. - NaN -例如
0f / 0f
或Math.Sqrt(-1)
然而,正如下面的评论者所指出的那样,虽然decimal
实际上检查溢出,但过于接近零的而不是被认为是溢出,就像浮点数一样。因此,如果您真的需要检查这一点,您将不得不创建自己的*
和/
方法。但是,对于十进制数,您不应该真正关心。
如果您需要这种乘法和除法的精度(也就是说,您希望除法可以通过乘法可逆),您可能应该使用有理数-两个整数(必要时可以使用大整数)。并且使用checked
上下文-这会在溢出时产生异常。
IEEE 754实际上确实处理了下溢。有两个问题:
- 返回值为0(或-1为负反回流)。设置了下流的异常标志,但是在。net中没有办法得到它。
- 这只发生在当你太接近零的精度损失。但你早在那之前就失去了大部分精确度。无论你拥有的"精确"数字是什么,都早已不复存在——操作是不可逆转的,而且它们也不精确。
所以如果你真的关心可逆性等,坚持用有理数。decimal
和double
都不能工作,c#与否。如果你不是那么精确,你就不应该关心溢出——只要选择最低的合理数字,并将低于该数字的任何东西声明为"无效";可能会确定你离实际的最大精度很远- double.Epsilon
显然没有帮助。
你只需要epsilon
。
这是一个"小数字",小到你不再感兴趣。
你可以使用:
double epsilon = 1E-50;
,当你的一个因子小于episln时,你就采取行动(例如把它当作0.0)