计算算术表达式时出现c++溢出

c++ overflow while calculating an arithmetic expression

本文关键字:c++ 溢出 表达式 计算      更新时间:2023-10-16
#include <iostream>
int main(int argc, char* argv[]) {
    std::cout << 30000 * (5 * 30000 - 22207) / 2 + 50488389 << std::endl;
    return 0;
}

我想要的正确答案是1967383389,但这个程序在我的电脑上运行后输出了-180100259。它似乎已经溢出了。但为什么呢?如何判断算术表达式是否会溢出?

编译示例代码时,编译器通常会抛出一个警告,准确地识别表达式中溢出的部分

main.cpp:4:24: warning: integer overflow in expression [-Woverflow]
     std::cout << 30000 * (5 * 30000 - 22207) / 2 + 50488389 << std::endl;
                  ~~~~~~^~~~~~~~~~~~~~~~~~~~~

实时演示


为了克服这一点,使用所有浮点常数进行计算:

#include <iostream>
#include <cmath>
int main(int argc, char* argv[]) {
    std::cout << std::trunc(30000.0 * (5.0 * 30000.0 - 22207.0) / 2.0 + 50488389.0) << std::endl;
                              // ^^     ^^        ^^        ^^     ^^           ^^
    return 0;
}

实时演示


如何判断算术表达式是否会溢出?

您可以检查std::numeric_limits以查看编译器实现和目标CPU可用的最大值。


如果你需要问题中提到的确切输出,你可以使用I/O操纵器将输出按形状:

std::cout  << std::fixed << std::setprecision(0) 
        // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           << std::trunc(30000.0 * (5.0 * 30000.0 - 22207.0) / 2.0 + 50488389.0) << std::endl;

实时演示

要直接回答这个问题,溢出由类型的大小控制。常见的C++类型有int(32位(、long(64位(、float(32位的(和double(64位的(。根据平台的不同,类型的大小可能会有所不同,因此在处理大量数据时必须小心。例如,64位的int类型现在很常见,但在一些旧的嵌入式平台上可能是16位。

在您的案例中,一个中间值超过了sizeof(int(,它看起来是32位。结果将是垃圾。您可以使用浮点作为其他建议,但在某些情况下可能会导致一轮错误。

如果你坚持使用整数文字,你可以用一个尾随字母来分配类型吗。这里有很多细节:

http://en.cppreference.com/w/cpp/language/integer_literal

例如,我更喜欢使用我感兴趣的显式大小的变量int32_t或int64_t。

希望能帮上忙,

--Matt

它已经溢出,因为30000*(5*30000-22207(的计算结果为3833790000,大于int可以容纳的最大整数。算术表达式中的常量被视为int类型,对两个int类型进行算术运算将只产生int类型,因此发生溢出。

您可以通过在unix/linux中使用g++编译上述代码来检查包含常量的算术表达式是否会溢出。当像这样编译时,它会抛出一个警告-

警告:表达式[-Woverflow中存在整数溢出

要获得所需答案,您只需将30000*(5*30000-22207(中任何常数的类型转换为long类型,如long(30000(*(5*30000-22207(

溢出的原因如下:

30000 * (5 * 30000 - 22207) / 2 + 50488389

从左到右计算。计算结果

30000 * (5 * 30000 - 22207)

3833790000

相当于

-461177296

模2^32。这里发生溢出,因为您使用的是32位带符号的整数数据类型。

计算

-461177296 / 2 + 50488389

然后给你

-180100259

据我所知,您无法直接检测是否发生了溢出。一种可能的方法是在C++代码中插入一些汇编代码并检查溢出标志。(但你必须对编译器和你的系统有很好的了解,才能让它正常工作。(