为何不加f的时候累加的0.2会有那么大的精度误差?

handsomekyle 2014-04-09 04:52:55

#include <stdio.h>
int main(void)
{
double x=0;

while (x < 2.0 )
{
printf("%.15f\n", x);
x=x+0.2;
}
printf("\n\n");
x = 0;
while (x < 2.0 )
{
printf("%.15f\n", x);
x=x+0.2f;
}
return 0;
}


...全文
242 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2014-04-10
  • 打赏
  • 举报
回复
不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实! 有人说一套做一套,你相信他说的还是相信他做的? 其实严格来说这个世界上古往今来所有人都是说一套做一套,不是吗? 不要写连自己也预测不了结果的代码!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float f;
double d;
void showf() {
    char bs[65];
    char b[65];
    unsigned char *p;
    char e[12];
    char *t;
    int ex;

    printf("\nf=%.6f\n",f);
    p=(unsigned char *)&f;
    printf("hex=%02X %02X %02X %02X\n",p[3],p[2],p[1],p[0]);
    ltoa(*(long *)&f,b,2);
    sprintf(bs,"%032s",b);
    printf("bin=%s\n",bs);
    printf("bin=%.1s %.8s   %s\n",bs,bs+1,bs+9);
    strncpy(e,bs+1,8);e[8]=0;
    ex=strtol(e,&t,2);
    printf("    %c %-4d-127 1.%s\n",(bs[0]=='0')?'+':'-',ex,bs+9);
    ex-=127;
    printf("    %c %-8d 1.%s\n",(bs[0]=='0')?'+':'-',ex,bs+9);
}
void showd() {
    char bs[65];
    char b[65];
    unsigned char *p;
    char e[12];
    char *t;
    int ex;

    printf("\nd=%.15lf\n",d);
    p=(unsigned char *)&d;
    printf("hex=%02X %02X %02X %02X %02X %02X %02X %02X\n",p[7],p[6],p[5],p[4],p[3],p[2],p[1],p[0]);
    _i64toa(*(__int64 *)&d,b,2);
    sprintf(bs,"%064s",b);
    printf("bin=%s\n",bs);
    printf("bin=%.1s %.11s   %s\n",bs,bs+1,bs+12);
    strncpy(e,bs+1,11);e[11]=0;
    ex=strtol(e,&t,2);
    printf("    %c %-6d-1023 1.%s\n",(bs[0]=='0')?'+':'-',ex,bs+12);
    ex-=1023;
    printf("    %c %-11d 1.%s\n",(bs[0]=='0')?'+':'-',ex,bs+12);
}
int main() {

    for (f=0.0f;f<=2.2f;f+=0.2f) showf();
    printf("------------\n");
    f=2.0f;showf();
    printf("-----------------------------------\n");
    for (d=0.0 ;d<=2.2 ;d+=0.2 ) showd();
    printf("------------\n");
    d=2.0 ;showd();

    return 0;
}
//
//f=0.000000
//hex=00 00 00 00
//bin=00000000000000000000000000000000
//bin=0 00000000   00000000000000000000000
//  + 0   -127 1.00000000000000000000000
//  + -127     1.00000000000000000000000
//
//f=0.200000
//hex=3E 4C CC CD
//bin=00111110010011001100110011001101
//bin=0 01111100   10011001100110011001101
//  + 124 -127 1.10011001100110011001101
//  + -3       1.10011001100110011001101
//
//f=0.400000
//hex=3E CC CC CD
//bin=00111110110011001100110011001101
//bin=0 01111101   10011001100110011001101
//  + 125 -127 1.10011001100110011001101
//  + -2       1.10011001100110011001101
//
//f=0.600000
//hex=3F 19 99 9A
//bin=00111111000110011001100110011010
//bin=0 01111110   00110011001100110011010
//  + 126 -127 1.00110011001100110011010
//  + -1       1.00110011001100110011010
//
//f=0.800000
//hex=3F 4C CC CD
//bin=00111111010011001100110011001101
//bin=0 01111110   10011001100110011001101
//  + 126 -127 1.10011001100110011001101
//  + -1       1.10011001100110011001101
//
//f=1.000000
//hex=3F 80 00 00
//bin=00111111100000000000000000000000
//bin=0 01111111   00000000000000000000000
//  + 127 -127 1.00000000000000000000000
//  + 0        1.00000000000000000000000
//
//f=1.200000
//hex=3F 99 99 9A
//bin=00111111100110011001100110011010
//bin=0 01111111   00110011001100110011010
//  + 127 -127 1.00110011001100110011010
//  + 0        1.00110011001100110011010
//
//f=1.400000
//hex=3F B3 33 34
//bin=00111111101100110011001100110100
//bin=0 01111111   01100110011001100110100
//  + 127 -127 1.01100110011001100110100
//  + 0        1.01100110011001100110100
//
//f=1.600000
//hex=3F CC CC CE
//bin=00111111110011001100110011001110
//bin=0 01111111   10011001100110011001110
//  + 127 -127 1.10011001100110011001110
//  + 0        1.10011001100110011001110
//
//f=1.800000
//hex=3F E6 66 68
//bin=00111111111001100110011001101000
//bin=0 01111111   11001100110011001101000
//  + 127 -127 1.11001100110011001101000
//  + 0        1.11001100110011001101000
//
//f=2.000000
//hex=40 00 00 01
//bin=01000000000000000000000000000001
//bin=0 10000000   00000000000000000000001
//  + 128 -127 1.00000000000000000000001
//  + 1        1.00000000000000000000001
//------------
//
//f=2.000000
//hex=40 00 00 00
//bin=01000000000000000000000000000000
//bin=0 10000000   00000000000000000000000
//  + 128 -127 1.00000000000000000000000
//  + 1        1.00000000000000000000000
//-----------------------------------
//
//d=0.000000000000000
//hex=00 00 00 00 00 00 00 00
//bin=0000000000000000000000000000000000000000000000000000000000000000
//bin=0 00000000000   0000000000000000000000000000000000000000000000000000
//  + 0     -1023 1.0000000000000000000000000000000000000000000000000000
//  + -1023       1.0000000000000000000000000000000000000000000000000000
//
//d=0.200000000000000
//hex=3F C9 99 99 99 99 99 9A
//bin=0011111111001001100110011001100110011001100110011001100110011010
//bin=0 01111111100   1001100110011001100110011001100110011001100110011010
//  + 1020  -1023 1.1001100110011001100110011001100110011001100110011010
//  + -3          1.1001100110011001100110011001100110011001100110011010
//
//d=0.400000000000000
//hex=3F D9 99 99 99 99 99 9A
//bin=0011111111011001100110011001100110011001100110011001100110011010
//bin=0 01111111101   1001100110011001100110011001100110011001100110011010
//  + 1021  -1023 1.1001100110011001100110011001100110011001100110011010
//  + -2          1.1001100110011001100110011001100110011001100110011010
//
//d=0.600000000000000
//hex=3F E3 33 33 33 33 33 34
//bin=0011111111100011001100110011001100110011001100110011001100110100
//bin=0 01111111110   0011001100110011001100110011001100110011001100110100
//  + 1022  -1023 1.0011001100110011001100110011001100110011001100110100
//  + -1          1.0011001100110011001100110011001100110011001100110100
//
//d=0.800000000000000
//hex=3F E9 99 99 99 99 99 9A
//bin=0011111111101001100110011001100110011001100110011001100110011010
//bin=0 01111111110   1001100110011001100110011001100110011001100110011010
//  + 1022  -1023 1.1001100110011001100110011001100110011001100110011010
//  + -1          1.1001100110011001100110011001100110011001100110011010
//
//d=1.000000000000000
//hex=3F F0 00 00 00 00 00 00
//bin=0011111111110000000000000000000000000000000000000000000000000000
//bin=0 01111111111   0000000000000000000000000000000000000000000000000000
//  + 1023  -1023 1.0000000000000000000000000000000000000000000000000000
//  + 0           1.0000000000000000000000000000000000000000000000000000
//
//d=1.200000000000000
//hex=3F F3 33 33 33 33 33 33
//bin=0011111111110011001100110011001100110011001100110011001100110011
//bin=0 01111111111   0011001100110011001100110011001100110011001100110011
//  + 1023  -1023 1.0011001100110011001100110011001100110011001100110011
//  + 0           1.0011001100110011001100110011001100110011001100110011
//
//d=1.400000000000000
//hex=3F F6 66 66 66 66 66 66
//bin=0011111111110110011001100110011001100110011001100110011001100110
//bin=0 01111111111   0110011001100110011001100110011001100110011001100110
//  + 1023  -1023 1.0110011001100110011001100110011001100110011001100110
//  + 0           1.0110011001100110011001100110011001100110011001100110
//
//d=1.600000000000000
//hex=3F F9 99 99 99 99 99 99
//bin=0011111111111001100110011001100110011001100110011001100110011001
//bin=0 01111111111   1001100110011001100110011001100110011001100110011001
//  + 1023  -1023 1.1001100110011001100110011001100110011001100110011001
//  + 0           1.1001100110011001100110011001100110011001100110011001
//
//d=1.800000000000000
//hex=3F FC CC CC CC CC CC CC
//bin=0011111111111100110011001100110011001100110011001100110011001100
//bin=0 01111111111   1100110011001100110011001100110011001100110011001100
//  + 1023  -1023 1.1100110011001100110011001100110011001100110011001100
//  + 0           1.1100110011001100110011001100110011001100110011001100
//
//d=2.000000000000000
//hex=3F FF FF FF FF FF FF FF
//bin=0011111111111111111111111111111111111111111111111111111111111111
//bin=0 01111111111   1111111111111111111111111111111111111111111111111111
//  + 1023  -1023 1.1111111111111111111111111111111111111111111111111111
//  + 0           1.1111111111111111111111111111111111111111111111111111
//
//d=2.200000000000000
//hex=40 01 99 99 99 99 99 99
//bin=0100000000000001100110011001100110011001100110011001100110011001
//bin=0 10000000000   0001100110011001100110011001100110011001100110011001
//  + 1024  -1023 1.0001100110011001100110011001100110011001100110011001
//  + 1           1.0001100110011001100110011001100110011001100110011001
//------------
//
//d=2.000000000000000
//hex=40 00 00 00 00 00 00 00
//bin=0100000000000000000000000000000000000000000000000000000000000000
//bin=0 10000000000   0000000000000000000000000000000000000000000000000000
//  + 1024  -1023 1.0000000000000000000000000000000000000000000000000000
//  + 1           1.0000000000000000000000000000000000000000000000000000
//
handsomekyle 2014-04-10
  • 打赏
  • 举报
