【问题标题】:Absolute value of INT_MIN [duplicate]INT_MIN 的绝对值 [重复]
【发布时间】:2014-03-08 11:38:18
【问题描述】:

如何提取INT_MIN 的绝对值而不溢出?请参阅此代码以了解问题:

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("INT_MAX: %d\n", INT_MAX);
    printf("INT_MIN: %d\n", INT_MIN);
    printf("abs(INT_MIN): %d\n", abs(INT_MIN));

    return 0;
}

吐出以下内容

INT_MAX: 2147483647
INT_MIN: -2147483648
abs(INT_MIN): -2147483648

我需要这个来检查 int 的值是否大于零。

至于这个问题与Why the absolute value of the max negative integer -2147483648 is still -2147483648? 重复,我不同意,因为这是一个如何,而不是为什么的问题。

【问题讨论】:

  • 二补码中最小数的绝对值不存在。更详细地描述你想要实现的目标,我相信还有另一种方法。
  • abs的函数原型在哪里?
  • 只是不要在边界操作 - 奇怪和不寻常的事情会发生
  • @ninjalj:是的,但是你能说出有多少 C 的实际实现是不成立的?

标签: c int integer-overflow


【解决方案1】:

printf 格式字符串中的%d 转换说明符将相应的参数转换为有符号十进制整数,在这种情况下,int 类型会溢出。 C 标准特别提到有符号整数溢出是未定义的行为。您应该做的是在格式字符串中使用%u。此外,您需要分别为函数printfabs 的原型包含标题stdio.hstdlib.h

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

// This solves the issue of using the standard abs() function
unsigned int absu(int value) {
    return (value < 0) ? -((unsigned int)value) : (unsigned int)value;
}

int main(void) {
    printf("INT_MAX: %d\n", INT_MAX);
    printf("INT_MIN: %d\n", INT_MIN);
    printf("absu(INT_MIN): %u\n", absu(INT_MIN));

    return 0;
}

这给出了我的 32 位机器上的输出:

INT_MAX: 2147483647
INT_MIN: -2147483648
absu(INT_MIN): 2147483648

【讨论】:

  • 这实际上是正确的还是由于实现定义的行为而发生的工作
  • 请注意,这仍然会调用 UB;来自abs 的 C99 标准:“如果结果无法表示,则行为未定义。
  • @ajay:唉,不; abs 的返回类型是 int
  • 将 INT_MIN 的值转换为字符串表示然后去掉减号不是更简单的解决方案
  • 在这里评论的每个人都需要到最顶部阅读这条评论:The absolute value of the smallest number in two-complement just does not exist.来自deviantfan。试图将 INT_MIN 转换为无符号只是不可表示的。就像在那条评论中一样,您需要在此处描述您实际想要完成的工作
【解决方案2】:

怎么样

printf ("abs(INT_MIN) = %ld", -((long int) INT_MIN));

或者如果您的long 不长于int

printf ("abs(INT_MIN) = %lld", -((long long int) INT_MIN));

或者,如果您准备接受 abs(INT_MIN) 始终是 INT_MAX + 1

printf ("abs(INT_MIN) = %u", ((unsigned int) INT_MAX ) + 1 );

【讨论】:

  • 假设 sizeof(long) > sizeof(int),但并非总是如此……
  • 我也有类似的想法,但是当我需要abs()LONG LONG 时会发生什么?
  • @OliCharlesworth:在这种情况下使用long long int(已编辑以解决此问题)。
  • @abligh 好的,这在某种程度上有效,所以我会赞成,但这不是一个完整的解决方案。
  • @MorganWilde no 2 的补码有符号整数类型将能够表示最大负数类型的绝对值。
【解决方案3】:

没有可移植的方法将最大负数的绝对值提取为整数。 ISO C 标准说(§6.2.6.2¶2):

作为值位的每个位应与对象中的同一位具有相同的值 对应的无符号类型的表示(如果有符号的有 M 个值位 type 和 N 为无符号类型,则 M ≤ N )。

注意它使用 ≤,而不是 <.>

由于2的补码中的符号位具有值-(2M),并且每个值位的值是之间的2的幂12M-1,在 M=N 可以表示 的实现中没有无符号整数2N,最多只能表示2N-1 = 1+2+...+ 2N-1

