我有一个简单的 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);
}

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

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

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

有帮助吗?

解决方案

如果您省略所有在F后缀(即-12.1而不是-12.1F),您将得到平等对待的几个数字了。你常数(特别是预期值)现在是浮因为F的。如果你正在做的是故意那么请解释一下。

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

其他提示

其实我倒希望他们没有实现的==操作符和双精度浮点数。这几乎总是错的事情来问过,如果一个双或浮动等于任何其他值。

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

如果要准确精度小数预定数量的使用小数,而不是双或接受一个小的时间间隔。

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

  

例如,0.1和0.01(二进制)的非表示性意味着试图正方形0.1的结果既不是0.01也不最靠近它的表示数。

只有当你想数系统的机器的解释(二)使用浮点。你不能代表10美分。

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

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

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

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

真的,只需检查“在……的公差范围内”

浮点型和双不能准确地存储的所有数字。这与IEEE浮点系统的限制。为了有忠实的精度,你需要使用更先进的数学库。

如果您不需要精密过去的某一个点的话,或许会小数为你工作好。它具有比双精度更高。

有关的类似问题,我最终会使用以下实现这似乎最成功我的测试用例(最多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));
}

有时,严格的结果比您预期的更精确:FP IEEE 754。这是因为 HW 使用更多位进行计算。看 C#规范本文

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top