双重和舍入很难,但这很疯狂

Tor*_*sen 1 c# math rounding-error

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 
Run Code Online (Sandbox Code Playgroud)

Sooooo,这意味着135.24无法用双精度表示,对吧?

use*_*016 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
Run Code Online (Sandbox Code Playgroud)

左侧没有"5",所以无法完成(称为算术的基本定理)

通常,只有在十进制数的素数因子分解中有足够数量的"五"时,有限二进制表示才是精确的.

现在有趣的部分:

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

    Console.WriteLine( delta );
Run Code Online (Sandbox Code Playgroud)

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

但最重要的是,内部处理器浮点栈可以有很多更好的精度超过8个字节(双).如果数字不必传输到RAM并且被剥离到8个字节,我们可以获得非常好的精度.

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

看一下CIL /汇编程序/ jasmin代码,看看究竟发生了什么(例如:对于C++ g++ -S test.cpp创建test.s带有汇编程序代码的文件)