这段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标准,并坚持生成这个值:)谁能告诉我一个有效的方法来解决这个问题?再次感谢。
6条答案
按热度按时间ldxq2e6h1#
如果
llabs()
的结果不能用类型long long
表示,那么行为是未定义的,我们可以推断这就是这里发生的事情--当转换为int64_t
时,超出范围的值0x 80000000000000被转换为值-9223372036854775808。并且long long
值是64位宽,因此值9223372036854775808是不可表示的。为了让你的程序有定义的行为,你必须确保传递给
llabs()
的值不小于-LLONG_MAX
。你如何做到这一点取决于你-要么修改“有机体”,使它们不能产生这个值(例如,过滤掉那些产生超出范围值的有机体),要么在你传递给llabs()
之前钳制这个值。bvjveswy2#
基本上,你不能。
int64_t
的可表示值的范围是-263到+263-1。(标准要求int64_t
具有纯2的补码表示;如果不支持,实现就不会定义int64_t
。)这个额外的负值没有相应的可表示的正值。
所以除非你的系统有一个大于64位的整数类型,否则你就不能把
0x8000000000000000
的绝对值表示成整数。事实上,根据ISO C标准,程序的行为是未定义的。引用7.22.6.12011 ISO C标准N1570 draft的www.example.com部分:
abs、labs和llabs函数用于计算整数j的绝对值。如果无法表示结果,则行为未定义。
就此而言,结果
是由实现定义的。假设
long long
是64位,则该常量的类型为unsigned long long
。它隐式转换为int64_t
。存储的值很可能是-263或-9223372036854775808
,但不能保证。(甚至允许转换引发由实现定义的信号,但这不太可能。)(It从理论上讲,程序的行为也有可能只是实现定义的,而不是未定义的。如果
long long
的宽度大于64位,那么llabs(a)
的计算就不是未定义的,但是将结果转换回int64_t
是实现定义的。实际上,我从未见过long long
宽度大于64位的C编译器。)如果确实需要表示这么大的整数值,可以考虑使用GNU GMP之类的多精度算术包。
vom3gejh3#
0x8000000000000000
是可以用有符号64位整数表示的最小数。由于two's complement中的一些特殊情况,这是唯一一个绝对值不能用64位有符号整数表示的64位整数。这是因为
0x8000000000000000 = -2^63
,而最大可表示的64位整数是0x7FFFFFFFFFFFFFFF = 2^63-1
。因此,取其绝对值是未定义的行为,通常会得到相同的值。
bybem2ql4#
有符号64位整数的范围为
−(2^63)
到2^63 − 1
,0x8000000000000000
或−(2^63)
的绝对值为2^63
,大于最大64位整数。yrwegjxp5#
最高位设置为高而所有其他位设置为低的有符号整数不能以与该整数的绝对值相同的类型表示。
观察8位整数
整数8_t x = 0x 80;//二进制1000_0000,十进制-128
8位有符号整数可以保存-128和+127之间的值,因此值+128超出范围。对于16位整数,也是如此
整数16_t = 0x 8000;//二进制1000_0000_0000,十进制-32,768
16位整数可以保存-32,768到+32,767之间的值(包括-32,768和+32,767)。
这种模式适用于任何大小的整数,只要它是用二进制补码表示的,这是计算机中整数的实际表示法。二进制补码表示所有位都为低,所有位都为高。
因此,N位有符号整数可以存储2^(N-1)到2^(N-1)-1之间的值,无符号整数可以存储0到2^N-1之间的值。
bprjcwpo6#
有趣的是:
在GCC-9上产生X1MON1X的值。
沮丧!