【讨论】:

  • 所以使用unsigned int 只能让我代表一个值2^(N-1)?这令人困惑。不是unsigned int02^(N) - 1的范围吗?该标准还说(6.2.5:9)-The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type。所以这两个范围的区别只有一个值2^(N-1)?
  • 使用无符号并不能保证你有更多的价值。但是,您可以检查limits.hINT_MAXUINT_MAX、... 中的宏,以查看您的特定平台是否为无符号整数提供了比有符号整数更大的非负整数值范围。
  • UINT_MAX2^32 - 1INT_MAX2^31 - 1 在我的机器上。我想知道为什么不能保证,那么无论如何使用无符号类型有什么用?还假设signedunsigned的范围相同(2^(N-1)除外),带符号位1的有符号类型在无符号类型中代表什么值?
  • 整数可以有填充位。一些整数值可能是陷阱表示(类似于浮点数中的 NaN)。 60 年代和 70 年代的旧机器和 DSP 会做一些奇怪的事情。编写 C 标准是为了支持所有已知运行 C 的古怪机器。
  • 该标准没有说明有多少填充位。它还说不需要任何填充位。有什么办法可以查出我的机器上是否有一个?这真的很古怪,我从来不知道这一点。尝试编写在所有使用过C 的机器上运行的C 真的很难!
【解决方案4】:

在 C 中,函数 int abs(int j) 仅存在 int 版本。您可以在标题stdlib.h 下使用另一个函数labs。它的原型:long int labs(long int j);

#include <limits.h> 
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("INT_MAX: %d\n", INT_MAX);
    printf("INT_MIN: %d\n", INT_MIN);
    printf("abs(INT_MIN): %ld\n", labs((long)INT_MIN));

    return 0;
}

【讨论】:

  • cplusplus.com/reference/climits 表示这些限制的实际值取决于实现。所以这个解决方案只有在 INT_MIN 不等于 LONG_MIN 时才有效
  • 是的,谢谢你的提醒。
【解决方案5】:

转换为下一个可用的更大整数类型应该可以做到。但你必须使用对应的 abs-variant(在这种情况下为 llabs(...)

printf("llabs(INT_MIN): %lld\n", llabs((long long int)INT_MIN));

编辑:

您可以通过比较 INT_MINLONG_MINLLONG_MIN 来检查下一个更大的类型是什么。也许在你的情况下,long 的演员已经可以做到了。

printf("labs(INT_MIN): %ld\n", labs((long int)INT_MIN));

请注意,显式转换实际上是不必要的,因为函数本身会隐式转换参数

【讨论】:

    【解决方案6】:

    首先,您必须#include &lt;math.h&gt; 才能正确使用abs 功能。

    第二件事是,如果您只想打印limits.h 中定义的INT_MIN 的绝对值,您可以将其打印为unsigned integerlong long integer,像这样:

    printf( "abs(INT_MIN): %u\n", abs( INT_MIN ) );     // %u for unsigned int
    printf( "abs(INT_MIN): %lld\n", abs( INT_MIN ) );   // %lld for long long int
    

    既然你想得到绝对值,那肯定是无符号的,这应该没问题。

    如果你不想包含math.h,你可以像这样自己做:

    // ternary implementation of the function abs
    printf( "abs(INT_MIN): %u\n", ( INT_MIN > 0 ) ? INT_MIN : -INT_MIN );
    

    如果您想将其用于其他目的,则可以将abs( INT_MIN ) 值存储在unsigned intlong long int 变量中。

    【讨论】:

    • ( INT_MIN &gt; 0 ) ? INT_MIN : -INT_MIN - 嗯?
    • "你必须包含 math.h 才能正确使用 abs"。 abs 在 stdlib.h...
    • @OliCharlesworth 随便什么人,随便……这是准确的翻译;但当然,它永远只是-INT_MIN
    • @JuniorDussouillez 我没有说它不在stdlib.h 中,是吗?两者兼而有之。
    • @ThoAppelsin:确实,但-INT_MIN == INT_MIN 在实践中(虽然技术上未定义的行为)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-15
    相关资源
    最近更新 更多