回复
引用 14 楼 RisJazz 的回复:
吾晓得侬港萨了。 最后一次循环。 当x加到 2.0的时候, 为什么 x < 2.0 判断为真。 浮点数计算有精度损失,在工程项目中,遇到判断浮点数边界的时候,一般会加一个精度阈值。 要判断一个浮点数是否小于 2.0 ,一般这么写: const double THRESHOLD = 1.0e-10; // 判断数值精确到10的-10次方。 if (x < 2.0 + THRESHOLD)
你的意思我明白了。 还是浮点数在计算时的精度损失导致了一系列误差的出现。 我纠结的是: 1. 为何精度损失是会变小 2. 双精度精确到小数点后15位,10的-10次方是小数点后10位,这个计算时的精度损失到底是怎么一种逻辑和机制导致精度损失,而且小到小数点后10位到15位之间呢? 感谢你的解惑:-)
lpcads 2014-04-09
  • 打赏
  • 举报
回复
引用 13 楼 handsomekyle 的回复:
[quote=引用 3 楼 zhao4zhong1 的回复:] http://bbs.csdn.net/topics/390676437
计算机小数并不能完全精确地表示十进制小数,但是在判断的时候或者计算的时候,立即数2.0和0.2为step累加到2.0两个数的区别在哪里呢?为何会比立即数小呢?[/quote] 2.0可以精确表示 0.2不行
可爱的小灵儿 2014-04-09
  • 打赏
  • 举报
