user-0 提问: 关于float或者double的精度问题

回复:
user: user-1
content: 计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步对应汇编一行！  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！

user: user-1
content: 引用 13 楼 dale999 的回复:[quote=引用 11 楼 mymtom 的回复:] [quote=引用 9 楼 dale999 的回复:]  学习了，之前没接触过这方面的东西。不过你这段代码在我那儿运行，貌似没法设置精度方向呢？运行成功，fesetround函数执行也成功，但是结果不对，不知道怎么回事 我是在Linux下运行的！[/quote] 我在WINDOWS 和 LINUX下运行都不对……[/quote] 啥叫对？

user: user-0
content: 引用 11 楼 mymtom 的回复:[quote=引用 9 楼 dale999 的回复:]  学习了，之前没接触过这方面的东西。不过你这段代码在我那儿运行，貌似没法设置精度方向呢？运行成功，fesetround函数执行也成功，但是结果不对，不知道怎么回事 我是在Linux下运行的！[/quote] 我在WINDOWS 和 LINUX下运行都不对……

user: user-2
content: 我觉得这个跟 编译器的优化有关   http://msdn.microsoft.com/en-us/library/aa289157%28VS.71%29.aspx  int b1=a*3 给优化成了 b1 = 1.0 \ 3 * 3  VS下面的优化选项：

user: user-3
content: 引用 9 楼 dale999 的回复:学习了，之前没接触过这方面的东西。不过你这段代码在我那儿运行，貌似没法设置精度方向呢？运行成功，fesetround函数执行也成功，但是结果不对，不知道怎么回事 我是在Linux下运行的！

user: user-1
content: feclearexcept, fegetexceptflag, feraiseexcept, fesetexceptflag, fetestexcept, fegetenv, fegetround, feholdexcept, fesetround, fesetenv, feupdateenv - C99 floating point rounding and exception handling

user: user-0
content: 引用 8 楼 mymtom 的回复:这个与系统的默认浮点环境设置有关系，但是可以根据需要设置。   /*-  * Copyright (C), 1988-2013 mymtom  *  * vi:set ts=4 sw=4:  */ #ifndef lint static const char rcsid[] = "$Id$"; #endif /* not lint */  /**  * @file    rnd.c  * @brief     */  #pragma STDC FENV_ACCESS ON #include <fenv.h>  #include <stdio.h>  static void func1(void); static void func2(void); static void func3(void); static void func4(void);  int main(int argc, char *argv[]) {      func1();     func2();     func3();     func4();      return 0; }  static void func1(void) {     fesetround(FE_DOWNWARD);     double a=1.0/3;     printf("%f\n",a);     int b1=a*3,b2=0.333333*3;     printf("%d,%d\n",b1,b2); } static void func2(void) {     fesetround(FE_TONEAREST);     double a=1.0/3;     printf("%f\n",a);     int b1=a*3,b2=0.333333*3;     printf("%d,%d\n",b1,b2); } static void func3(void) {     fesetround(FE_TOWARDZERO);     double a=1.0/3;     printf("%f\n",a);     int b1=a*3,b2=0.333333*3;     printf("%d,%d\n",b1,b2); } static void func4(void) {     fesetround(FE_UPWARD);     double a=1.0/3;     printf("%f\n",a);     int b1=a*3,b2=0.333333*3;     printf("%d,%d\n",b1,b2); }  /* 输出 0.333333 0,0 0.333333 1,0 0.333333 0,0 0.333333 1,0  */   学习了，之前没接触过这方面的东西。不过你这段代码在我那儿运行，貌似没法设置精度方向呢？运行成功，fesetround函数执行也成功，但是结果不对，不知道怎么回事

user: user-3
content: 这个与系统的默认浮点环境设置有关系，但是可以根据需要设置。   /*-  * Copyright (C), 1988-2013 mymtom  *  * vi:set ts=4 sw=4:  */ #ifndef lint static const char rcsid[] = "$Id$"; #endif /* not lint */  /**  * @file    rnd.c  * @brief     */  #pragma STDC FENV_ACCESS ON #include <fenv.h>  #include <stdio.h>  static void func1(void); static void func2(void); static void func3(void); static void func4(void);  int main(int argc, char *argv[]) {      func1();     func2();     func3();     func4();      return 0; }  static void func1(void) {     fesetround(FE_DOWNWARD);     double a=1.0/3;     printf("%f\n",a);     int b1=a*3,b2=0.333333*3;     printf("%d,%d\n",b1,b2); } static void func2(void) {     fesetround(FE_TONEAREST);     double a=1.0/3;     printf("%f\n",a);     int b1=a*3,b2=0.333333*3;     printf("%d,%d\n",b1,b2); } static void func3(void) {     fesetround(FE_TOWARDZERO);     double a=1.0/3;     printf("%f\n",a);     int b1=a*3,b2=0.333333*3;     printf("%d,%d\n",b1,b2); } static void func4(void) {     fesetround(FE_UPWARD);     double a=1.0/3;     printf("%f\n",a);     int b1=a*3,b2=0.333333*3;     printf("%d,%d\n",b1,b2); }  /* 输出 0.333333 0,0 0.333333 1,0 0.333333 0,0 0.333333 1,0  */

