17

我有一个简单的 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”(双精度),所以如果我改变它,我可以有更高的精度。

4

9 回答 9

11

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

于 2009-07-03T11:37:37.530 回答
7

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

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

于 2009-02-19T20:24:19.143 回答
7

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

  • 如果您希望对非常大和非常小的数字进行数学运算,请不要使用浮点数或双精度数。
  • 如果您需要无限精度,请不要使用浮点数或双精度数。
  • 如果要聚合大量值,请不要使用 float 或 double (错误会自行加剧)。
  • 如果您需要速度和大小,请使用 float 或 double。

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

于 2009-02-19T21:00:03.907 回答
6

如果您省略所有 F 后缀(即-12.1代替-12.1F),您将获得更多位数的相等性。您的常量(尤其是预期值)现在是浮动的,因为F. 如果您是故意这样做的,请解释一下。

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

于 2009-02-19T20:31:22.217 回答
5

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

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

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

于 2009-02-19T20:31:52.777 回答
3

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

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

于 2009-02-19T20:21:48.273 回答
1

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

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

于 2009-02-19T20:25:11.573 回答
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));
}
于 2009-04-22T08:25:02.717 回答
0

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

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

于 2010-01-12T01:50:13.607 回答