如何使用 Doubles 检测精度的完全损失?
How do I detect total loss of precision with Doubles?
当我 运行 以下代码时,我在两行上都打印了 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 的设计目的是什么?我假设某些操作必须有 return 它,设计师肯定没有把它放在那里以防万一吧?这可能是 BCL 中的错误吗? (我知道不太可能,但是,这就是为什么我想了解 Double.NaN 应该如何工作)
更新
顺便说一下,这个问题不是double特有的。 decimal 完全相同:
Decimal a = 0.0000000000000000000000000001m;
Decimal b = a* 0.1m;
Console.WriteLine(b);
这也给出零。
在我的例子中,我需要两倍,因为我需要他们提供的范围(我正在做概率计算)而且我不那么担心精度。
不过,我需要的是能够检测到我的结果何时停止意味着什么,也就是说,当计算将值降得太低以至于它不能再用双精度表示时。
是否有实用的检测方法?
Double
完全按照浮点数规范 IEEE 754 工作。所以不,这不是 BCL 中的错误 - 这只是 IEEE 754 浮点数的工作方式。
原因当然是浮动根本不是设计的目的。 相反,您可能想使用 decimal
,这是一个 精确 十进制数,与 float
/double
不同。
浮点数中有几个特殊值,意义不同:
- 无限 - 例如
1f / 0f
.
- -无穷大-例如
-1f / 0f
.
- NaN - 例如
0f / 0f
或 Math.Sqrt(-1)
然而,正如下面的评论者所指出的,虽然 decimal
实际上会检查溢出,但过于接近零 不会 被视为溢出,就像浮点数字。所以如果你真的需要检查这个,你将不得不制作自己的 *
和 /
方法。不过,对于十进制数字,您不必太在意。
如果您需要这种精度的乘法和除法(也就是说,您希望您的除法可以通过乘法反转),您应该改用有理数 - 两个整数(必要时使用大整数)。并使用 checked
上下文 - 这将在溢出时产生异常。
IEEE 754 实际上确实处理了下溢。有两个问题:
- return 值为 0(或 -1 表示负向回流)。设置了下溢的异常标志,但无法在 .NET 中获取它。
- 这只会在您太接近零时出现精度损失。但是在那之前很久你就失去了大部分的精确度。无论您拥有的 "precise" 数字早已不复存在 - 操作不可逆,而且不精确。
因此,如果您真的关心可逆性等,请坚持使用有理数。 decimal
和 double
都不起作用,C# 与否。如果你不那个精确,你不应该关心下溢 - 只需选择最低的合理数字,并将其下的任何内容声明为"invalid";可能确定您远离 实际 最大精度 - double.Epsilon
显然无济于事。
你只需要 epsilon
.
这是一个"small number",它太小了,所以你不再感兴趣了。
您可以使用:
double epsilon = 1E-50;
每当你的一个因素变得小于 epislon 时,你就会采取行动(例如将其视为 0.0)
当我 运行 以下代码时,我在两行上都打印了 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 的设计目的是什么?我假设某些操作必须有 return 它,设计师肯定没有把它放在那里以防万一吧?这可能是 BCL 中的错误吗? (我知道不太可能,但是,这就是为什么我想了解 Double.NaN 应该如何工作)
更新
顺便说一下,这个问题不是double特有的。 decimal 完全相同:
Decimal a = 0.0000000000000000000000000001m;
Decimal b = a* 0.1m;
Console.WriteLine(b);
这也给出零。
在我的例子中,我需要两倍,因为我需要他们提供的范围(我正在做概率计算)而且我不那么担心精度。
不过,我需要的是能够检测到我的结果何时停止意味着什么,也就是说,当计算将值降得太低以至于它不能再用双精度表示时。
是否有实用的检测方法?
Double
完全按照浮点数规范 IEEE 754 工作。所以不,这不是 BCL 中的错误 - 这只是 IEEE 754 浮点数的工作方式。
原因当然是浮动根本不是设计的目的。 相反,您可能想使用 decimal
,这是一个 精确 十进制数,与 float
/double
不同。
浮点数中有几个特殊值,意义不同:
- 无限 - 例如
1f / 0f
. - -无穷大-例如
-1f / 0f
. - NaN - 例如
0f / 0f
或Math.Sqrt(-1)
然而,正如下面的评论者所指出的,虽然 decimal
实际上会检查溢出,但过于接近零 不会 被视为溢出,就像浮点数字。所以如果你真的需要检查这个,你将不得不制作自己的 *
和 /
方法。不过,对于十进制数字,您不必太在意。
如果您需要这种精度的乘法和除法(也就是说,您希望您的除法可以通过乘法反转),您应该改用有理数 - 两个整数(必要时使用大整数)。并使用 checked
上下文 - 这将在溢出时产生异常。
IEEE 754 实际上确实处理了下溢。有两个问题:
- return 值为 0(或 -1 表示负向回流)。设置了下溢的异常标志,但无法在 .NET 中获取它。
- 这只会在您太接近零时出现精度损失。但是在那之前很久你就失去了大部分的精确度。无论您拥有的 "precise" 数字早已不复存在 - 操作不可逆,而且不精确。
因此,如果您真的关心可逆性等,请坚持使用有理数。 decimal
和 double
都不起作用,C# 与否。如果你不那个精确,你不应该关心下溢 - 只需选择最低的合理数字,并将其下的任何内容声明为"invalid";可能确定您远离 实际 最大精度 - double.Epsilon
显然无济于事。
你只需要 epsilon
.
这是一个"small number",它太小了,所以你不再感兴趣了。
您可以使用:
double epsilon = 1E-50;
每当你的一个因素变得小于 epislon 时,你就会采取行动(例如将其视为 0.0)