C# .NET 上的双精度问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/566958/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 08:28:42  来源:igfitidea点击:

Double precision problems on .NET

c#.netmathdouble

提问by Jader Dias

I have a simple C# function:

我有一个简单的 C# 函数:

public static double Floor(double value, double step)
{
    return Math.Floor(value / step) * step;
}

That calculates the higher number, lower than or equal to "value", that is multiple of "step". But it lacks precision, as seen in the following tests:

即计算更高的数字,小于或等于“值”,即“步长”的倍数。但它缺乏精度,如以下测试所示:

[TestMethod()]
public void FloorTest()
{
    int decimals = 6;
    double value = 5F;
    double step = 2F;
    double expected = 4F;
    double actual = Class.Floor(value, step);
    Assert.AreEqual(expected, actual);
    value = -11.5F;
    step = 1.1F;
    expected = -12.1F;
    actual = Class.Floor(value, step);
    Assert.AreEqual(Math.Round(expected, decimals),Math.Round(actual, decimals));
    Assert.AreEqual(expected, actual);
}

The first and second asserts are ok, but the third fails, because the result is only equal until the 6th decimal place. Why is that? Is there any way to correct this?

第一个和第二个断言没问题,但第三个断言失败,因为结果只等于小数点后第 6 位。这是为什么?有什么办法可以纠正这个问题吗?

UpdateIf I debug the test I see that the values are equal until the 8th decimal place instead of the 6th, maybe because Math.Round introduces some imprecision.

更新如果我调试测试,我会看到这些值直到小数点后第 8 位而不是第 6 位才相等,这可能是因为 Math.Round 引入了一些不精确性。

NoteIn my test code I wrote the "F" suffix (explicit float constant) where I meant "D" (double), so if I change that I can have more precision.

注意在我的测试代码中,我写了“F”后缀(显式浮点常量),其中我的意思是“D”(双精度),所以如果我改变它,我可以获得更高的精度。

采纳答案by Henk Holterman

If you omit all the F postfixes (ie -12.1instead of -12.1F) you will get equality to a few digits more. Your constants (and especially the expected values) are now floats because of the F. If you are doing that on purpose then please explain.

如果您省略所有 F 后缀(即-12.1代替-12.1F),您将获得多几个数字的相等性。由于F. 如果您是故意这样做的,请解释一下。

But for the rest i concur with the other answers on comparing double or float values for equality, it's just not reliable.

但是对于其余的我同意其他关于比较双精度或浮点值是否相等的答案,它只是不可靠。

回答by hometoast

Check the answers to this question: Is it safe to check floating point values for equality to 0?

检查这个问题的答案:检查浮点值是否等于 0 是否安全?

Really, just check for "within tolerance of..."

真的,只需检查“在……的容忍范围内”

回答by veggerby

Floating point arithmetic on computers are not Exact Science :).

计算机上的浮点运算不是精确科学:)。

If you want exact precision to a predefined number of decimals use Decimal instead of double or accept a minor interval.

如果您希望精确到预定义的小数位数,请使用 Decimal 而不是 double 或接受较小的间隔。

回答by Erik Funkenbusch

floats and doubles cannot accurately store all numbers. This is a limitation with the IEEE floating point system. In order to have faithful precision you need to use a more advanced math library.

浮点数和双精度数无法准确存储所有数字。这是 IEEE 浮点系统的限制。为了获得忠实的精度,您需要使用更高级的数学库。

If you don't need precision past a certain point, then perhaps decimal will work better for you. It has a higher precision than double.

如果您不需要超过某个点的精度,那么十进制可能更适合您。它具有比双精度更高的精度。

回答by Amy B

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

For example, the non-representability of 0.1 and 0.01 (in binary) means that the result of attempting to square 0.1 is neither 0.01 nor the representable number closest to it.

例如,0.1 和 0.01(二进制)的不可表示性意味着尝试平方 0.1 的结果既不是 0.01,也不是最接近它的可表示数。

Only use floating point if you want a machine's interpretation (binary) of number systems. You can't represent 10 cents.

如果您想要机器对数字系统的解释(二进制),请仅使用浮点数。你不能代表 10 美分。

回答by Michael Meadows

If you want precision, use System.Decimal. If you want speed, use System.Double (or System.Float). Floating point numbers are not "infinite precision" numbers, and therefore asserting equality must include a tolerance. As long as your numbers have a reasonable number of significant digits, this is ok.

如果您想要精度,请使用 System.Decimal。如果您想要速度,请使用 System.Double(或 System.Float)。浮点数不是“无限精度”数,因此断言相等必须包括容差。只要您的数字具有合理数量的有效数字,就可以了。

  • If you're looking to do math on very large AND very small numbers, don't use float or double.
  • If you need infinite precision, don't use float or double.
  • If you are aggregating a very large number of values, don't use float or double (the errors will compound themselves).
  • If you need speed and size, use float or double.
  • 如果您想对非常大和非常小的数字进行数学运算,请不要使用浮点数或双精度数。
  • 如果您需要无限精度,请不要使用 float 或 double。
  • 如果您正在聚合大量的值,请不要使用 float 或 double(错误会自己复合)。
  • 如果您需要速度和大小,请使用 float 或 double。

See thisanswer (also by me) for a detailed analysis of how precision affects the outcome of your mathematical operations.

有关精度如何影响数学运算结果的详细分析,请参阅答案(也是我的答案)。

回答by Seb

For the similar issue, I end up using the following implementation which seems to success most of my test case (up to 5 digit precision):

对于类似的问题,我最终使用了以下实现,这似乎成功了我的大部分测试用例(高达 5 位精度):

public static double roundValue(double rawValue, double valueTick)
{
    if (valueTick <= 0.0) return 0.0;

    Decimal val = new Decimal(rawValue);
    Decimal step = new Decimal(valueTick);
    Decimal modulo = Decimal.Round(Decimal.Divide(val,step));

    return Decimal.ToDouble(Decimal.Multiply(modulo, step));
}

回答by Jon Grant

I actually sort of wish they hadn't implemented the == operator for floats and doubles. It's almost always the wrong thing to do to ever ask if a double or a float is equal to any other value.

我实际上有点希望他们没有为浮点数和双精度数实现 == 运算符。询问 double 或 float 是否等于任何其他值几乎总是错误的。

回答by Pavel Savara

Sometimes the result is more precise than you would expect from strict:FP IEEE 754. That's because HW uses more bits for the computation. See C# specificationand this article

有时结果比您对 strict:FP IEEE 754 的预期更精确。那是因为 HW 使用更多位进行计算。请参阅C# 规范本文

Java has strictfp keyword and C++ have compiler switches. I miss that option in .NET

Java 有strictfp 关键字,C++ 有编译器开关。我想念 .NET 中的那个选项