鱼C论坛

 找回密码
 立即注册
楼主: BlackWhite_idea

[已解决]很纳闷这两种方式不是注释那段更简便?为什么不能输出

[复制链接]
发表于 2021-11-29 21:28:31 | 显示全部楼层
lightninng 发表于 2021-11-29 21:22
从结果看,不准的应该是pow吧,2的次方怎么可能是偶数呢,盲猜是因为二进制的问题~

而且我知道我的问题出现在哪里了,
2 << 0 = 2
pow(2, 0) = 1

问题出现在 i = 0 的时候,而且

2 << 2 = 8
pow(2, 2) = 4

位元必须 i-1 才正确
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 21:29:22 | 显示全部楼层
傻眼貓咪 发表于 2021-11-29 21:18
感谢指教,确实我的错了。

而且我又发现新知识了:位元运算为什么不比 pow() 准?

        double 精度比位元操作精度高?完全不可能!
        试试这个代码,纯粹的位元操作,看看精度如何?
  1. #include <stdio.h>

  2. int main(void)
  3. {
  4.         unsigned long long d , i                             ;
  5.         for(d = i = 0 , d = 0 ; i < 64 ; i ++) d |= 1LL << i ;
  6.         printf("%I64u\n" , d)                                ;
  7. }
复制代码

        编译、运行实况:
  1. D:\00.Excise\C>g++ -o x x.c

  2. D:\00.Excise\C>x
  3. 18446744073709551615

  4. D:\00.Excise\C>
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 21:41:39 | 显示全部楼层
jackz007 发表于 2021-11-29 21:29
double 精度比位元操作精度高?完全不可能!
        试试这个代码,纯粹的位元操作,看看精度 ...

受教了,C/C++ 语言确实很多需要学。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-11-29 21:55:01 | 显示全部楼层
lightninng 发表于 2021-11-29 20:49
仔细观察

上面这段代码等价的代码应该是

就是这样用的话,C不输出。、、、
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 22:05:06 | 显示全部楼层
jackz007 发表于 2021-11-29 21:29
double 精度比位元操作精度高?完全不可能!
        试试这个代码,纯粹的位元操作,看看精度 ...

感谢你的代码,我悄悄简化了一下:
  1. #include <stdio.h>
  2. #include <math.h>

  3. int main()
  4. {
  5.     unsigned long long wheat = (unsigned long long)(pow(2, 64) - 1);
  6.     printf("舍罕王应该给予达依尔 %llu 粒麦子!!!", wheat);
  7.     return 0;
  8. }
复制代码
输出:
  1. 舍罕王应该给予达依尔 18446744073709551615 粒麦子!!!
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 22:08:32 | 显示全部楼层
傻眼貓咪 发表于 2021-11-29 22:05
感谢你的代码,我悄悄简化了一下:输出:

         这个想法很妙!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 22:10:20 | 显示全部楼层

多亏你了,我完全没有想到位元 OR 这点,然后我就结合数学知识得出这个
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 22:18:23 | 显示全部楼层
傻眼貓咪 发表于 2021-11-29 22:10
多亏你了,我完全没有想到位元 OR 这点,然后我就结合数学知识得出这个

        确实,只有老鸟才会想到这个办法,菜鸟们是万万想不到的,我也没有想到!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 22:21:57 | 显示全部楼层
jackz007 发表于 2021-11-29 22:18
确实,只有老鸟才会想到这个办法,菜鸟们是万万想不到的,我也没有想到!

共同学习
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 22:25:56 | 显示全部楼层

        互相学习!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 23:13:21 | 显示全部楼层
问题在于一个是进行整数运算,一个是浮点数运算
而浮点数运算是有精度损失的,进行运算的数越大,损失就越大

看下面的代码
  1. #include <stdio.h>
  2. #include <math.h>

  3. int main(void) {
  4.     int i;
  5.     unsigned long long int mi = 0;
  6.     unsigned long long int sum = 0;
  7.     unsigned long long int x = 0;
  8.     for(i = 0; i <= 64; i++) {
  9.         mi = pow(2, i); sum += mi;
  10.         x += pow(2,i);
  11.         if(i == 51 || i == 52 || i == 53 || i == 54) {
  12.             printf("%d: %llu\n", i, sum);
  13.             printf("%d: %llu\n", i, x);
  14.         }
  15.     }
  16.     printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", x);
  17.     printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", sum);
  18.     return 0;
  19. }
复制代码

  1. $ ./main
  2. 51: 4503599627370495
  3. 51: 4503599627370495
  4. 52: 9007199254740991
  5. 52: 9007199254740991
  6. 53: 18014398509481983
  7. 53: 18014398509481984
  8. 54: 36028797018963967
  9. 54: 36028797018963968
  10. 舍罕王应该给予达依尔0粒麦子!!!
  11. 舍罕王应该给予达依尔18446744073709551615粒麦子!!!
  12. $
复制代码


当 i 等于 52 的时候还是正常的,但是到 i 等于 53 的时候就开始出现问题了
一个的结果是
18014398509481983
另一个是
18014398509481984


那最后为什么有一个输出的是 0 ?
看下面的代码

