如何以编程方式确定C中int数据的最大和最小限制?

如何以编程方式确定C中int数据的最大和最小限制?,c,C,我正在尝试K&R的练习2.1。练习内容如下: 编写一个程序,通过从标准标题打印适当的值和直接计算,确定有符号和无符号变量的字符、短、整数和长范围。如果计算它们,则更难:确定各种浮点类型的范围 打印标准标题中常量的值很容易,如下所示(例如仅显示整数): 但是,我希望以编程方式确定限制 我尝试了这段代码,它看起来应该可以工作,但实际上它进入了一个无限循环,并被卡在那里: printf("Integral Ranges (determined programmatically)\n"); int i

我正在尝试K&R的练习2.1。练习内容如下:

编写一个程序,通过从标准标题打印适当的值和直接计算,确定
有符号
无符号
变量的
字符
整数
范围。如果计算它们,则更难:确定各种浮点类型的范围

打印标准标题中常量的值很容易,如下所示(例如仅显示整数):

但是,我希望以编程方式确定限制

我尝试了这段代码,它看起来应该可以工作,但实际上它进入了一个无限循环,并被卡在那里:

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位,因此您可能必须强制转换在按位记录之前,先将数据类型de>0U
转换为具有适当位宽的数据类型。但除此之外,这些技术是基于2的补码整数表示法的基本数学原理。也就是说,如果您的计算机使用不同的方式表示整数,例如补码或most,则这些技术将不起作用-有效符号位。

分配说明允许“从标准标题打印适当的值”,并且在现实世界中
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