我运行以下程序,输出中出现意外数字。
输入要检查的号码
9999999999
1410065407是素数
我在输出中得到一个不同的数字。我输入了9999999999,输出中看到了1410065407。有人可以解释这是怎么回事吗?看起来我超出了整数域的范围。
#include<stdio.h>
#include<stdlib.h>
void main()
{
int n,i;
printf("Enter the number to be checked\n");
scanf("%d",&n);
i=2;
while(i<n)
{
if(n%i==0)
{
printf("%d is not a prime number\n", n);
exit(0);
}
i++;
}
printf("%d is a prime number\n", n);
}
答案 0 :(得分:3)
9,999,999,999
(十9
s)非常适合您的int
数据类型。如果你计算出它与1,410,065,407
之间的差异,你就会发现它的精确度为2(确切地说是2 33 ),这意味着它在你扫描时会缠绕它英寸
通过“缠绕”,我指的是整数的属性,只需将它们的最大值环绕以成为意外的东西。
例如,一个8位无符号整数可以保存值0..255
,当你向保持255
的变量添加一个时,它将变为零(有符号值倾向于从最大的正值中包裹)值为最小的负值,在这种情况下最小的含义为零。)
因此,假设您正在读取十进制数字,其中类型的范围为0..255
,并且该数字的字符串表示形式为456
。以下“代码”大致是如何工作的:
def scanNum(s):
result = 0
for each character c in s:
result = result * 10 + value(c)
return result
4
字符时,result
乘以10,0 * 10 = 0
,然后再添加4,即4
。5
字符时,result
乘以10,4 * 10 = 40
,然后添加5,即45
。6
字符时,result
乘以10,45 * 10 = 450
但,因为您只能代表0..255
,它会换行给你450 - 256 = 194
。然后你添加六个,给200
。您可以看到,您想要的内容与获得的内容之间的差异与范围非常相关:456 - 200 = 256
。
如果您使用 9 9
s(或999,999,999
进行尝试,请记住,在{{1}处,32位二进制补码可表示的最大数字更大}),或使用能够容纳更大数字的数据类型,您可能会发现它没关系:
2,147,483,647
我原本认为根据标准这是错误的,因为根据#include <stdio.h>
#include <stdlib.h>
int main (void) {
long long n;
printf("Enter the number to be checked\n");
scanf("%lld",&n);
printf("%lld\n", n);
return 0;
}
,它遵循C11 7.21.6.2 The fscanf function /12
函数:
d 匹配可选的带符号十进制整数,其格式与strtol函数的主题序列的预期格式相同,其基值参数值为10。相应的参数应该是指向有符号整数的指针。
和strtol
函数,strtol
表示:
如果正确的值超出了可表示值的范围,则返回LONG_MIN,LONG_MAX,LLONG_MIN,LLONG_MAX,ULONG_MAX或ULLONG_MAX(根据值的返回类型和符号,如果有的话)。
但实际上,只有格式遵循该功能。 C11 7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions /8
及其兄弟的结果由fscanf
控制:
如果此对象没有合适的类型,或者如果转换结果无法在对象中表示,行为未定义。
所以,如果它超出范围你得到的数字可以是任何东西,事实上,鉴于对未定义行为的结果缺乏限制,它可以做任何而不仅仅是返回一个狡猾的结果,包括格式化你的磁盘和将当地时空折叠成一个裸体奇点: - )
在这种情况下,它似乎只是将值包装为扫描过程的一部分,这是代码最简单的事情。