c语言中浮点数怎么判断是否合法(深入理解浮点数在值域范围内表示大整数的舍掉和舍入位的问题)

看以下实例:

// 有问题的版本 #include <stdio.h> int main() { Float sum = 0.0f; for (int i = 0; i < 10000; i ) sum = i 1; printf("Sum of 1~10000: %f\n", sum); printf("Sum of 1~10000: %f\n",10001*5000.0); getchar(); return 0; } /* output: Sum of 1~10000: 50002896.000000 Sum of 1~10000: 50005000.000000 */

我们知道,float使用1个符号位,8个阶码和23个尾码。

阶码采取移码,E = e 2^(8-1)-1

其值域有:

Float(IEEE754) 32 -3.4028E 38 3.4028E 38

为什么会有这个情况呢?

问题出在尾数只有23位,当其全部用来表示整数时,只能表示32位,也就是2^24-12 = 16777215

当大于这个数字时,就存在丢弃和舍入位的可能。

c语言中浮点数怎么判断是否合法(深入理解浮点数在值域范围内表示大整数的舍掉和舍入位的问题)(1)

看累加到多少开始有精度损失:

#include <stdio.h> #define MM 5793 int main() { float sum = 0.0f; for (int i = 0; i < MM; i ) { sum = i 1; printf("%d %f\n",i,sum); } printf("Sum of 1~%d: %f\n",MM,sum); printf("Sum of 1~%d: %f\n",MM,(MM 1)*MM/2.0); getchar(); return 0; } /* output: …… 5791 16776528.000000 5792 16782321.000000 Sum of 1~5793: 16782320.000000 Sum of 1~5793: 16782321.000000 */

看下面实例的演示:

#include <stdio.h> int main() { float sum =16777215.0f; printf("%f\n",sum); sum; printf("%f\n",sum); sum ; printf("%f\n",sum); sum ; printf("%f\n",sum); sum ; printf("%f\n",sum); sum ; printf("%f\n",sum); sum ; printf("%f\n",sum); getchar(); return 0; } /* 16777215.000000 16777216.000000 16777217.000000 16777217.000000 16777217.000000 16777217.000000 16777217.000000 */

到底是怎样舍掉和舍入的呢?再看下面的实例:

#include <stdio.h> int main() { float sum =16777215; int si = sum; printf("%f\n",sum); // 16777215.000000 sum =16777216; printf("%f\n",sum); // 16777216.000000 sum =16777217; printf("%f\n",sum); // 16777216.000000 // 舍掉1位 sum =16777218; printf("%f\n",sum); // 16777218.000000 sum =16777219; printf("%f\n",sum); // 16777220.000000 // 舍入1位 sum =16777220; printf("%f\n",sum); // 16777220.000000 sum =16777221; printf("%f\n",sum); // 16777220.000000 // 舍掉1位 getchar(); return 0; }

数值增大几倍:

#include <stdio.h> int main() { float sum =97897215; int si = sum; printf("%f\n",sum); sum =97897216; printf("%f\n",sum); sum =97897217; printf("%f\n",sum); sum =97897218; printf("%f\n",sum); sum =97897219; printf("%f\n",sum); sum =97897220; printf("%f\n",sum); sum =97897221; printf("%f\n",sum); sum =97897222; printf("%f\n",sum); getchar(); return 0; } /* output: 97897216.000000 97897216.000000 97897216.000000 97897216.000000 97897216.000000 97897216.000000 97897224.000000 97897224.000000 */

float类型的97897222的阶码和尾码:

c语言中浮点数怎么判断是否合法(深入理解浮点数在值域范围内表示大整数的舍掉和舍入位的问题)(2)

其存在舍入和舍掉位的位数 = 阶码-127-23。

同理,double类型也存在同样的情况,只是其起点更长。

下面这样写参数可能会转换为double,放到了寄存器,则看不到精度丢失。(release编译更会如此)

#include <stdio.h> int main() { float sum =16777215; printf("%f\n",sum); printf("%f\n", sum); printf("%f\n", sum); printf("%f\n", sum); printf("%f\n", sum); getchar(); return 0; } /* 16777215.000000 16777216.000000 16777217.000000 16777218.000000 16777219.000000 */

上面将sum的类型从float改成double就不会存在上述问题。因为double的尾码高达52位。

-End-

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页