在看下面的代码之前,先看一下你的代码
  1.         int i;
  2.         unsigned long long int mi = 0;
  3.         unsigned long long int sum = 0;
  4.         for(i=0;i<=64;i++)
  5.         {
  6.                 mi = pow(2,i);
  7.                 sum += mi;
  8.                
  9. //                mi += pow(2,i);
  10.         }
  11. //        printf("舍罕王应该给予达依尔%llu粒麦子!!!",mi);
  12.         printf("舍罕王应该给予达依尔%llu粒麦子!!!",sum);
复制代码


再看标准答案
  1. #include <stdio.h>
  2. #include <math.h>

  3. int main()
  4. {
  5.         unsigned long long sum = 0;
  6.         unsigned long long temp;
  7.         unsigned long long weight;
  8.         int i;

  9.         for (i=0; i < 64; i++)
  10.         {
  11.                 temp = pow(2, i);
  12.                 sum = sum + temp;
  13.         }

  14.         weight = sum / 25000;

  15.         printf("舍罕王应该给予达依尔%llu粒麦子!\n", sum);
  16.         printf("如果每25000粒麦子为1kg,那么应该给%llu公斤麦子!\n", weight);

  17.         return 0;
  18. }
复制代码


看出哪里不一样了吗?

  1. for (i=0; i < 64; i++)
  2. for(i=0;i<=64;i++)
复制代码


看出来了吗?

所以,代码改成这样
  1. #include <stdio.h>
  2. #include <math.h>

  3. int main(void) {
  4.     int i;
  5.     unsigned long long int mi = 0;
  6.     unsigned long long int sum = 0;
  7.     unsigned long long int x = 0;
  8.     //for(i = 0; i <= 64; i++) {
  9.     for(i = 0; i < 64; i++) {
  10.         mi = pow(2, i); sum += mi;
  11.         x += pow(2,i);
  12.         if(i == 62) {
  13.             printf("%d: %llu\n", i, x);
  14.             unsigned long long int temp = pow(2, 63);
  15.             printf("temp: %llu\n", temp);
  16.             x += temp;
  17.             printf("x: %llu\n", x);
  18.         }
  19.     }
  20.     printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", x);
  21.     printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", sum);
  22.     return 0;
  23. }
复制代码
  1. $ ./main
  2. 62: 9223372036854775808
  3. temp: 9223372036854775808
  4. x: 0
  5. 舍罕王应该给予达依尔9223372036854775808粒麦子!!!
  6. 舍罕王应该给予达依尔18446744073709551615粒麦子!!!
  7. $
复制代码


在第 62 次的时候
x 的值是 9223372036854775808
temp 是 9223372036854775808

x + temp = 9223372036854775808 + 9223372036854775808
= 18446744073709551616
18446744073709551616 这个数字太大了,unsigned long long int 也放不下
这个数字十进制形式不太明显,换成十六进制形式
18446744073709551616 = 0x10000000000000000
对于 unsigned long long int,正好最前面的 1 放不下,后面全是 0,所以最后输出的是 0


那为什么 i 等于 64 的时候 sum 的值不是 0 ?看起来是个正确的值?
就是这个代码
  1. for(i=0;i<=64;i++)
复制代码

  1. #include <stdio.h>
  2. #include <math.h>

  3. int main(void) {
  4.     int i;
  5.     unsigned long long int mi = 0;
  6.     unsigned long long int sum = 0;
  7.     for(i = 0; i <= 64; i++) {
  8.         mi = pow(2, i); sum += mi;
  9.         if(i == 62 || i == 63 || i == 64) {
  10.             printf("%d: %llu\n", i, sum);
  11.         }
  12.     }
  13.     printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", sum);
  14.     return 0;
  15. }
复制代码

  1. $ ./main
  2. 62: 9223372036854775807
  3. 63: 18446744073709551615
  4. 64: 18446744073709551615
  5. 舍罕王应该给予达依尔18446744073709551615粒麦子!!!
  6. $
复制代码


现在的问题变成了
为什么 i 等于 63 和 i 等于 64 的时候,sum 的值是一样的?

  1. #include <stdio.h>
  2. #include <math.h>

  3. int main(void) {
  4.     int i = 64;
  5.     unsigned long long int x = pow(2, i);
  6.     printf("%llu\n", x);
  7.     return 0;
  8. }
复制代码

  1. $ ./main
  2. 0
  3. $
复制代码


18446744073709551615 + 0 = 18446744073709551615
所以一样

为什么不把代码写成下面这样?
  1. #include <stdio.h>
  2. #include <math.h>

  3. int main(void) {
  4.     unsigned long long int x = pow(2, 64);
  5.     printf("%llu\n", x);
  6.     return 0;
  7. }
复制代码

  1. $ ./main
  2. 18446744073709551615
复制代码


为什么输出这个?
这我就不知道了,我感觉是编译器认为数字太大超范围了,就给 x 设置了一个 x 能表示的最大的数
你需要知道的是 pow(2, 64),是编译器直接算出来的,不是调用 math.h 中的 pow 函数