user: user-0
content: 引用 4 楼 zhao4zhong1 的回复:用10进制小数不能精确表示某些三进制小数0.1(3)=0.33333333333……(10) 同理，用二进制小数也不能精确表示某些10进制小数。 include\float.h ... #define DBL_DIG         15                      /* # of decimal digits of precision */ #define DBL_EPSILON     2.2204460492503131e-016 /* smallest such that 1.0+DBL_EPSILON != 1.0 */ #define DBL_MANT_DIG    53                      /* # of bits in mantissa */ #define DBL_MAX         1.7976931348623158e+308 /* max value */ #define DBL_MAX_10_EXP  308                     /* max decimal exponent */ #define DBL_MAX_EXP     1024                    /* max binary exponent */ #define DBL_MIN         2.2250738585072014e-308 /* min positive value */ #define DBL_MIN_10_EXP  (-307)                  /* min decimal exponent */ #define DBL_MIN_EXP     (-1021)                 /* min binary exponent */ #define _DBL_RADIX      2                       /* exponent radix */ #define _DBL_ROUNDS     1                       /* addition rounding: near */  #define FLT_DIG         6                       /* # of decimal digits of precision */ #define FLT_EPSILON     1.192092896e-07F        /* smallest such that 1.0+FLT_EPSILON != 1.0 */ #define FLT_GUARD       0 #define FLT_MANT_DIG    24                      /* # of bits in mantissa */ #define FLT_MAX         3.402823466e+38F        /* max value */ #define FLT_MAX_10_EXP  38                      /* max decimal exponent */ #define FLT_MAX_EXP     128                     /* max binary exponent */ #define FLT_MIN         1.175494351e-38F        /* min positive value */ #define FLT_MIN_10_EXP  (-37)                   /* min decimal exponent */ #define FLT_MIN_EXP     (-125)                  /* min binary exponent */ #define FLT_NORMALIZE   0 #define FLT_RADIX       2                       /* exponent radix */ #define FLT_ROUNDS      1                       /* addition rounding: near */ ...  嗯，不能精确表示可以从float类型的存储方式可以看出来嘛~但是像Windows的处理方式，a的值实际已经是小于1/3了，然后乘3赋给b，值返回仍然是1，不编译器是怎么处理的……

user: user-0
content: 引用 1 楼 user-5 的回复:这个怎么感觉是gcc的bug？ 修改成这样：    double a=1.0/3;  	double a2=a*3;     printf("%lf,%lf\n",a,a2);    int b1=a2,b2=0.333333*3;     printf("%d,%d\n",b1,b2); gcc和vs结果相同，都是1，0  其实我就没搞清楚，乘回去等1的情况，编译器怎么知道之前是1/3……

user: user-4
content: 两者的结果都是正确的。

user: user-5
content: 这个怎么感觉是gcc的bug？ 修改成这样：    double a=1.0/3;  	double a2=a*3;     printf("%lf,%lf\n",a,a2);    int b1=a2,b2=0.333333*3;     printf("%d,%d\n",b1,b2); gcc和vs结果相同，都是1，0

user: user-1
content: 用10进制小数不能精确表示某些三进制小数0.1(3)=0.33333333333……(10) 同理，用二进制小数也不能精确表示某些10进制小数。 include\float.h ... #define DBL_DIG         15                      /* # of decimal digits of precision */ #define DBL_EPSILON     2.2204460492503131e-016 /* smallest such that 1.0+DBL_EPSILON != 1.0 */ #define DBL_MANT_DIG    53                      /* # of bits in mantissa */ #define DBL_MAX         1.7976931348623158e+308 /* max value */ #define DBL_MAX_10_EXP  308                     /* max decimal exponent */ #define DBL_MAX_EXP     1024                    /* max binary exponent */ #define DBL_MIN         2.2250738585072014e-308 /* min positive value */ #define DBL_MIN_10_EXP  (-307)                  /* min decimal exponent */ #define DBL_MIN_EXP     (-1021)                 /* min binary exponent */ #define _DBL_RADIX      2                       /* exponent radix */ #define _DBL_ROUNDS     1                       /* addition rounding: near */  #define FLT_DIG         6                       /* # of decimal digits of precision */ #define FLT_EPSILON     1.192092896e-07F        /* smallest such that 1.0+FLT_EPSILON != 1.0 */ #define FLT_GUARD       0 #define FLT_MANT_DIG    24                      /* # of bits in mantissa */ #define FLT_MAX         3.402823466e+38F        /* max value */ #define FLT_MAX_10_EXP  38                      /* max decimal exponent */ #define FLT_MAX_EXP     128                     /* max binary exponent */ #define FLT_MIN         1.175494351e-38F        /* min positive value */ #define FLT_MIN_10_EXP  (-37)                   /* min decimal exponent */ #define FLT_MIN_EXP     (-125)                  /* min binary exponent */ #define FLT_NORMALIZE   0 #define FLT_RADIX       2                       /* exponent radix */ #define FLT_ROUNDS      1                       /* addition rounding: near */ ...

