.NET上的双精度问题

时间:2009-02-19 20:15:55

标签: c# .net math double

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

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

计算更高的数字,低于或等于“值”,即“步数”的倍数。但它缺乏精确性,如下面的测试所示:

[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);
}

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

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

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

9 个答案:

答案 0 :(得分:11)

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

答案 1 :(得分:8)

计算机上的浮点运算不是Exact Science :)。

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

答案 2 :(得分:6)

如果省略所有F后缀(即-12.1而不是-12.1F),您将获得更多相等的几位数。由于F,您的常量(尤其是预期值)现在是浮点数。如果您是故意这样做,请解释。

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

答案 3 :(得分:5)

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

  

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

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

答案 4 :(得分:5)

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

  • 如果你想在非常大和非常小的数字上做数学,不要使用float或double。
  • 如果您需要无限精度,请不要使用float或double。
  • 如果要汇总大量的值,请不要使用float或double(错误会自行复合)。
  • 如果您需要速度和尺寸,请使用float或double。

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

答案 5 :(得分:3)

检查此问题的答案:Is it safe to check floating point values for equality to 0?

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

答案 6 :(得分:1)

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

如果您不需要超过某一点的精度,那么也许十进制对您来说效果更好。它的精度高于双精度。

答案 7 :(得分:0)

对于类似的问题,我最终使用以下实现,这似乎是我测试用例的大部分成功(最多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));
}

答案 8 :(得分:0)

有时结果比您对严格要求的结果更精确:FP IEEE 754。 那是因为HW使用更多位进行计算。 请参阅C# specificationthis article

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