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

And*_*ykh 7 .net c# precision

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

Double a = 9.88131291682493E-324;
Double b = a*0.1D;
Console.WriteLine(b);
Console.WriteLine(BitConverter.DoubleToInt64Bits(b));
Run Code Online (Sandbox Code Playgroud)

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

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

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

更新

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

Decimal a = 0.0000000000000000000000000001m;
Decimal b =  a* 0.1m;
Console.WriteLine(b);
Run Code Online (Sandbox Code Playgroud)

这也给出了零.

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

我需要的是能够检测到我的结果何时停止意味着什么,也就是当计算将值降低到如此之低时,它就不能再用double表示.

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

Lua*_*aan 6

Double 完全按照浮点数规范IEEE 754工作.所以不,它不是BCL中的错误 - 它只是IEEE 754浮点工作的方式.

当然,原因在于它根本不是浮子的设计.相反,你可能想要使用decimal,这是一个精确的十进制数,不像float/ double.

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

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

但是,正如下面的评论者所指出的那样,decimal实际上确实检查了溢出,过于接近零不会被视为溢出,就像浮点数一样.所以,如果你真的需要检查这一点,你必须使自己*/方法.但是,对于十进制数字,您不应该非常关心.

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

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

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

因此,如果您真的关心可逆性等,请坚持理性数字.C#与否都decimal不会double起作用.如果你不精确,你不应该在乎反正下溢-只需选择最低的合理数量,并根据该为"无效"的任何声明; 可能你肯定远离实际的最大精度 - double.Epsilon显然不会有帮助.


DrK*_*och 5

你需要的只是epsilon.

这是一个"小数字",足够小,所以你不再感兴趣.

你可以使用:

double epsilon = 1E-50;
Run Code Online (Sandbox Code Playgroud)

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