好了,没了,^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 23:17:57 | 显示全部楼层
我又检查了一遍,希望没有说错的地方
^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-29 23:22:25 | 显示全部楼层
好像漏了一个代码
这个
  1. #include <stdio.h>
  2. #include <math.h>

  3. int main(void) {
  4.     int i;
  5.     unsigned long long int mi = 0;
  6.     unsigned long long int sum = 0;
  7.     unsigned long long int x = 0;
  8.     for(i = 0; i < 64; i++) {
  9.         mi = pow(2, i); sum += mi;
  10.         x += pow(2,i);
  11.     }
  12.     printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", x);
  13.     printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", sum);
  14.     return 0;
  15. }
复制代码

  1. $ ./main
  2. 舍罕王应该给予达依尔0粒麦子!!!
  3. 舍罕王应该给予达依尔18446744073709551615粒麦子!!!
复制代码


小于 64,输出也依然是 0

评分

参与人数 1鱼币 +3 收起 理由
BlackWhite_idea + 3

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-30 00:43:18 | 显示全部楼层
本帖最后由 傻眼貓咪 于 2021-11-30 00:46 编辑

感谢大佬们指教,收益良多,这题解法大致三种:
  1. #include <stdio.h>
  2. #include <math.h>

  3. int main()
  4. {
  5.     /*方法 1:位元 AND 运算符*/
  6.     unsigned long long A = 1LL;
  7.     for(unsigned long long i = 0; i < 64; i++) A ^= 2LL<<i;
  8.     printf("A: %llu\n", A);
  9.    
  10.     /*方法 2:位元 OR 运算符*/
  11.     unsigned long long B = 0;
  12.     for(unsigned long long i = 0; i < 64; i++) B |= 1LL<<i;
  13.     printf("B: %llu\n", B);
  14.    
  15.     /*方法 3:二次方运算*/
  16.     unsigned long long C = (unsigned long long)(pow(2, 64)-1);
  17.     printf("C: %llu", C);

  18.     return 0;
  19. }
复制代码
输出:
  1. A: 18446744073709551615
  2. B: 18446744073709551615
  3. C: 18446744073709551615
复制代码
*注:以上三种都正确,只要明白其中原理,其实题目很简单就能解决,还是要感谢 22 楼大佬代码,让我突然清晰脑袋(有时做太多习题,往往会忘记基本)
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-30 00:51:45 | 显示全部楼层
人造人 发表于 2021-11-29 23:22
好像漏了一个代码
这个

至於为什么 pow(2, 64)-1 会输出答案:如图(这是数学公式)

数学公式

数学公式
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-30 00:53:58 | 显示全部楼层
傻眼貓咪 发表于 2021-11-30 00:51
至於为什么 pow(2, 64)-1 会输出答案:如图(这是数学公式)

没明白你的这个回复
我最后的问题是
这个代码
  1. #include <stdio.h>
  2. #include <math.h>

  3. int main(void) {
  4.     unsigned long long int x = pow(2, 64);
  5.     printf("%llu\n", x);
  6.     return 0;
  7. }
复制代码


为什么输出这个?
  1. $ ./main
  2. 18446744073709551615
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-30 00:58:23 | 显示全部楼层
傻眼貓咪 发表于 2021-11-30 00:43
感谢大佬们指教,收益良多,这题解法大致三种:输出:*注:以上三种都正确,只要明白其中原理,其实题目很 ...

我感觉你们讨论的问题不是题主问的问题

很纳闷这两种方式不是注释那段更简便?为什么不能输出


就像 lightninng 所言

难道人家问的不是为啥不能直接用mi+=pow(2,i);代替上面的语句?
其实上面大家指出的所有问题都没错,但是就是没有回答题主的问题~~~

我感觉要么是我和 lightninng 理解错题主的问题了,要么就是你们理解错了
我感觉我是对的
^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-30 01:01:37 | 显示全部楼层
人造人 发表于 2021-11-30 00:53
没明白你的这个回复
我最后的问题是
这个代码

如图:表示 1+2+4+8 ......+9223372036854775808 = (2^64)-1

你想想看,假设...

i = 0, 二的0次方 = 1, sum = 1
i = 1, 二的1次方 = 2, sum = 3
i = 2, 二的2次方 = 4, sum = 7
i = 3, 二的3次方 = 8, sum = 15(你会发现每次 sum 就是 2 次方 -1)
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-30 01:05:19 | 显示全部楼层
人造人 发表于 2021-11-30 00:58
我感觉你们讨论的问题不是题主问的问题

很纳闷这两种方式不是注释那段更简便?为什么不能输出

如果真的不明白我的解说,我也无能为力了,但是非常感谢这次楼主,让我学习很多
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-30 01:07:01 | 显示全部楼层
傻眼貓咪 发表于 2021-11-30 01:01
如图:表示 1+2+4+8 ......+9223372036854775808 = (2^64)-1

你想想看,假设...

不是,我没明白你说的这个是对我上面的哪个问题的补充或是指正
我上面提出了好多问题,除了最后一个,其他的问题都已自问自答的形式解决了
你的这个回复是指的我的哪一个问题
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-4-25 21:57

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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