1
double a = 135.24;          // a is set to 135.24000000000001 actually
double b = Math.Round(a, 0);    // set to 135.0
double c = Math.Round(a, 1);    // set to 135.19999999999999
double d = Math.Round(a, 2);    // set to 135.24000000000001 
double e = Math.Round(a, 3);    // set to 135.24000000000001 
double f = Math.Round(a, 4);    // set to 135.24000000000001 
double g = Math.Round(a, 5);    // set to 135.24000000000001 
double h = Math.Round(a, 10);   // set to 135.24000000000001 
double i = Math.Round(a, 14);   // set to 135.24000000000001 


double j = Math.Round(a, 2
 , MidpointRounding.AwayFromZero ); // set to 135.24000000000001 
double k = Math.Round(a, 2
 , MidpointRounding.ToEven );   // set to 135.24000000000001 

太棒了,这意味着 135.24 不能用双精度表示,对吧?

4

5 回答 5

5

是的,135.24 不能用 double 表示,因为 double 使用二进制指数表示法。

即:135.24 可以以 2 为底以指数形式表示为 1.0565625 * 128 = ( 1 + 1/32 + 1/64 + 1/128 + 1/1024 + ... ) * (2**7)。

无法准确表示,因为 13524 不除以 5。让我们看一下:

135.24 = 13524/(10**2)

表示是有限的<=>存在整个 x 和 n 满足135.24 = x/(2**n)

135.24 = x/(2**n)
13524 / (10**2) = x / (2**n)
13524 * (2**n) = (10**2) * x
13524 * (2**n) = 2*2*5*5 * x

左边没有“5”,所以不能做(称为算术基本定理)

通常,只有在十进制数的素数分解中有足够数量的“五”时,有限二进制表示才是准确的。

现在有趣的部分:

    double delta = 0.5;
    while( 1 + delta > 1 )
        delta /= 2;

    Console.WriteLine( delta );

double 的精度在 1 附近不同,在 0 附近不同,对于一些大数字也不同。维基百科上的一些二进制表示示例:双精度浮点格式

但最重要的是,内部处理器浮点堆栈的精度可能比 8 字节(双精度)高得多。如果不必将数字传输到 RAM 并减少到 8 个字节,我们可以获得非常好的精度。

在不同的处理器(AMD、Intel)、语言(C、C++、C#、Java)或编译器优化级别上测试类似的东西可以得到大约 1e-16、1e-20 甚至 1e-320 的结果

看看 CIL / assembler / jasmin code 看看到底发生了什么(例如:对于 C++g++ -S test.cpp创建test.s包含汇编代码的文件)

于 2013-11-07T13:32:57.813 回答
4

这通常是浮点数的问题。如果您需要数字的精确表示(例如,用于计费,...),那么您应该使用 Decimal。试试下面的代码,你会发现你没有输出 0, 0.1, 0.2, ... 1.0。

for(double i = 0; i <= 1.0; i += 0.001)
{
    Console.WriteLine(i);
}
于 2013-11-07T10:27:50.423 回答
3

尝试改用小数。浮点数不是很精确(因此无法表示某些数字):)

于 2013-11-07T10:26:18.677 回答
0

是的,它不能。这就是为什么还有另一种非整数数据类型称为decimal. 它需要不同的内存量,并且具有不同的最小/最大数值范围double,并且不可位转换*) 加倍,但反过来它可以精确地保存数字而不会出现任何失真。

*) 也就是说,您不能将其复制为字节并推送到 C++ 代码。但是,您仍然cast可以将其翻倍并返回。请注意,演员阵容不会精确,因为double不能容纳一些decimal可以的数字,反之亦然

于 2013-11-07T10:26:33.277 回答
0

您可以看到定义。 Round 函数定义为-

public static double Round(double value, int digits, MidpointRounding mode)
    {
      if (digits < 0 || digits > 15)
        throw new ArgumentOutOfRangeException("digits", Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));
      if (mode >= MidpointRounding.ToEven && mode <= MidpointRounding.AwayFromZero)
        return Math.InternalRound(value, digits, mode);
      throw new ArgumentException(Environment.GetResourceString("Argument_InvalidEnumValue", (object) mode, (object) "MidpointRounding"), "mode");
    }


private static unsafe double InternalRound(double value, int digits, MidpointRounding mode)
    {
      if (Math.Abs(value) < Math.doubleRoundLimit)
      {
        double num1 = Math.roundPower10Double[digits];
        value *= num1;
        if (mode == MidpointRounding.AwayFromZero)
        {
          double num2 = Math.SplitFractionDouble(&value);
          if (Math.Abs(num2) >= 0.5)
            value += (double) Math.Sign(num2);
        }
        else
          value = Math.Round(value);
        value /= num1;
      }
      return value;
    }
于 2013-11-07T10:33:35.663 回答