转换int->unsigned long long是否被标准定义?

4
我无法找到标准中关于如何将int值转换为unsigned long long的确切规范。标准中指定了各种类似的转换,例如int -> unsigned、unsigned -> int(如果为负数则UB)、unsigned long long -> int等。
例如,对于GCC而言,-1被转换为0xffffffffffffffff,而不是0x00000000ffffffff。我能依赖这种行为吗?

那看起来像是符号扩展:https://zh.wikipedia.org/wiki/%E7%AC%A6%E5%8F%B7%E6%89%A9%E5%B1%95 所以很可能是的。 - OSborn
有两种可能性:1)转换为 long long,然后转换为 unsigned long long;2)转换为 unsigned,然后转换为 unsigned long long。按照标准的字面意思,不清楚哪个优先级更高。 - user12
@user12:实现的方式既不是你提出的两种可能性,而是从intunsigned long long的非常直接的转换。如果后者更大(通常是这样),那么在具有带符号整数值的二进制补码表示的普通计算机上,将执行符号扩展。符号扩展包括简单地复制最高位(符号位)以填充左侧。 - Cheers and hth. - Alf
3个回答

2
是的,这是很明确定义的。基本上是将max unsigned long long + 1加到-1上,这将始终为max unsigned long long。在草案C++标准中第4.7整数转换中有所涵盖:

如果目标类型是无符号的,则结果值是与源整数同余的最小无符号整数(模2 n,其中n是用于表示无符号类型的位数)。[注:在二进制补码表示中,这种转换是概念性的,如果没有截断,则不会更改位模式。——结束注释]

它做的是C99相同的事情,但草案C99标准更容易理解,来自第6.3.1.3带符号和无符号整数

否则,如果新类型是无符号的,则通过反复添加或减去可以表示为新类型中最大值加1的最大值,直到该值处于新类型的范围内进行转换。49)

其中脚注49说:

规则描述的是数学值上的算术运算,而不是给定类型表达式的值。


1

是的,已经定义:

C++11 § 4.7 [conv.integral]/2 表示:

如果目标类型为无符号类型,则结果值是源整数对(模2n,其中n是用于表示无符号类型的位数)最小的无符号整数。

-1(模2sizeof(unsigned long long))的最小无符号整数是可能的 unsigned long long 的最大值。


1

无符号整数具有保证的模算术。因此,任何intv都会转换为unsigned longu,使得u=K*2n+v,其中K为0或1,nunsigned long的值表示位数。换句话说,如果v为负数,则只需加上2n


2的幂次方源于C++标准对整数使用纯二进制表示的要求。使用n位表示值时,可能的值的数量为2的n次方。对于浮点类型没有这样的要求(您可以使用std::numeric_limits来检查浮点值的表示中基数)。
请注意,为了适应一些现在过时的平台以及一个采用自己方式处理事情的流行编译器,标准将在无法直接表示为有符号值的无符号值进行相反转换时留下未定义行为。在实践中,在现代系统上所有编译器都可以被告知使得反向转换成为无符号类型转换的确切相反操作,例如Visual C++默认情况下就是这样做的。然而,值得记住的是,没有正式支持,因此可移植代码会产生轻微的(现代计算机不需要的)低效率。

如果“unsigned”值更小的话,“k”理论上可以是除了0或1之外的任何东西,但是“long”和“long long”不会比“int”更小。顺带一提,我发誓最近在这里有关于大于“int”的“int”类型的某些变化/澄清... - Yakk - Adam Nevraumont
@Yakk:是的,如果涉及到其他类型,你可以有其他(整数)K值。关于澄清,我不认为是我说的。我记得几年前有人问起内置类型的范围问题,我把他们引荐给了C标准。这招致了那个孩子的愤怒,他拒绝相信C标准中的任何内容都适用于C ++。或者他/他们希望我去找到并引用相关段落。他或他们还拒绝相信2的幂与数字范围有任何关系。那时感觉有点不愉快。 - Cheers and hth. - Alf
我的意思是,我认为在C/C++中存在一个关于大于“int”或“long”的整数类型的错误,最近已经修复了(例如C++11或类似版本)。 - Yakk - Adam Nevraumont
@Yakk:不是bug,但是C++03不支持“long long”,因为C++03只是C++98的技术修正,在C99引入“long long”之前就发布了。C++11基于C99,因此具有“long long”。C++11也有C99的“<stdint.h>”,但是不幸的是,在赶制C++11标准时,支持的C头文件清单有三个不同的计数(现在已经修复,但我认为这不是你所指的)。哦,负数相除和取模的结果现在是明确定义的,向0舍入。 - Cheers and hth. - Alf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接