user: user-1
content: 常量也有类型！ C++ Floating-Point Constants Floating-point constants specify values that must have a fractional part. These values contain decimal points (.) and can contain exponents.  Syntax  floating-constant :  fractional-constant exponent-partopt floating-suffixopt digit-sequence exponent-part floating-suffixopt  fractional-constant :  digit-sequenceopt . digit-sequence digit-sequence .  exponent-part :  e signopt digit-sequence E signopt digit-sequence  sign : one of  + –  digit-sequence :  digit digit-sequence digit  floating-suffix :one of  f l F L  Floating-point constants have a “mantissa,” which specifies the value of the number, an “exponent,” which specifies the magnitude of the number, and an optional suffix that specifies the constant’s type. The mantissa is specified as a sequence of digits followed by a period, followed by an optional sequence of digits representing the fractional part of the number. For example:  18.46 38.  The exponent, if present, specifies the magnitude of the number as a power of 10, as shown in the following example:  18.46e0      // 18.46 18.46e1      // 184.6  If an exponent is present, the trailing decimal point is unnecessary in whole numbers such as 18E0.  Floating-point constants default to type double. By using the suffixes f or l (or F or L — the suffix is not case sensitive), the constant can be specified as float or long double, respectively.  Although long double and double have the same representation, they are not the same type. For example, you can have overloaded functions like  void func( double );  and   void func( long double );  C++ Integer Constants Integer constants are constant data elements that have no fractional parts or exponents. They always begin with a digit. You can specify integer constants in decimal, octal, or hexadecimal form. They can specify signed or unsigned types and long or short types.  Syntax  integer-constant :  decimal-constant integer-suffixopt octal-constant integer-suffixopt hexadecimal-constant integer-suffixopt 'c-char-sequence'  decimal-constant :  nonzero-digit decimal-constant digit  octal-constant :  0 octal-constant octal-digit  hexadecimal-constant :  0x hexadecimal-digit 0X hexadecimal-digit hexadecimal-constant hexadecimal-digit  nonzero-digit : one of  1 2 3 4 5 6 7 8 9  octal-digit : one of  0 1 2 3 4 5 6 7  hexadecimal-digit : one of  0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F  integer-suffix :  unsigned-suffix long-suffixopt long-suffix unsigned-suffixopt  unsigned-suffix : one of  u U  long-suffix : one of  l L  64-bit integer-suffix :  i64  To specify integer constants using octal or hexadecimal notation, use a prefix that denotes the base. To specify an integer constant of a given integral type, use a suffix that denotes the type.  To specify a decimal constant, begin the specification with a nonzero digit. For example:  int i = 157;   // Decimal constant int j = 0198;  // Not a decimal number; erroneous octal constant int k = 0365;  // Leading zero specifies octal constant, not decimal  To specify an octal constant, begin the specification with 0, followed by a sequence of digits in the range 0 through 7. The digits 8 and 9 are errors in specifying an octal constant. For example:  int i = 0377;   // Octal constant int j = 0397;   // Error: 9 is not an octal digit  To specify a hexadecimal constant, begin the specification with 0x or 0X (the case of the “x” does not matter), followed by a sequence of digits in the range 0 through 9 and a (or A) through f (or F). Hexadecimal digits a (or A) through f (or F) represent values in the range 10 through 15. For example:  int i = 0x3fff;   // Hexadecimal constant int j = 0X3FFF;   // Equal to i  To specify an unsigned type, use either the u or U suffix. To specify a long type, use either the l or L suffix. For example:  unsigned uVal = 328u;             // Unsigned value long lVal = 0x7FFFFFL;            // Long value specified                                    //  as hex constant unsigned long ulVal = 0776745ul;  // Unsigned long value