回复
吾晓得侬港萨了。 最后一次循环。 当x加到 2.0的时候, 为什么 x < 2.0 判断为真。 浮点数计算有精度损失,在工程项目中,遇到判断浮点数边界的时候,一般会加一个精度阈值。 要判断一个浮点数是否小于 2.0 ,一般这么写: const double THRESHOLD = 1.0e-10; // 判断数值精确到10的-10次方。 if (x < 2.0 + THRESHOLD)
handsomekyle 2014-04-09
  • 打赏
  • 举报
回复
引用 3 楼 zhao4zhong1 的回复:
http://bbs.csdn.net/topics/390676437
计算机小数并不能完全精确地表示十进制小数,但是在判断的时候或者计算的时候,立即数2.0和0.2为step累加到2.0两个数的区别在哪里呢?为何会比立即数小呢?
可爱的小灵儿 2014-04-09
  • 打赏
  • 举报
回复
引用 10 楼 handsomekyle 的回复:
加f导致强制转换的问题倒明白,关键是如果不加f的话,都是double型,却会打印出2.000000是为何呢?
呃,你是说小数点后几位吗?

double x = 2.0;
printf("%.1\n", x);        // 打印小数点后一位。
printf("%.15\n", x);      // 打印小数点后15位。
printf("%5.15\n", x);     // 打印小数点前5位,小数点后15位。
handsomekyle 2014-04-09
  • 打赏
  • 举报
