3

此 C 代码试图找到负数的绝对值,但输出也是负数。谁能告诉我如何克服这个问题?

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>

int main() {
    int64_t a = 0x8000000000000000;
    a = llabs(a);
    printf("%" PRId64 "\n", a);
    return 0;
}

输出

-9223372036854775808

更新:

感谢您的所有回答。我知道这是一个非标准值,这就是我无法对其执行绝对操作的原因。但是,我确实在遗传编程模拟的实际代码库中遇到了这种情况。此处的“有机体”不了解 C 标准并坚持生成此值:) 谁能告诉我解决此问题的有效方法?再次感谢。

4

5 回答 5

6

如果 的结果llabs()不能在 type 中表示long long,则行为未定义。我们可以推断这就是这里发生的事情 - 超出范围的值 0x8000000000000000 在转换为 时被转换为值 -9223372036854775808 int64_t,并且您的long long值是 64 位宽,因此值 9223372036854775808 是无法表示的。

为了让您的程序具有定义的行为,您必须确保传递给的值llabs()不小于-LLONG_MAX. 如何执行此操作取决于您 - 修改“有机体”以使其无法生成此值(例如,过滤掉那些创建超出范围值的立即不合适的值)或在将值传递给之前钳制该值llabs().

于 2013-07-30T01:02:45.423 回答
5

基本上,你不能。

的可表示值的范围int64_t是 -2 63到 +2 63 -1。(并且该标准要求int64_t具有纯 2 的补码表示;如果不支持,则不会定义实现int64_t。)

那个额外的负值没有对应的可表示的正值。

因此,除非您的系统具有大于 64 位的整数类型,否则您将无法将 的绝对值表示0x8000000000000000为整数。

事实上,根据 ISO C 标准,您的程序的行为是未定义的。引用2011 ISO C 标准N1570 草案的第 7.22.6.1 节:

abslabsllabs函数计算整数j的绝对值。如果结果无法表示,则行为未定义。

就此而言,结果

int64_t a = 0x8000000000000000;

是实现定义的。假设long long是 64 位,则该常量的类型为unsigned long long. 它被隐式转换为int64_t. 存储的值很可能(但不能保证)为 -2 63-9223372036854775808. (甚至允许转换引发实现定义的信号,但这不太可能。)

(理论上,您的程序的行为也可能只是实现定义的而不是未定义的。如果long long大于 64 位,则评估llabs(a)不是未定义的,但结果转换回int64_t是实现定义的。在实践中,我从未见过long long超过 64 位的 C 编译器。)

如果您确实需要表示那么大的整数值,您可以考虑使用多精度算术包,例如GNU GMP

于 2013-07-30T01:01:43.570 回答
2

0x8000000000000000是可以用带符号的 64 位整数表示的最小数。由于二进制补码的怪癖,这是唯一一个绝对值不能表示为 64 位有符号整数的 64 位整数。

这是因为0x8000000000000000 = -2^63,而最大可表示的 64 位整数是0x7FFFFFFFFFFFFFFF = 2^63-1

因此,取 this 的绝对值是未定义的行为,通常会产生相同的值。

于 2013-07-30T01:03:00.897 回答
1

有符号的 64 位整数范围从−(2^63)到,或,2^63 − 1的绝对值大于最大 64 位整数。0x8000000000000000−(2^63)2^63

于 2013-07-30T01:01:37.660 回答
1

最高位设置为高且所有其他位设置为低的有符号整数不能以与该整数的绝对值相同的类型表示。

观察一个 8 位整数

int8_t x = 0x80;// 二进制 1000_0000,十进制 -128

一个 8 位有符号整数可以保存 -128 和 +127 之间的值,因此 +128 超出范围。对于 16 位整数,这也成立

int16_t = 0x8000; // 二进制 1000_0000_0000_0000,十进制 -32,768

一个 16 位整数可以保存介于 -32,768 和 +32,767 之间的值。

这种模式适用于任何大小的整数,只要它以二进制补码表示,就像计算机中整数的事实表示一样。二进制补码将 0 作为所有位低,将 -1 作为所有位高。

因此,N 位有符号整数可以保存 2^(N-1) 和 2^(N-1)-1 之间的值,无符号整数可以保存 0 和 2^N-1 之间的值。

于 2013-12-19T12:24:13.557 回答