WinDerek
WinDerek

Reputation: 93

c++ overflow while calculating an arithmetic expression

#include <iostream>

int main(int argc, char* argv[]) {
    std::cout << 30000 * (5 * 30000 - 22207) / 2 + 50488389 << std::endl;

    return 0;
}

The right answer I want is 1967383389, but this program outputted -180100259 after running on my computer. It seemed it had overflowed. But why? How can I judge whether an arithmetic expression will overflow?

Upvotes: 3

Views: 507

Answers (4)

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

When you compile your example code, the compiler usually throws a warning identifying exactly the part of your expression that overflows

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

Live Demo


To overcome this, use all floating point constants to do this calculation:

#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;
}

Live Demo


How can I judge whether an arithmetic expression will overflow?

You can check the std::numeric_limits to see what are the maximum values, that are available with your compiler implementation and target CPU.


If you need the exact output as it was mentioned in your question, you can use I/O manipulators to bang the output in shape:

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

Live Demo

Upvotes: 4

Xaver
Xaver

Reputation: 1041

The reason for the overflow is the following:

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

is calculated from left to right. The result of calculating

30000 * (5 * 30000 - 22207)

is

3833790000

which is equivalent to

-461177296

modulo 2^32. Here the overflow occurs, because you use a 32-bit signed integer datatype.

Calculating

-461177296 / 2 + 50488389

then gives you

-180100259

To my knowledge, you cannot directly detect if an overflow had occurred. A possible way is to insert some assembly code into your C++ code and check the overflow flag. (But you have to have a good understanding of compilers and your system in order to get it working correctly.)

Upvotes: 0

surru
surru

Reputation: 26

It has overflowed because 30000 * (5 * 30000 - 22207) evaluates to 3833790000 which is greater than the largest integer an int can hold. Constants in an arithmetic expression are treated as int type and an arithmetic operation on two int types will result in int type only, so overflow occurred.

You can check whether an arithmetic expression containing constants will overflow by compiling the above code with g++ in unix/linux. It will throw a warning when compiling like this -

warning: integer overflow in expression [-Woverflow]

To get the desired answer you can just convert the type of any constant in 30000 * (5 * 30000 - 22207) to type long like long(30000) * (5 * 30000 - 22207)

Upvotes: 0

Matthew Fisher
Matthew Fisher

Reputation: 2336

To directly answer the question, overflow is controlled by size of the type. The common C++ types are int (32-bit), long (64-bit), float(32-bit) and double (64-bit). Depending on the platform the size of the types may vary so caution must be observed when dealing with large numbers. An int type of 64-bit is common these days for example but may be 16-bit on some old embedded platforms.

In your case, an intermediate value exceeded the sizeof(int) which appears to be 32 bit. The result will be rubbish. You can use floating point as the other suggested, that can lead to round of error in certain cases however.

If you stick with integer literals, can you can assign the type with a trailing letter. There are a lot of details here:

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

I prefer to use variable of the explicit size that I'm interested in int32_t or int64_t for example.

Hope that help,

--Matt

Upvotes: 0

Related Questions