C#十进制乘法奇怪的行为

时间:2013-08-01 10:40:48

标签: c# decimal multiplication

在C#中乘以十进制值时,我注意到了一种奇怪的行为。请考虑以下乘法运算:

1.1111111111111111111111111111m * 1m = 1.1111111111111111111111111111 // OK
1.1111111111111111111111111111m * 2m = 2.2222222222222222222222222222 // OK
1.1111111111111111111111111111m * 3m = 3.3333333333333333333333333333 // OK
1.1111111111111111111111111111m * 4m = 4.4444444444444444444444444444 // OK
1.1111111111111111111111111111m * 5m = 5.5555555555555555555555555555 // OK
1.1111111111111111111111111111m * 6m = 6.6666666666666666666666666666 // OK
1.1111111111111111111111111111m * 7m = 7.7777777777777777777777777777 // OK
1.1111111111111111111111111111m * 8m = 8.888888888888888888888888889  // Why not 8.8888888888888888888888888888 ?
1.1111111111111111111111111111m * 9m = 10.000000000000000000000000000 // Why not 9.9999999999999999999999999999 ?

我无法理解的是上述最后两个案例。怎么可能?

2 个答案:

答案 0 :(得分:73)

decimal存储28或29位有效数字(96位)。基本上尾数在 - / + 79,228,162,514,264,337,593,543,950,335范围内。

这意味着最多约7.9 .... 你可以准确地得到29位有效数字 - 但是高于你不能。这就是8和9都出错的原因,但不是早期的价值观。一般情况下,你应该只依赖 28个有效数字,以避免像这样的奇怪情况。

将原始输入减少到28位有效数字后,您将获得预期的输出:

using System;

class Test
{
    static void Main()
    {
        var input = 1.111111111111111111111111111m;
        for (int i = 1; i < 10; i++)
        {
            decimal output = input * (decimal) i;
            Console.WriteLine(output);
        }
    }
}

答案 1 :(得分:2)

数学家区分有理数和超集实数。对有理数的算术运算是明确定义和精确的。算术(使用加法,减法,乘法和除法的运算符)对于实数是“精确的”,只是在无理数以无理形式(符号)或可能在某些表达式中转换为有理数的程度上。例如,2的平方根没有小数(或任何其他有理基)表示。然而,两个的平方根乘以2的平方根是理性的 - 2,显然。

计算机及其上运行的语言通常只实现有理数 - 隐藏在诸如int,long int,float,double precision,real(FORTRAN)或其他一些暗示实数的名称后面。但是包含的有理数是有限的,不像有理数的范围是无限的。

琐碎的例子 - 在计算机上找不到。 1/2 * 1/2 = 1/4如果您有一个有理数的类并且分子和分母的大小不超过整数运算的限制,那么它可以正常工作。所以(1,2)*(1,2) - > (1,4)

但是如果可用的有理数是十进制且限制在十进制之后的单个数字 - 不切实际 - 但代表在选择近似理性(浮点数/实数等)数字的实现时所做的选择,那么1/2可以完全转换为0.5,然后0.5 + 0.5等于1.0,但0.5 * 0.5必须是0.2或0.3!