【问题标题】:How does C++ integer division work for limit and negative values?C++ 整数除法如何用于限制和负值?
【发布时间】:2016-12-10 17:24:33
【问题描述】:

我在 C++ 中使用整数除法面临一些奇怪的结果。我正在尝试计算:-2147483648 / -1

我得到的是 3 个不同场景下的 3 个不同结果:

int foo(int numerator, int denominator) {
    int res = numerator / denominator; // produces SIGFPE, Arithmetic exception interrupt

    cout << res << endl;
}

int main() {
    int res = -2147483648 / -1;
    cout << res << endl;               // prints -2147483648
    cout << -2147483648 / -1 << endl;  // prints 2147483648
    foo(-2147483648, -1);
    return 0;
}

为什么整数除法运算在不同情况下会产生不同的结果?

【问题讨论】:

  • 值得一提的是,在所有 -2147483648 / -1 行上显示 negative integral constant converted to unsigned type unary minus operator applied to unsigned type, result still unsigned 的代码无法在 Windows VS-2015 上编译
  • 简单回答here.
  • 这就是视觉工作室的做法:#define INT_MIN (-2147483647 - 1) // minimum (signed) int value
  • @usr 2147483648 甚至在考虑一元 - 之前就是 UB。所以 VC 定义通过避免 2147483648 文字来解决这个问题。
  • @flatmouse:我猜他们错误地使用了适用于积分促销的列表,即“int、unsigned int、long int、unsigned long int、long long int 或 unsigned long long int”或者他们使用用于二进制/八进制/十六进制文字的列表,而不是用于十进制文字的列表。至于为什么二进制/八进制/十六进制文字的规则不同,我相信这是因为程序员正在控制文字的长度,以位为单位。实际上,我希望看到auto x = 0x0000000000000001; 自动成为符合int64_least_t 理念的任何基本类型

标签: c++ integer precision 16-bit integer-division


【解决方案1】:

您的结果可能是INT_MAX+1,换句话说它可能会溢出。那是未定义的行为,任何事情都可能发生。例如,编译器可能会直接拒绝代码。

(一个系统可能有INT_MAX &gt;= 2147483648,但是你会期望你的3个测试用例得到相同的结果)

【讨论】:

    【解决方案2】:

    文字 -2147483648 / -1 由编译器计算为 2147483648,其数据类型的宽度足以容纳该值。

    当直接打印字面量时,它会正确打印值。

    当文字存储在res 中时,它被强制转换为intint 在您的系统上似乎是 32 位宽。值 2147483648 不能表示为 32 位有符号整数,因此强制转换会导致溢出。在您的系统上,此溢出导致值 -2147483648(可能使用二进制补码)。

    最后,当试图在运行时执行除法时(在foo函数中),由于溢出而出现SIGFPE异常(因为int数据类型不能代表结果)。

    请注意,所有这三个选项都依赖于平台相关行为:

    • 当文字计算溢出时编译器不会产生任何错误(或其他问题),而只是使用足够大的数据类型来保存结果
    • int 在存储文字时溢出的事实会生成该特定值(并且没有其他问题)
    • 在运行时溢出时抛出SIGFPE 异常

    【讨论】:

    • 编译期间AFAICT整数溢出是未定义行为;使用更大的数据类型是一种可能的行为,但正如 FirstSTep 所示,另一种行为是出错。
    • @MSalters :是的,我在最后一段中说了这么多。
    • 这也是错误的:每个平台都必须支持足够宽的数据类型; long long int 确实够宽。
    • @MSalters:你说得对——我习惯于根据 C++03 回答仅用“C++”标记的问题,但是自从 C++11 以来已经有一段时间了(和 C++14 )出来了,我应该与时俱进并调整这种习惯。我已经相应地重新措辞了我的答案。
    【解决方案3】:
    int res = -2147483648 / -1;
    cout << res << endl;               // prints -2147483648
    cout << -2147483648 / -1 << endl;  // prints 2147483648
    int res = numerator / denominator; // produces SIGFPE, Arithmetic exception interrupt
    

    请注意,integer literals 没有负数。

    没有负整数文字。诸如 -1 之类的表达式将一元减号运算符应用于由文字表示的值,这可能涉及隐式类型转换。

    文字2147483648大于int的最大值,所以它的类型将是long(或long long,取决于实现)。那么-2147483648的类型是long,计算结果(-2147483648 / -1)也是long

    对于第一种情况,long 类型的结果2147483648implicitly convertedint,但它大于int 的最大值,结果是实现定义的。 (看来结果是按照这里的表示规则(2的补码)环绕的,所以你得到了结果-2147483648。)

    对于第二种情况,直接打印出long类型的结果,所以你得到了正确的结果。

    对于第三种情况,您在两个ints 上进行计算,结果不适合结果类型(即int),signed integer arithmetic operation overflow 发生了,行为未定义。 (此处产生 SIGFPE,算术异常中断。)

    【讨论】:

    • -2147483648 可以是 long long,例如在 MSVC 上(有 LONG_MAX=2147483647
    • iirc 在旧版本的 C++ 中也可以是 32 位无符号长整数(这实际上不会改变最终结果,但会改变你到达那里的方式)。
    • @plugwash 我对“旧”C++ 了解不多。 :) 目前,它将是longlong long(C++11 起),除非使用后缀u,否则它不会是unsigned。表示是实现定义的,所以long 可能用 32 位整数表示。
    • @plugwash 我找到了它here,“在 C99 之前的 C 中(但不是在 C++ 中),不适合 long int 的无后缀十进制值允许具有 unsigned long int 类型。 "
    • 很好的解释!这篇文章应该是公认的答案。
    猜你喜欢
    • 1970-01-01
    • 2021-06-16
    • 2014-12-18
    • 2019-08-17
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 2020-08-18
    • 1970-01-01
    相关资源
    最近更新 更多