【问题标题】:Comparing 8 bits types of different signedness (int8_t, uint8_t): is result deterministic?比较 8 位类型的不同符号(int8_t,uint8_t):结果是确定性的吗?
【发布时间】:2017-02-12 21:29:15
【问题描述】:

我跑this simple program:

#include <stdint.h>
#include <iostream>
int main() {
    uint8_t x = 100;
    int8_t y = -128;
    if (x < y) {
        std::cout << (int) x << " is less than " << (int) y << std::endl;
    } else {
        std::cout << (int) y << " is less than " << (int) x << std::endl;
    }
}

正确输出:

-128 is less than 100

起初我惊讶地发现没有生成签名警告。
然后我很惊讶没有发生错误的转换 (-128 -> 255),因此没有得到错误的结果。
然后我读了this

1 可以将除 bool、char16_t、char32_t 或 wchar_t 以外的整数类型的纯右值转换为整数转换等级 (4.13) 小于 int 等级的纯右值如果 int 可以表示源类型的所有值;否则,源纯右值可以转换为 unsigned int 类型的纯右值。 [§ 4.5]

Link to standard n2356
可以转换”是什么意思?是否会发生这种转换并因此该表达式是否会返回正确的值是否取决于编译器实现?

重点是编译器搜索将两个操作数转换为的通用类型,但我在标准中没有发现任何义务尽其所能 以便这种通用类型能够表示两种输入类型的所有可能值。

注意:我也标记了 C,因为这种情况似乎也适用于它。
相关问题:Comparison signed and unsigned char。还有this

【问题讨论】:

  • 阅读this postthese规则
  • 在 C 中,x,y&lt; 之前都被转换为 int(建议 1 个语言标签)
  • @chux 当然可以,但是基于哪个规则?
  • @CoryKramer 积​​分提升的规则说“可能”,而不是“应该”。有什么我想念的吗?
  • C11 §6.3.1.1 2 "... 如果int 可以表示原始类型的所有值...,则将该值转换为int;否则,将其转换到unsigned int. ..." 如果代码使用uint16_t/int16_t,那么在 16 位和 32 位 int 平台上会出现差异。

标签: c++ c gcc4.9


【解决方案1】:

是的,结果是确定性的,而不是(编译器的)实现定义的。这里遵循 C++11 的动机(其他应该可以这样做),遵循文档here(链接建议here

需要结合以下所有内容:

5.9 关系运算符

  1. [...]

  2. 通常的算术转换是在算术或枚举类型的操作数上执行的。

要找到通常的算术转换,我们需要转到第 5 章第 9 段的开头:

许多期望算术或枚举类型操作数的二元运算符会导致转换和产生 结果类型以类似的方式。目的是产生一个通用类型,这也是结果的类型。 这种模式称为通常的算术转换,其定义如下:

  • [...](枚举和浮点类型)
  • 否则,整体促销 (4.5) 应在两者上执行 操作数。[59]那么以下规则应适用于推广 操作数:

    • 如果两个操作数的类型相同,则无需进一步转换。
    • [...]

所以,整体提升,引用自 4.5:

整数类型的纯右值 bool、char16_t、char32_t 或 wchar_t,其整数转换 rank (4.13) 小于 int 的 rank 可以转换为 int 类型的纯右值,如果 int 可以表示所有 源类型的值;否则,源纯右值可以转换为无符号类型的纯右值 诠释。

所以:
我们有一个关系运算符,将使用通常的算术转换。这些有义务应用积分促销。 uint8_tint8_t 的整体升级可以升级到 int,因此必须应用。

因此,uint8_tint8_t 之间的比较被编译器转换为 2 int 之间的比较。没有不确定的行为。

有一个类似的 Q/A here(不过是关于 short 类型),这让我走上了正确的道路。

请注意以下矛盾:关系运算符返回一个布尔值 (5.9.1),但它们使用通常的算术转换来获得 2 个相同类型的操作数。但是,问题就在这里,通常算术转换的定义说,公共类型也将是结果的类型,而关系运算符则不是这样!

C11 不存在矛盾,关系运算符返回的结果确实是int。 (谢谢 chux)

【讨论】:

  • 你为什么一直提到未定义的行为。即使没有整数提升,仍然不会有未定义的行为,只是你所说的“错误结果”
  • 回复。最后一段,“目的是产生一个通用类型,这也是结果的类型。”指上一句“Many binary operator”——注意“Many”,而不是“All”
  • “关系运算符返回一个布尔值”是 C/C++ 的一部分。在 C 中,它们返回一个 int
  • @M.M 在我使用“未定义”代替“不确定”(实现定义)的答案中,现在更正了。如果短语说“许多二元运算符 [...] 导致转换 [...]”。如果某些东西导致转换,例如关系运算符,则该段落中的所有内容都适用,包括“这也是结果的类型”。反过来也可以看到:关系运算符(与许多其他二元运算符一样)需要通常的算术转换,这表示它转换为的类型也是结果的类型。 C++11 标准肯定是矛盾的。
  • 你描述的矛盾是错误的。 算术转换的结果是2个普通类型的值。然后将两个参数的公共类型版本应用于关系运算符,从而产生int/bool (C/C++)。
猜你喜欢
  • 2013-04-01
  • 1970-01-01
  • 2019-07-18
  • 2023-03-10
  • 2016-01-19
  • 2021-11-02
  • 1970-01-01
  • 2016-02-22
  • 2021-11-07
相关资源
最近更新 更多