【问题标题】:Allowing signed integer overflows in C/C++在 C/C++ 中允许有符号整数溢出
【发布时间】:2011-05-13 13:10:23
【问题描述】:

希望有符号整数在变得太大时溢出。如何在不使用下一个最大数据类型的情况下(或者当我已经在 int128_t 时)实现这一点?

例如,使用 8 位整数 19*12 通常是 260,但我想要结果 1 11 10 01 00 与第 9 位截断,因此 -27。

【问题讨论】:

  • 不是19 * 12 == 228?即使是 8 位无符号整数?

标签: c++ c integer-overflow


【解决方案1】:

有符号溢出在 C 中未定义,that's for real

一个解决方案如下:

signed_result = (unsigned int)one_argument + (unsigned int)other_argument;

上述解决方案在从unsignedint 的最终转换中涉及实现定义的行为,但不要调用未定义的行为。对于大多数编译平台的实现定义选择,结果正是您期望的二进制补码结果。

最后,an optimizing compiler for one of the numerous platforms on which implementation-defined choices force the compiler to give you the behavior you expect 会将上面的代码编译成明显的汇编指令。

或者,如果您使用 gcc,那么选项 -fwrapv/-fno-strict-overflow 可能正是您想要的。它们为签名溢出环绕的标准提供了额外的保证。我不确定两者之间的区别。

【讨论】:

  • 这个答案的第二段最接近这个问题的正确答案。这就是我要走的路。
  • 您对signed_result = unsigned_expression 的解决方案没有明确定义,因为标准中的以下短语:“如果新类型已签名并且无法在其中表示值;要么结果是实现-已定义或引发实现定义的信号”C11 标准的第 6.3.1.3 节。
  • @wich 我看到“上述解决方案涉及实现定义的行为”这句话。我的回答中包含的内容令人困惑,至少对某些读者而言。您认为应该如何措辞更好?
  • 首先我不会称其为解决方案,充其量它是一种不可移植的无保证解决方法。至于您引用的句子的措辞,可能类似于“只有当编译器按照您希望的方式实现有符号到无符号整数转换时,上述内容才会执行您想要的操作,因为标准中没有定义这种行为。”这将很好地引向您的以下句子,尽管您可能还想区分未定义的内容(无符号到有符号的转换)和未定义的内容(其他所有内容。)
  • 我看到第二句话有点尴尬的措辞是您答案的先前版本的遗留物,因为它看起来整个过程是实现定义的,而不仅仅是无符号到有符号的转换.我会删除冒号,并明确冒号后面的位是对代码应该做什么的解释。
【解决方案2】:

根据 C 和 C++ 标准,有符号整数溢出是未定义的。如果没有特定的平台,就无法实现您想要的。

【讨论】:

    【解决方案3】:

    可以以正确的标准 C 方式执行此操作,只要您可以访问与您的 signed 类型宽度相同的 unsigned 类型(即多一个值位) .用int64_t演示:

    int64_t mult_wrap_2scomp(int64_t a, int64_t b)
    {
        uint64_t result = (uint64_t)a * (uint64_t)b;
    
        if (result > INT64_MAX)
            return (int64_t)(result - INT64_MAX - 1) - INT64_MAX - 1;
        else
            return (int64_t)result;
    }
    

    这不会产生任何有问题的中间结果。

    【讨论】:

      【解决方案4】:

      您可以围绕 int 创建一个目标包装器,但这会涉及大量开销代码。

      【讨论】:

        【解决方案5】:

        假设二进制补码有符号整数算术(如今这是一个合理的假设),对于加法和减法,只需转换为无符号数即可进行计算。对于乘法和除法,确保操作数为正,转换为无符号,计算并调整符号。

        【讨论】:

          【解决方案6】:

          听起来你想做 unsinged 整数运算,然后将结果填充到有符号整数中:

          unsigned char a = 19;
          unsigned char b = 12;
          
          signed char c = (signed char)(a*b);
          

          应该给你你正在寻找的东西。如果没有,请告诉我们。

          【讨论】:

          • 该代码仍然表现出未定义的行为。 (仍然是签名溢出)
          • @Billy - 一点也不反对你......但我已经看到了大量依赖于无符号整数计数器值的有符号减法的 C 代码(通常是实时/嵌入式的东西),在为了获得“滚动增量”。我认为有符号到无符号的转换是明确定义的,而 overflow 则不是。只是出于好奇,您能否提供对声明所有这些内容为“未定义行为”的 C 标准相关部分的参考?
          • @Lee:1. 我没有对此投反对票。 2. 我看不出那个演员表不是溢出的。我没有具体的参考资料说演员阵容无效,但我也没有具体的参考资料说它也是有效的。
          • 它不是未定义的,它是实现定义的。转换为太小而无法容纳值的有符号类型的强制转换或隐式转换不被标准视为溢出。而是执行实现定义的转换或引发实现定义的信号。
          • 如果你想避免实现定义,试试这个:signed char c; *(unsigned char *)&c = (a*b);
          【解决方案7】:

          使用更大的数据类型。使用GMP,您将拥有您可能需要的所有空间。

          【讨论】:

          • 问题专门询问如何在没有更大数据类型的情况下做到这一点。 GMP 总是一个不好的答案,因为它喜欢在没有警告的情况下abort() 调用程序。
          • 我理解的方式“(或者当我已经在 int128_t 时)?”是他愿意尽可能使用更大的数据类型。当请求的内存过多时,GMP 中止。
          猜你喜欢
          • 1970-01-01
          • 2021-03-15
          • 2013-04-10
          • 2018-09-07
          • 2014-12-09
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          • 2017-05-13
          相关资源
          最近更新 更多