代码之家  ›  专栏  ›  技术社区  ›  to StackOverflow

Math.Round(双精度,十进制)是否总是返回一致的结果

  •  4
  • to StackOverflow  · 技术社区  · 15 年前

    当然,绝对不应比较计算得出的浮点值是否相等,而应始终使用较小的公差,例如:

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

    但是,如果我使用Math.Round,我是否可以始终确保结果值是一致的,即,即使舍入的值不能用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);
    

    然而,我也对一个更数学的解释感兴趣。对于能够执行以下任一操作的任何更具数学性的StackOverflower,可获得额外的向上投票:

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

    • 确定四舍五入结果适用的值范围

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

    2 回复  |  直到 7 年前
        1
  •  2
  •   Adriaan Stander    15 年前

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

    我尝试了以下方法

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

    它输入了以下值

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

    我想这就是你想要的答案?

    如果没有,请让我知道,这样我可以测试更多。

        2
  •  2
  •   Grizzly    15 年前

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

    Sqrt(x)*Sqrt(x)==x )由于计算中的舍入误差,可能会和很可能会有所不同