巨大的printf浮点数/ windows / linux上的整数位数的双重差异

时间:2015-06-19 10:16:26

标签: c windows gcc floating-point printf

#include <float.h> 
#include <stdio.h> 

int main(int argc, char** argv) 
{ 
  printf("[0] %f\n", FLT_MAX); 
  printf("[1] %lf\n", FLT_MAX); 
  printf("[2] %Lf\n", FLT_MAX); // gcc warning: expects argument of type     ‘long double’ 
  printf("[3] %f\n", DBL_MAX); 
  printf("[4] %lf\n", DBL_MAX); 
  printf("[5] %Lf\n", DBL_MAX); // gcc warning: expects argument of type     ‘long double’ 

  //using C++ und std::numeric_limits<float/double>::max() gives same     results

  return 0; 
} 

Linux中: 64位 lsb_release -d打印“描述:Ubuntu 15.04” gcc --version打印“gcc(Ubuntu 4.9.2-10ubuntu13)4.9.2” ldd --version打印“ldd(Ubuntu GLIBC 2.21-0ubuntu4)2.21”

[0] 340282346638528859811704183484516925440.000000 
[1] 340282346638528859811704183484516925440.000000 
[2] --> warning-line disabled 
[3] 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000 
[4] 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000 
[5] --> warning-line disabled

Windows 7 x64: VS2010(最新版本10.0.40219.1 SP1Rel)Debug / Win32

[0] 340282346638528860000000000000000000000.000000 
[1] 340282346638528860000000000000000000000.000000 
[2] 340282346638528860000000000000000000000.000000
[3] 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000 
[4] 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000 
[5] 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000    

FLT_MAX的差异 VS2010:340282346638528860000000000000000000000.000000 GCC4.9.2:340282346638528859811704183484516925440.000000

是1.8829581651548307456e + 20(不是那么小) - 并且使用双打变得更大

更新:实际问题

有没有办法(只需要很少的代码更改)就可以在Linux和Windows(以及其他系统)上获得相同的结果,或者我是否需要在所有系统上使用相同的实现?我害怕在Windows / Linux / Linux-ARM / VxWorks / Solaris平台上实现自己的实现。

3 个答案:

答案 0 :(得分:2)

printf函数在这些平台上的实现方式不同。

看看这段代码:

#include <stdio.h>

int main()
{
    printf("%lf\n", ((double)1e100)/3);
    return 0;
}

用VC ++编译的这个程序给出了:

3333333333333333200000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000

虽然用g ++编译的同一程序给出了:

3333333333333333224453896013722304246165110619355184909726539264904319486405759542029132894851563520.000000

答案 1 :(得分:1)

平台之间的区别在于数字的打印方式,而不是数字本身。

您似乎误解了浮点数的工作原理。它们的准确性与它们的大小有关。数字的指数,其尾数值反映了幅度。尾数的大小是固定的,对于float,它是23位加一个隐含位。转换为十进制,这意味着您可以准确地表示大约七位有效十进制数字。

FLT_MAX约为3.40282346639e + 38。可以表示为float的下一个较小数字约为3.40282326356e + 38。这相差2.02824096037e + 31或比你认为的错误大10个数量级。

即使数字之间的明显差异似乎很大,两个打印值都更接近FLT_MAX而不是任何其他单精度浮点数,并将文本表示重新转换为'float { {1}} FLT_MAX`。

简而言之:should yield的两种实现都是有效的。

答案 2 :(得分:0)

  

有没有办法(只需要很少的代码更改)才能在Linux和Windows上获得相同的结果?

是的 - 主要是。

  1. 在Windows上使用gcc。通过“Windows”,当然OP指的是Visual Stdio编译器或相关产品。 gcc可在Windows,Linux和许多其他平台上使用,并且比OP的示例具有更一致的结果。它实际上是编译器问题,而不是 OS 问题。

  2. 使用base-2/16输出。

    printf("%a\n", FLT_MAX);
    // 0x1.fffffep+127  gcc 4.9.2
    // 0x1.fffffep+127  VS 2010
    
  3. 使用精度有限的"%.e"。 C规范仅为float指定最小精度为6位,对double指定10位精度,否则使用FLT_DIG/DBL_DIG

  4. 要修复2/3位指数,请参阅How to control the number of exponent digits ...visual studio _set_output_format
    请注意,"%.*e"中的精度字段是前导数字后面的位数,因此代码使用-1

        printf("%.*e\n", FLT_DIG - 1, FLT_MAX);
        // 3.40282e+38     gcc 4.9.2
        // 3.40282e+038    VS 2010
    
    1. 使用"%.e"精度更高但不过分。 FLT_DECIMAL_DIG/DBL_DECIMAL_DIG是要读回值并以相同float值结束的打印位数。使用更多数字打印会导致OP出现问题。考虑double:注意VS在OP的帖子中打印到17个正确舍入的有效数字。如果在VS中定义,那么DBL_DECIMAL_DIG将为17。 VS打印到17位数以保留“往返”数字。通过指导gcc打印到17位有效数字,我们得到相同的结果。

      #ifdef FLT_DECIMAL_DIG
        //  FLT_DECIMAL_DIG/DBL_DECIMAL_DIG typically not available in VS
        #define OP_FLT_Digs (FLT_DECIMAL_DIG)
        #define OP_DLB_Digs (DBL_DECIMAL_DIG)
      #else  
        #define OP_FLT_Digs (FLT_DIG + 3)
        #define OP_DBL_Digs (DBL_DIG + 2)
      #endif
      printf("%.*e\n", OP_FLT_Digs - 1, FLT_MAX);
      // 3.40282347e+38     gcc 4.9.2
      // 3.40282347e+038    VS 2010
      
    2. 有关"%.e"的更多信息。由于角落情况下扫描回数字将导致下一个 FP编号,因此不打印超过FLT_DECIMAL_DIG/DBL_DECIMAL_DIG个有效数字的值。基本上是一个双舍入问题 - 这个帖子有点深 - 所以没有细节。

    3. 当然,如果各种系统使用极为不同的FP格式,那么所有这些都没有用,long double很可能。精确的FP一致性很难,但上述内容肯定会有助于最大限度地减少差异。