傻眼貓咪
发表于 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 才正确
jackz007
发表于 2021-11-29 21:29:22
傻眼貓咪 发表于 2021-11-29 21:18
感谢指教,确实我的错了。
而且我又发现新知识了:位元运算为什么不比 pow() 准?
double 精度比位元操作精度高?完全不可能!
试试这个代码,纯粹的位元操作,看看精度如何?
#include <stdio.h>
int main(void)
{
unsigned long long d , i ;
for(d = i = 0 , d = 0 ; i < 64 ; i ++) d |= 1LL << i ;
printf("%I64u\n" , d) ;
}
编译、运行实况:
D:\00.Excise\C>g++ -o x x.c
D:\00.Excise\C>x
18446744073709551615
D:\00.Excise\C>
傻眼貓咪
发表于 2021-11-29 21:41:39
jackz007 发表于 2021-11-29 21:29
double 精度比位元操作精度高?完全不可能!
试试这个代码,纯粹的位元操作,看看精度 ...
受教了,C/C++ 语言确实很多需要学。
BlackWhite_idea
发表于 2021-11-29 21:55:01
lightninng 发表于 2021-11-29 20:49
仔细观察
上面这段代码等价的代码应该是
就是这样用的话,C不输出。、、、
傻眼貓咪
发表于 2021-11-29 22:05:06
jackz007 发表于 2021-11-29 21:29
double 精度比位元操作精度高?完全不可能!
试试这个代码,纯粹的位元操作,看看精度 ...
感谢你的代码,我悄悄简化了一下:#include <stdio.h>
#include <math.h>
int main()
{
unsigned long long wheat = (unsigned long long)(pow(2, 64) - 1);
printf("舍罕王应该给予达依尔 %llu 粒麦子!!!", wheat);
return 0;
}输出:舍罕王应该给予达依尔 18446744073709551615 粒麦子!!!
jackz007
发表于 2021-11-29 22:08:32
傻眼貓咪 发表于 2021-11-29 22:05
感谢你的代码,我悄悄简化了一下:输出:
这个想法很妙!
傻眼貓咪
发表于 2021-11-29 22:10:20
jackz007 发表于 2021-11-29 22:08
这个想法很妙!
多亏你了,我完全没有想到位元 OR 这点,然后我就结合数学知识得出这个
jackz007
发表于 2021-11-29 22:18:23
傻眼貓咪 发表于 2021-11-29 22:10
多亏你了,我完全没有想到位元 OR 这点,然后我就结合数学知识得出这个
确实,只有老鸟才会想到这个办法,菜鸟们是万万想不到的,我也没有想到!{:5_104:}
傻眼貓咪
发表于 2021-11-29 22:21:57
jackz007 发表于 2021-11-29 22:18
确实,只有老鸟才会想到这个办法,菜鸟们是万万想不到的,我也没有想到!
共同学习{:5_108:}
jackz007
发表于 2021-11-29 22:25:56
傻眼貓咪 发表于 2021-11-29 22:21
共同学习
互相学习!{:5_95:}
人造人
发表于 2021-11-29 23:13:21
问题在于一个是进行整数运算,一个是浮点数运算
而浮点数运算是有精度损失的,进行运算的数越大,损失就越大
看下面的代码
#include <stdio.h>
#include <math.h>
int main(void) {
int i;
unsigned long long int mi = 0;
unsigned long long int sum = 0;
unsigned long long int x = 0;
for(i = 0; i <= 64; i++) {
mi = pow(2, i); sum += mi;
x += pow(2,i);
if(i == 51 || i == 52 || i == 53 || i == 54) {
printf("%d: %llu\n", i, sum);
printf("%d: %llu\n", i, x);
}
}
printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", x);
printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", sum);
return 0;
}
$ ./main
51: 4503599627370495
51: 4503599627370495
52: 9007199254740991
52: 9007199254740991
53: 18014398509481983
53: 18014398509481984
54: 36028797018963967
54: 36028797018963968
舍罕王应该给予达依尔0粒麦子!!!
舍罕王应该给予达依尔18446744073709551615粒麦子!!!
$
当 i 等于 52 的时候还是正常的,但是到 i 等于 53 的时候就开始出现问题了
一个的结果是
18014398509481983
另一个是
18014398509481984
那最后为什么有一个输出的是 0 ?
看下面的代码
在看下面的代码之前,先看一下你的代码
int i;
unsigned long long int mi = 0;
unsigned long long int sum = 0;
for(i=0;i<=64;i++)
{
mi = pow(2,i);
sum += mi;
// mi += pow(2,i);
}
// printf("舍罕王应该给予达依尔%llu粒麦子!!!",mi);
printf("舍罕王应该给予达依尔%llu粒麦子!!!",sum);
再看标准答案
#include <stdio.h>
#include <math.h>
int main()
{
unsigned long long sum = 0;
unsigned long long temp;
unsigned long long weight;
int i;
for (i=0; i < 64; i++)
{
temp = pow(2, i);
sum = sum + temp;
}
weight = sum / 25000;
printf("舍罕王应该给予达依尔%llu粒麦子!\n", sum);
printf("如果每25000粒麦子为1kg,那么应该给%llu公斤麦子!\n", weight);
return 0;
}
看出哪里不一样了吗?
for (i=0; i < 64; i++)
for(i=0;i<=64;i++)
看出来了吗?
所以,代码改成这样
#include <stdio.h>
#include <math.h>
int main(void) {
int i;
unsigned long long int mi = 0;
unsigned long long int sum = 0;
unsigned long long int x = 0;
//for(i = 0; i <= 64; i++) {
for(i = 0; i < 64; i++) {
mi = pow(2, i); sum += mi;
x += pow(2,i);
if(i == 62) {
printf("%d: %llu\n", i, x);
unsigned long long int temp = pow(2, 63);
printf("temp: %llu\n", temp);
x += temp;
printf("x: %llu\n", x);
}
}
printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", x);
printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", sum);
return 0;
}
$ ./main
62: 9223372036854775808
temp: 9223372036854775808
x: 0
舍罕王应该给予达依尔9223372036854775808粒麦子!!!
舍罕王应该给予达依尔18446744073709551615粒麦子!!!
$
在第 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 ?看起来是个正确的值?
就是这个代码
for(i=0;i<=64;i++)
#include <stdio.h>
#include <math.h>
int main(void) {
int i;
unsigned long long int mi = 0;
unsigned long long int sum = 0;
for(i = 0; i <= 64; i++) {
mi = pow(2, i); sum += mi;
if(i == 62 || i == 63 || i == 64) {
printf("%d: %llu\n", i, sum);
}
}
printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", sum);
return 0;
}
$ ./main
62: 9223372036854775807
63: 18446744073709551615
64: 18446744073709551615
舍罕王应该给予达依尔18446744073709551615粒麦子!!!
$
现在的问题变成了
为什么 i 等于 63 和 i 等于 64 的时候,sum 的值是一样的?
#include <stdio.h>
#include <math.h>
int main(void) {
int i = 64;
unsigned long long int x = pow(2, i);
printf("%llu\n", x);
return 0;
}
$ ./main
0
$
18446744073709551615 + 0 = 18446744073709551615
所以一样
为什么不把代码写成下面这样?
#include <stdio.h>
#include <math.h>
int main(void) {
unsigned long long int x = pow(2, 64);
printf("%llu\n", x);
return 0;
}
$ ./main
18446744073709551615
为什么输出这个?
这我就不知道了,我感觉是编译器认为数字太大超范围了,就给 x 设置了一个 x 能表示的最大的数
你需要知道的是 pow(2, 64),是编译器直接算出来的,不是调用 math.h 中的 pow 函数
好了,没了,^_^
人造人
发表于 2021-11-29 23:17:57
我又检查了一遍,希望没有说错的地方
^_^
人造人
发表于 2021-11-29 23:22:25
好像漏了一个代码
这个
#include <stdio.h>
#include <math.h>
int main(void) {
int i;
unsigned long long int mi = 0;
unsigned long long int sum = 0;
unsigned long long int x = 0;
for(i = 0; i < 64; i++) {
mi = pow(2, i); sum += mi;
x += pow(2,i);
}
printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", x);
printf("舍罕王应该给予达依尔%llu粒麦子!!!\n", sum);
return 0;
}
$ ./main
舍罕王应该给予达依尔0粒麦子!!!
舍罕王应该给予达依尔18446744073709551615粒麦子!!!
小于 64,输出也依然是 0
傻眼貓咪
发表于 2021-11-30 00:43:18
本帖最后由 傻眼貓咪 于 2021-11-30 00:46 编辑
感谢大佬们指教,收益良多,这题解法大致三种:#include <stdio.h>
#include <math.h>
int main()
{
/*方法 1:位元 AND 运算符*/
unsigned long long A = 1LL;
for(unsigned long long i = 0; i < 64; i++) A ^= 2LL<<i;
printf("A: %llu\n", A);
/*方法 2:位元 OR 运算符*/
unsigned long long B = 0;
for(unsigned long long i = 0; i < 64; i++) B |= 1LL<<i;
printf("B: %llu\n", B);
/*方法 3:二次方运算*/
unsigned long long C = (unsigned long long)(pow(2, 64)-1);
printf("C: %llu", C);
return 0;
}输出:A: 18446744073709551615
B: 18446744073709551615
C: 18446744073709551615*注:以上三种都正确,只要明白其中原理,其实题目很简单就能解决,还是要感谢 22 楼大佬代码,让我突然清晰脑袋(有时做太多习题,往往会忘记基本)
傻眼貓咪
发表于 2021-11-30 00:51:45
人造人 发表于 2021-11-29 23:22
好像漏了一个代码
这个
至於为什么 pow(2, 64)-1 会输出答案:如图(这是数学公式)
人造人
发表于 2021-11-30 00:53:58
傻眼貓咪 发表于 2021-11-30 00:51
至於为什么 pow(2, 64)-1 会输出答案:如图(这是数学公式)
没明白你的这个回复
我最后的问题是
这个代码
#include <stdio.h>
#include <math.h>
int main(void) {
unsigned long long int x = pow(2, 64);
printf("%llu\n", x);
return 0;
}
为什么输出这个?
$ ./main
18446744073709551615
人造人
发表于 2021-11-30 00:58:23
傻眼貓咪 发表于 2021-11-30 00:43
感谢大佬们指教,收益良多,这题解法大致三种:输出:*注:以上三种都正确,只要明白其中原理,其实题目很 ...
我感觉你们讨论的问题不是题主问的问题
很纳闷这两种方式不是注释那段更简便?为什么不能输出
就像 lightninng 所言
难道人家问的不是为啥不能直接用mi+=pow(2,i);代替上面的语句?
其实上面大家指出的所有问题都没错,但是就是没有回答题主的问题~~~
我感觉要么是我和 lightninng 理解错题主的问题了,要么就是你们理解错了
我感觉我是对的
^_^
傻眼貓咪
发表于 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)
傻眼貓咪
发表于 2021-11-30 01:05:19
人造人 发表于 2021-11-30 00:58
我感觉你们讨论的问题不是题主问的问题
很纳闷这两种方式不是注释那段更简便?为什么不能输出
如果真的不明白我的解说,我也无能为力了,但是非常感谢这次楼主,让我学习很多
人造人
发表于 2021-11-30 01:07:01
傻眼貓咪 发表于 2021-11-30 01:01
如图:表示 1+2+4+8 ......+9223372036854775808 = (2^64)-1
你想想看,假设...
不是,我没明白你说的这个是对我上面的哪个问题的补充或是指正
我上面提出了好多问题,除了最后一个,其他的问题都已自问自答的形式解决了
你的这个回复是指的我的哪一个问题