【问题标题】:Does Math.Round(double, decimal) always return consistent resultsMath.Round(double, decimal) 是否总是返回一致的结果
【发布时间】:2009-12-02 11:25:27
【问题描述】:

当然,永远不应该比较由相等计算得出的浮点值,但始终使用较小的容差,例如:

double value1 = ... 
double value2 = ...
if (Math.Abs(value1 - value2) < tolerance * Math.Abs(value1))
{
    ... values are close enough
}

但是,如果我使用 Math.Round,我是否可以始终确保结果值是一致的,即以下 Assert 始终会成功,即使舍入的值是一个不能用 double 精确表示的值?

public static void TestRound(double value1, double value2, int decimals)
{
    double roundedValue1 = Math.Round(value1, decimals);
    double roundedValue2 = Math.Round(value2, decimals);

    string format = "N" + decimals.ToString();
    if (roundedValue1.ToString(format) == roundedValue2.ToString(format))
    {
        // They rounded to the same value, was the rounding exact?
        Debug.Assert(roundedValue1 == roundedValue2);
    }
}

如果不是请举个反例。

编辑

感谢astander 提供了一个由蛮力生成的反例,证明结果在一般情况下不是“一致的”。这个反例在四舍五入的结果中有 16 位有效数字 - 在按比例缩放时它也会以同样的方式失败:

        double value1 = 10546080000034341D;
        double value2 = 10546080000034257D;
        int decimals = 0;
        TestRound(value1, value2, decimals);

不过,我也对更数学的解释感兴趣。对任何可以执行以下任何操作的数学化程度更高的 Stackoverflowers 给予奖励:

  • 找到一个反例,其中舍入结果的有效数字少于 16 个。

  • 确定舍入结果始终为此处定义的“一致”的值的范围(例如,舍入结果中有效位数小于 N 的所有值) .

  • 提供生成反例的算法方法。

【问题讨论】:

标签: .net floating-point


【解决方案1】:

好的,这似乎是一个非常技术性的问题,所以我认为蛮力可能会告诉我们。

我尝试了以下

public static void TestRound(double value1, double value2, int decimals)
{
    double roundedValue1 = Math.Round(value1, decimals);
    double roundedValue2 = Math.Round(value2, decimals);

    string format = "N" + decimals.ToString();
    if (roundedValue1.ToString(format) == roundedValue2.ToString(format))
    {
        // They rounded to the same value, was the rounding exact?
        if (roundedValue1 != roundedValue2)
        {
            string s = "";
        }
    }
}
private void button1_Click(object sender, EventArgs e)
{
    for (double d = 0, inc = .000001; d < 1000; d += inc)
        for (int p = 0; p <= 15; p++)
            TestRound(Math.Pow(Math.Pow(d, inc), 1 / inc), d, p);
}

我在 "string s = "";" 上放了一个断点进入本节时检查,

它输入了以下值

value1 = 1.0546080000034341
value2 = 1.0546080000034257
decimals = 15
roundedValue1 = 1.0546080000034339
roundedValue2 = 1.0546080000034259
roundedValue1.ToString(format) = 1.054608000003430
roundedValue2.ToString(format) = 1.054608000003430

我认为这就是您要寻找的答案?

如果没有,请告诉我,以便我进行更多测试。

【讨论】:

  • 是我,还是ToString("N15") 的缺陷比Round 的缺陷多?这些双精度数四舍五入到小数点后 15 位的不同值,但 ToString("N15") 将它们四舍五入到小数点后 14 位加零。
【解决方案2】:

虽然浮点计算的精度有限,因此不精确,但它们是确定性的。因此,如果您以相同的顺序对相同的值使用相同的计算,则应该始终得到相同的结果。所以对相同的值使用相同的舍入方法会得到相同的结果。

浮点计算的问题在于,数学上给出相同结果的两个不同计算的结果(例如Sqrt(x)*Sqrt(x)==x)可能并且很可能由于计算内部的舍入误差而不同

【讨论】:

  • 当然,但我说的是对两个 不同 值使用相同的舍入方法,这将舍入到“相同”的结果。请参阅 asstander 的回答。
猜你喜欢
  • 2011-02-02
  • 1970-01-01
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 2016-05-18
  • 2017-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多