回复
引用 7 楼 zhao4zhong1 的回复:
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
赵老师,你发的这些好像没有讲到,为什么我不加f的时候,while判断的时候却判断出了2.0也给打印出来了。。
handsomekyle 2014-04-09
  • 打赏
  • 举报
回复
引用 9 楼 RisJazz 的回复:
立即数写成 2.0 默认是double类型, 一般是64位浮点数。 立即数写成 2.0f 默认是float类型, 一般是32位浮点数。 你的变量x是double类型的,执行 x = x + 0.2f 的时候,会将float型的0.2f 强转为double类型的 0.2, 32位转64位, 少了32位的数据,该怎么办呢? 这是一个未定义的行为,高级一点的系统会将缺少的32位数据全部置0,但大多数系统会保留后的32位存储地址内的随机值。 简单来说 0.2f 是float类型, 强转为double类型会出现精度损失。 0.2 是 double类型,与x类型一致,没有出现精度损失。
加f导致强制转换的问题倒明白,关键是如果不加f的话,都是double型,却会打印出2.000000是为何呢?
可爱的小灵儿 2014-04-09
  • 打赏
  • 举报
回复
立即数写成 2.0 默认是double类型, 一般是64位浮点数。 立即数写成 2.0f 默认是float类型, 一般是32位浮点数。 你的变量x是double类型的,执行 x = x + 0.2f 的时候,会将float型的0.2f 强转为double类型的 0.2, 32位转64位, 少了32位的数据,该怎么办呢? 这是一个未定义的行为,高级一点的系统会将缺少的32位数据全部置0,但大多数系统会保留后的32位存储地址内的随机值。 简单来说 0.2f 是float类型, 强转为double类型会出现精度损失。 0.2 是 double类型,与x类型一致,没有出现精度损失。
Mindly123 2014-04-09
  • 打赏
  • 举报
回复
太专业级的问题了,顶一下
赵4老师 2014-04-09
  • 打赏
  • 举报
回复
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
赵4老师 2014-04-09
  • 打赏
  • 举报
回复
常量也有类型。 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 );
luciferisnotsatan 2014-04-09
  • 打赏
  • 举报
回复
x=x+0.2f; 不加f,编译器对0.2是使用double型的,加了告诉编译器,这是个float。然后你的x是double,float的0.2要转为double,误差就更大了。
赵4老师 2014-04-09
  • 打赏
  • 举报
回复
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 */
...
赵4老师 2014-04-09
  • 打赏
  • 举报
回复
bichir 2014-04-09
  • 打赏
  • 举报
回复
因为0.2默认类型就是double类型,而double类型的精确度可以在15位,所以前面那个不会出现精度问题。后面那个你显示给0.2设成了float,而float的精度在7、8之间,所以过了7位或8位后面的就会不精确。当你用double+floatj时,系统会自动给你把float转成double这样就造成7、8位乱显数字问题。所以现在了以上结果。
翊麾校尉 2014-04-09
  • 打赏
  • 举报
回复
不加后缀f的浮点型字面值一般表示的是double类型,加 f 的表示单精度类型,单精度类型只保证6个有效位数,所以后边出现了一串没有意义的数字

69,382

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