如何使用双打检测完全精度损失?

时间:2015-06-09 08:35:44

标签: c# .net precision

当我运行以下代码时,我在两行上都打印出0:

Double a = 9.88131291682493E-324;
Double b = a*0.1D;
Console.WriteLine(b);
Console.WriteLine(BitConverter.DoubleToInt64Bits(b));

如果操作结果超出范围,我希望得到Double.NaN。相反,我得到0.它看起来能够检测到这种情况发生时我必须检查:

  • 在操作之前检查是否有任何操作数为零
  • 操作后,如果两个操作数都不为零,则检查结果是否为零。如果不让它运行。如果它为零,则为其指定Double.NaN以表示它不是真的为零,它只是一个无法在此变量中表示的结果。

这相当笨拙。有没有更好的办法? Double.NaN的目的是什么?我假设某些操作必须归还它,当然设计师并没有把它放在那里以防万一?这可能是BCL中的一个错误吗? (我知道不太可能,但是,这就是为什么我想了解Double.NaN应该如何工作的原因)

更新

顺便说一句,这个问题不是双重的。十进制暴露它们都是一样的:

Decimal a = 0.0000000000000000000000000001m;
Decimal b =  a* 0.1m;
Console.WriteLine(b);

这也给零。

在我的情况下,我需要加倍,因为我需要他们提供的范围(我正在进行概率计算)并且我并不担心精度。

我需要的是能够检测到我的结果何时停止意味着什么,也就是说当计算值降低到这么低时,它就不能再用双倍表示。

有没有实用的方法来检测这个?

2 个答案:

答案 0 :(得分:6)

Double完全符合浮点数规范IEEE 754.所以不,它不是BCL中的错误 - 它只是IEEE 754浮点的工作方式。 / p>

当然,原因在于它根本不是浮子的设计。 相反,您可能希望使用decimal,这是精确十进制数,与float / double不同。

浮点数中有一些特殊值,含义不同:

  • 无限 - 例如1f / 0f
  • - 无限 - 例如-1f / 0f
  • NaN - 例如0f / 0fMath.Sqrt(-1)

但是,正如下面的评论者指出的那样,虽然decimal实际上确实检查了溢出,但是过于接近零是被认为是溢出,就像浮点数一样。因此,如果您确实需要检查此内容,则必须制作自己的*/方法。但是,对于十进制数字,您不应该非常关心。

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

事实上,IEEE 754确实可以处理下溢。有两个问题:

  • 返回值为0(对于负向不流,则为-1)。设置了下溢的异常标志,但是没有办法在.NET中获得它。
  • 仅当您太接近零时才会出现精度损失。但是在此之前很久你就失去了大部分精确的方法。无论什么"精确"你所拥有的数字早已消失 - 操作不可逆,而且不准确。

因此,如果您真的关心可逆性等,请坚持理性数字。 decimaldouble都不起作用,C#也不起作用。如果您 ,那么您无论如何都不应该关心下溢 - 只需选择最低的合理数字,并在其下面声明任何内容,因为"无效&#34 ;;可以肯定你远离实际的最大精度 - double.Epsilon显然无济于事。

答案 1 :(得分:5)

您需要的只是epsilon

这是一个“小数字”,足够小,所以你不再感兴趣了。

您可以使用:

double epsilon = 1E-50;

每当你的一个因素变得比epislon小时,你就会采取行动(例如将其视为0.0)