鱼C论坛

 找回密码
 立即注册
查看: 1281|回复: 2

[已解决]鱼C第一季第六课的动动手1为什么我计算的结果是0

[复制链接]
发表于 2019-6-4 14:12:17 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
我之前发过一个帖子,使用win7做的,不知道为什么,然后就装个虚拟机,在linux里面做,结果还是一样,请教大神是怎么回事?

首先,这是我写的代码:

[root@Samba ~]# vim a.c

#include <stdio.h>
#include <math.h>
main()
{
         int i;

        unsigned long long sum = 0;

        for ( i = 0; i < 64; i++ ){

           sum = sum + pow(2,i);
       }
                                                                                                   
        printf( "舍罕王应该给予达依尔%llu粒麦子。\n", sum );
        printf( "如果每25000粒麦子为1kg,那么应该给%llukg麦子。\n", sum / 25000 );
                                                                                                  
        return 0;
}


然后,执行结果是:
[root@Samba ~]# gcc -lm a.c && ./a.out
舍罕王应该给予达依尔0粒麦子。
如果每25000粒麦子为1kg,那么应该给0kg麦子。
然后,搞不懂为什么,把%llu换成%I64u也是不行,于是我就照着鱼C课程里的答案,添加了一个temp的变量,结果就好了,不知道为啥:

#include <stdio.h>
#include <math.h>
main()
{
        int i;

        unsigned long long sum = 0;
        unsigned long long temp;

        for ( i = 0; i < 64; i++ ){

           temp = pow(2,i);
           sum = sum + temp;
        }   
        printf( "舍罕王应该给予达依尔%llu粒麦子。\n", sum );
        printf( "如果每25000粒麦子为1kg,那么应该给%llukg麦子。\n", sum / 25000 );

        return 0;
}


结果一执行,结果和答案一样了:
[root@Samba ~]# gcc -lm a.c && ./a.out
舍罕王应该给予达依尔18446744073709551615粒麦子。
如果每25000粒麦子为1kg,那么应该给737869762948382kg麦子。


问大家这是为什么呢?
最佳答案
2019-6-4 14:53:23
#include <stdio.h>
#include <math.h>
main()
{
        int i;
        
        unsigned long long sum = 0;
                
        for ( i = 0; i < 64; i++ ){
                
                sum = sum + (unsigned long long)pow(2, i);  //这里加一个unsigned long long
        }
        
        printf( "舍罕王应该给予达依尔%llu粒麦子。\n", sum );
        printf( "如果每25000粒麦子为1kg,那么应该给%llukg麦子", sum / 25000 );
        
        return 0;
}

等于0的原因是因为这道题的答案是2^64-1,刚好是unsigned long long的上界
而pow函数的返回值是double类型,那么运行时,等式右边的结果也是double类型,自动转换为unsigned long long,再赋值给sum,
但是double类型的分数位只有52位,换句话说,其精度大概只有16到17位,其并不能精确地表示2^64-1,换算后,可能近似为2^64,那么对于unsigned long long就直接溢出为0了

处理方法就是先把pow的结果转换为unsigned long long(double对于其表示范围内的2的整数次方总是能精确表示的),再行计算,就不存在近似的问题了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-6-4 14:49:09 From FishC Mobile | 显示全部楼层
之前我也是这个问题,代码跟你的基本差不多。特意调试发现到第63次的时候还是可以显示的,到64就变成0了。
后来我仔细看了下,小甲鱼的结果的最后2位是15,自己写的代码的结果最后2位是16。
我个人认为的啊,llu最大取值是范围是从0~....15,
而我们计算的结果是从1~.....16。
可能正是因为多了这1位,导致结果溢出。
这是我的猜想,,
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-6-4 14:53:23 | 显示全部楼层    本楼为最佳答案   
#include <stdio.h>
#include <math.h>
main()
{
        int i;
        
        unsigned long long sum = 0;
                
        for ( i = 0; i < 64; i++ ){
                
                sum = sum + (unsigned long long)pow(2, i);  //这里加一个unsigned long long
        }
        
        printf( "舍罕王应该给予达依尔%llu粒麦子。\n", sum );
        printf( "如果每25000粒麦子为1kg,那么应该给%llukg麦子", sum / 25000 );
        
        return 0;
}

等于0的原因是因为这道题的答案是2^64-1,刚好是unsigned long long的上界
而pow函数的返回值是double类型,那么运行时,等式右边的结果也是double类型,自动转换为unsigned long long,再赋值给sum,
但是double类型的分数位只有52位,换句话说,其精度大概只有16到17位,其并不能精确地表示2^64-1,换算后,可能近似为2^64,那么对于unsigned long long就直接溢出为0了

处理方法就是先把pow的结果转换为unsigned long long(double对于其表示范围内的2的整数次方总是能精确表示的),再行计算,就不存在近似的问题了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-10-3 21:25

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表