如何检测双精度的全部损失

本文关键字:双精度 全部 损失 检测 何检测 | 更新日期: 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浮点的工作方式。

当然,原因是这根本不是float的设计目的。相反,您可能希望使用decimal,它是一个精确的十进制数,不像float/double

在浮点数中有一些特殊值,它们具有不同的含义:

  • Infinity -例如1f / 0f .
  • - infinity -例如-1f / 0f .
  • NaN -例如0f / 0fMath.Sqrt(-1)

然而,正如下面的评论者所指出的那样,虽然decimal实际上检查溢出,但过于接近零的而不是被认为是溢出,就像浮点数一样。因此,如果您真的需要检查这一点,您将不得不创建自己的*/方法。但是,对于十进制数,您不应该真正关心。

如果您需要这种乘法和除法的精度(也就是说,您希望除法可以通过乘法可逆),您可能应该使用有理数-两个整数(必要时可以使用大整数)。并且使用checked上下文-这会在溢出时产生异常。

IEEE 754实际上确实处理了下溢。有两个问题:

  • 返回值为0(或-1为负反回流)。设置了下流的异常标志,但是在。net中没有办法得到它。
  • 这只发生在当你太接近零的精度损失。但你早在那之前就失去了大部分精确度。无论你拥有的"精确"数字是什么,都早已不复存在——操作是不可逆转的,而且它们也不精确。

所以如果你真的关心可逆性等,坚持用有理数。decimaldouble都不能工作,c#与否。如果你不是那么精确,你就不应该关心溢出——只要选择最低的合理数字,并将低于该数字的任何东西声明为"无效";可能会确定你离实际的最大精度很远- double.Epsilon显然没有帮助。

你只需要epsilon

这是一个"小数字",小到你不再感兴趣。

你可以使用:

double epsilon = 1E-50;

,当你的一个因子小于episln时,你就采取行动(例如把它当作0.0)