C++ 中的符号扩展是编译器选项,还是依赖于编译器或依赖于目标?
Posted
技术标签:
【中文标题】C++ 中的符号扩展是编译器选项,还是依赖于编译器或依赖于目标?【英文标题】:Is Sign Extension in C++ a compiler option, or compiler dependent or target dependent? 【发布时间】:2016-11-03 08:10:09 【问题描述】:以下代码已在 3 个不同的编译器和 3 个不同的处理器上编译,并给出了 2 个不同的结果:
typedef unsigned long int u32;
typedef signed long long s64;
int main ()
u32 Operand1,Operand2;
s64 Result;
Operand1=95;
Operand2=100;
Result= (s64)(Operand1-Operand2);
Result 产生 2 个结果:
任何一个
-5
或 4294967291
我知道(Operand1-Operand2)
的操作是作为 32 位无符号计算完成的,然后当转换为 s64
时,符号扩展在第一种情况下正确完成,但在第二种情况下没有正确完成。
我的问题是符号扩展是否可以通过编译器选项进行控制,或者它是依赖于编译器的,或者它是依赖于目标的。
【问题讨论】:
您做出了各种不正确的假设,最严重的是unsigned long
是 32 位,这对于大多数现代操作系统来说通常是不正确的。使用<cstdint>
和适当的固定宽度类型。
" 是作为 32 位无符号计算完成的" 无法保证。仅仅因为您决定将typedef
某事改名为u32
,并不意味着这是真的。如果你需要固定宽度的整数,C++11 提供了它们via a header file
Operand1-Operand2
是无符号的,因此当转换为 s64
时,它总是零扩展。可能有问题的是您没有显示的打印功能
你是怎么检查的?而且您仍然没有提供您正在使用哪个编译器/目标以及如何打印它
使用固定宽度的类型并重复测试,会让人混淆unsigned long
在各个平台上的大小。
【参考方案1】:
您的问题是您假设 unsigned long int
为 32 位宽,signed long long
为 64 位宽。 这个假设是错误的。
We can visualize what's going on 使用 types that have a guaranteed (by the standard) bit width:
int main()
uint32_t large = 100, small = 95;
int64_t result = (small - large);
std::cout << "32 and 64 bits: " << result << std::endl;
// 4294967291
uint32_t large = 100, small = 95;
int32_t result = (small - large);
std::cout << "32 and 32 bits: " << result << std::endl;
// -5
uint64_t large = 100, small = 95;
int64_t result = (small - large);
std::cout << "64 and 64 bits: " << result << std::endl;
// -5
return 0;
在这三种情况中的每一种情况下,表达式small - large
都会产生无符号 整数类型(相应宽度)的结果。此结果是使用模运算计算得出的。
在第一种情况下,因为无符号结果可以存储在更宽的有符号整数中,所以不执行值的转换。
在其他情况下,结果不能存储在有符号整数中。因此执行了实现定义的转换,这通常意味着将无符号值的位模式解释为有符号值。因为结果是“大”,所以最高位将被设置,当被视为有符号值(在二进制补码下)时,相当于一个“小”负值。
突出 Lưu Vĩnh Phúc 的评论:
Operand1-Operand2
是无符号的,因此在转换为 s64 时,它始终是零扩展。 [..]
符号扩展只在第一种情况下进行,因为只有这样才会有一个扩大的转换,它确实总是零扩展。
引用来自the standard,强调我的。关于small - large
:
如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模
2^n$
其中 n 是用于表示无符号类型的位数)。 [..]§ 4.7/2
关于从无符号到有符号的转换:
如果[整数转换的]目标类型是有符号的,如果可以在目标类型中表示,则值不变;否则,该值为实现定义。
§ 4.7/3
【讨论】:
@DanielJour:我的假设一般来说是错误的,但是在我的目标编译器上它是 32 位的。我的问题主要是关于第一种情况:“在某些情况下,它给出 -5,有时是 4294967291”,有没有办法强制符号扩展而不将 2 个操作数中的任何一个显式转换为 int64_t? @AYZAB 真正的“符号扩展”仅在您从X
类型转换为 Y
时完成,其中 both 都是有符号类型,Y
更宽( “有更多位”)比X
。
感谢您的回复。所以只是为了确保(如果我们确定类型)u32 如果转换为 s64 将始终用零扩展,并且在上述情况下要正常工作,除了转换为 s32 开始(或转换)之外,我们没有其他解决方案s32) 的操作数之一,然后转换为 s64。如果您可以向我提供上述语句的标准摘录“真正的“符号扩展”仅在您从类型 X 转换为 Y 时完成,其中两者都是有符号类型并且 Y 比 X 更宽(“具有更多位”)。 "真的很感谢你
@AYZAB“符号扩展”是标准甚至懒得谈论的东西。这是一个实现问题,如何存储值“[..] 如果它可以在目标类型中表示,则保持不变”。你想要达到的行为是什么? u32到(最终)s64?但结果是-5
(在这种情况下)?。正确的方法应该是int64_t result = reinterpret_cast<int32_t>(small - large)
。这执行无符号模运算,通过保持位模式(因此不是实现定义,但定义良好)转换为有符号,最后进行符号扩展。【参考方案2】:
符号扩展取决于平台,其中平台是编译器、目标硬件架构和操作系统的组合。
此外,正如 Paul R 提到的,内置类型(如 unsigned long
)的宽度也取决于平台。使用来自<cstdint>
的类型来获得固定宽度的类型。尽管如此,它们只是依赖于平台的定义,因此它们的符号扩展行为仍然取决于平台。
Here 是关于类型大小的一个很好的几乎重复的问题。 here 是一个很好的关于字体大小关系的表格。
【讨论】:
【参考方案3】:类型提升,对应的符号扩展由 C++ 语言指定。
未指定但取决于平台的是所提供的整数类型的范围。 char
、short int
、int
、long int
和 long long int
甚至都符合标准,只要该范围满足 long long int
的 C++ 标准要求即可。在这样的平台上,不会发生扩大或缩小,但有符号无符号转换仍然可以改变值。
【讨论】:
以上是关于C++ 中的符号扩展是编译器选项,还是依赖于编译器或依赖于目标?的主要内容,如果未能解决你的问题,请参考以下文章
哪些编译器选项用于在 Visual Studio 中编译 C++ STL 类/函数?