如何以编程方式确定C中int数据的最大和最小限制?
我正在尝试K&R的练习2.1。练习内容如下: 编写一个程序,通过从标准标题打印适当的值和直接计算,确定如何以编程方式确定C中int数据的最大和最小限制?,c,C,我正在尝试K&R的练习2.1。练习内容如下: 编写一个程序,通过从标准标题打印适当的值和直接计算,确定有符号和无符号变量的字符、短、整数和长范围。如果计算它们,则更难:确定各种浮点类型的范围 打印标准标题中常量的值很容易,如下所示(例如仅显示整数): 但是,我希望以编程方式确定限制 我尝试了这段代码,它看起来应该可以工作,但实际上它进入了一个无限循环,并被卡在那里: printf("Integral Ranges (determined programmatically)\n"); int i
有符号
和无符号
变量的字符
、短
、整数
和长
范围。如果计算它们,则更难:确定各种浮点类型的范围
打印标准标题中常量的值很容易,如下所示(例如仅显示整数):
但是,我希望以编程方式确定限制
我尝试了这段代码,它看起来应该可以工作,但实际上它进入了一个无限循环,并被卡在那里:
printf("Integral Ranges (determined programmatically)\n");
int i_max = 0;
while ((i_max + 1) > i_max) {
++i_max;
}
printf("int max: %d\n", i_max);
为什么这会陷入一个循环?似乎当一个整数溢出时,它会从2147483647跳到-2147483648。递增的值明显小于上一个值,因此循环应该结束,但它没有结束。因此它实际上没有陷入无限循环中。C代码通常速度很快,如果它不能立即完成,我认为它已经崩溃了 在我让它运行大约10秒后,它最终返回了正确的答案。结果表明,2147483647增量需要相当多的周期才能完成 我还应该注意,我使用
cc-O0
编译来禁用优化,所以这不是问题所在
更快的解决方案可能如下所示:
int i_max = 0;
int step_size = 256;
while ((i_max + step_size) > i_max) {
i_max += step_size;
}
while ((i_max + 1) > i_max) {
++i_max;
}
printf("int max: %d\n", i_max);
然而,由于有符号溢出是一种未定义的行为,因此在实践中试图通过编程方式猜测这一点可能是一个糟糕的想法。最好使用
INT\u MAX
好的,我正要写一篇评论,但是评论太长了
是否允许使用sizeof
如果为true,则有一种简单的方法可以找到任何类型的最大值:
例如,我将找到整数的最大值:
定义:
INT_MAX=(1假设2的补码处理器,使用无符号数学:
unsigned ... smax, smin;
smax = ((unsigned ...)0 - (unsigned ...)1) / (unsigned ...) 2;
smin = ~smax;
正如在其他解决方案中所指出的,试图在C中溢出一个整数是未定义的行为,但至少在这种情况下,我认为你可以得到一个有效的答案,即使是从U.B.的事情:
这种情况是,如果你增加一个值,并将新值与上一个值进行比较,你总是会得到一个更大的值,除了溢出(在这种情况下,你会得到一个更小或相等的值——你没有更多的值更大,这是溢出的情况),所以你至少可以尝试:
int i_old = 0, i = 0;
while (++i > i_old)
i_old = i;
printf("MAX_INT guess: %d\n", i_old);
在这个循环之后,您将获得预期的溢出,并且old_i
将存储最后一个有效数字。当然,如果您继续,您必须使用以下代码片段:
int i_old = 0, i = 0;
while (--i < i_old)
i_old = i;
printf("MIN_INT guess: %d\n", i_old);
inti_old=0,i=0;
而(--i
当然,U.B.甚至可能意味着程序停止运行(在这种情况下,您必须进行跟踪,以获得至少打印的最后一个值)
顺便说一句,在古代的K&R中,整数通常是16位宽的,这是一个很容易通过向上计数获得的值(比现在更容易,尝试从0向上溢出64位整数)我会使用2的补码的属性来计算值
unsigned int uint_max = ~0U;
signed int int_max = uint_max >> 1;
signed int int_min1 = (-int_max - 1);
signed int int_min2 = ~int_max;
2^3是1000
。2^3-1是0111
。2^4-1是1111
w
是数据类型的长度(以位为单位)
uint\u max
为2^w-1,或111…111
。此效果通过使用~0U
实现
int_max
是2^(w-1)-1,或0111…111
。这种效果可以通过将uint_max
1位向右移位来实现。因为uint_max
是一个无符号值,所以逻辑移位由
运算符应用,这意味着它会添加前导零,而不是扩展符号位
int_min
为-2^(w-1),或100…000
。在二的补码中,最有效位的权重为负
这是如何可视化计算的第一个表达式int\u min1
:
...
011...111 int_max +2^(w-1) - 1
100...000 (-int_max - 1) -2^(w-1) == -2^(w-1) + 1 - 1
100...001 -int_max -2^(w-1) + 1 == -(+2^(w-1) - 1)
...
加1将向下移动,减1将向上移动。首先我们求反int\u max
以生成有效的int
值,然后减去1得到int\u min
。我们不能只求反(int\u max+1)
因为这将超过int\u max
本身的最大int
值
取决于您使用的是C或C++的版本,表达式<代码> -(IntMax + 1)
要么变成有符号的64位整数,保持有符号但牺牲原始位宽度;要么变成无符号的32位整数,保持原始位宽度但牺牲有符号性。我们需要以这种迂回的方式编程声明int\u min
,以使其保持有效的int
值
如果这对您来说有点(或字节)太复杂,您可以执行~int\u max
,观察int\u max
是011…111
,int\u min
是100…000
请记住,我在这里提到的这些技术可以用于整数数据类型的任何位宽w。它们可以用于char
,short
,int
,long
,也可以用于long
。请记住,默认情况下整数文本几乎总是32位,因此您可能必须强制转换int i_old = 0, i = 0;
while (--i < i_old)
i_old = i;
printf("MIN_INT guess: %d\n", i_old);
unsigned int uint_max = ~0U;
signed int int_max = uint_max >> 1;
signed int int_min1 = (-int_max - 1);
signed int int_min2 = ~int_max;
...
011...111 int_max +2^(w-1) - 1
100...000 (-int_max - 1) -2^(w-1) == -2^(w-1) + 1 - 1
100...001 -int_max -2^(w-1) + 1 == -(+2^(w-1) - 1)
...
#include <stdio.h>
int main() {
int n = 1;
while(n>0) {
n=n<<1;
}
int int_min = n;
int int_max = -(n+1);
printf("int_min is: %d\n",int_min);
printf("int_max is: %d\n", int_max);
return 0;
}
unsigned long LMAX=(unsigned long)-1L;
long SLMAX=LMAX/2;
long SLMIN=-SLMAX-1;
unsigned long long LLMAX=(unsigned long long)-1LL;
signed int max_signed_int = ~(1 << ((sizeof(int) * 8) -1));
signed int min_signed_int = (1 << ((sizeof(int) * 8) -1));
unsigned int max_unsigned_int = 0U;
unsigned int min_unsigned_int = ~0U;
// max_signed_int = 2147483647
// min_signed_int = -2147483648
// max_unsigned_int = 0
// min_unsigned_int = 4294967295