Math.Round(double,decimal)总是返回一致的结果

时间:2009-12-02 11:25:28

标签: .net floating-point

当然,永远不应该比较相等计算产生的浮点值,但总是使用一个小容差,例如:

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

但是如果我使用Math.Round,我总是可以确定结果值是否一致,即即使舍入值是一个无法用双精确表示的值,下面的Assert总是会成功吗?

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位有效数字的反例。

  • 确定一系列值,其舍入结果始终为“一致”,如此处所定义(例如,舍入结果中有效数字的数字

  • 提供一种算法方法来生成反例。

2 个答案:

答案 0 :(得分:2)

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

我尝试了以下

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 =”“;”上放了一个断点当它进入这个部分时chcek,

并输入以下值

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

我认为这是您正在寻找的答案?

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

答案 1 :(得分:2)

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

浮点计算的问题在于,数学上给出相同结果的两个不同计算的结果(e.q。Sqrt(x)*Sqrt(x)==x)可能并且很可能因计算中的舍入误差而不同