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 个结果: 任何一个 -54294967291

我知道(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&lt;int32_t&gt;(small - large)。这执行无符号模运算,通过保持位模式(因此不是实现定义,但定义良好)转换为有符号,最后进行符号扩展。【参考方案2】:

符号扩展取决于平台,其中平台是编译器、目标硬件架构和操作系统的组合。

此外,正如 Paul R 提到的,内置类型(如 unsigned long)的宽度也取决于平台。使用来自&lt;cstdint&gt; 的类型来获得固定宽度的类型。尽管如此,它们只是依赖于平台的定义,因此它们的符号扩展行为仍然取决于平台。

Here 是关于类型大小的一个很好的几乎重复的问题。 here 是一个很好的关于字体大小关系的表格。

【讨论】:

【参考方案3】:

类型提升,对应的符号扩展由 C++ 语言指定。

未指定但取决于平台的是所提供的整数类型的范围。 charshort intintlong intlong long int 甚至都符合标准,只要该范围满足 long long int 的 C++ 标准要求即可。在这样的平台上,不会发生扩大或缩小,但有符号无符号转换仍然可以改变值。

【讨论】:

以上是关于C++ 中的符号扩展是编译器选项,还是依赖于编译器或依赖于目标?的主要内容,如果未能解决你的问题,请参考以下文章

哪些编译器选项用于在 Visual Studio 中编译 C++ STL 类/函数?

编译器不遵循 Visual Studio C++ 中的符号链接

PHP开发第一个扩展

告诉编译器我不再使用 C++ 中的命名空间

转载c++面试题

OJ提交题目中的语言选项里G++与C++的区别(转)