简体   繁体   中英

Why does the snippet throw a Floating point exception?

This

double what;
for (int i = 1; i < (long)pow(10, 7); i++)
    what = (i + i) / (i * i) - i; 

raises Floating point exception (core dumped). Why? I'm using clang++.

Depending on you platform, int is likely 32 or 64 bit wide.

From [basic.fundamental]/2 and [basic.fundamental]/3 [extract, emphasis mine]:

[basic.fundamental]/2

There are five standard signed integer types: “ signed char ”, “ short int ”, “ int ”, “ long int ”, and “ long long int ”. In this list, each type provides at least as much storage as those preceding it in the list . [...] Plain int s have the natural size suggested by the architecture of the execution environment; the other signed integer types are provided to meet special needs.

[basic.fundamental]/3

For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: “ unsigned char ”, “ unsigned short int ”, “ unsigned int ”, “ unsigned long int ”, and “ unsigned long long int ”, each of which occupies the same amount of storage and has the same alignment requirements as the corresponding signed integer type; [...]

The signed and unsigned integer types shall satisfy the constraints given in the C standard, section 5.2.4.2.1 .

We could go to the C11 Standard draft [extract, emphasis mine]:

5.2.4.2.1 Sizes of integer types <limits.h>

[...] Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign.

[...]

  • maximum value for an object of type int : INT_MAX +32767

[...]

This doesn't help us without knowing target/architectural details, however, so to simplify your questions, let's consider the example using fixed-width signed integers instead, and note that the following example is "fine" (in this context):

#include <cstddef>
#include <math.h>

int main() {    
    double what;
    for (int32_t i = 1; i < (int32_t)pow(10, 4); i++)
    {
        what = (i + i) / (i * i) - i; 
    }
    (void)what;
    return 0;
}

whereas the following results in a "Floating point exception" for the particular executions I have attempted (UB; dragons may fly out of our noses, see below):

#include <cstddef>
#include <math.h>

int main() {    
    double what;
    for (int32_t i = 1; i < (int32_t)pow(10, 5); i++)
    {
        what = (i + i) / (i * i) - i; 
    }
    (void)what;
    return 0;
}

The key here is that the maximum value of an int32_t is 2,147,483,647 , which means i * i will overflow for values of the magnitude of pow(10, 5) . Signed integer overflow is undefined behaviour (UB), and from there on anything goes . Likely in this case is that the UB, by coincidence , yields a value 0 from the overflow of the expression i * i , which in turn leads to division by zero (UB again) in the expression (i + i) / (i * i) , which, by further coincidence , is likely to be the root case of the Floating point exception .

I'm emphasizing on by coincidence here, as any point beyond UB makes for a useless target for logical analysis; the compiler vendor may assume we never have UB and does anything goes once we've entered the domain of UB. Any results we see should be considered coincidental, unless you are working in some non-standard dialect of C++ on a particular target architecture and hardware where eg a particular case such as signed integer overflow may be defined as non-standard implementation-defined (and thus specified by the particular implementation) rather than UB.

Your int overflows because 10^14 fits in no int that I have ever seen (note that int is platform dependent). On some platforms, 10^7 will already overflow. Signed overflow always results in undefined behaviour and sometimes results in 0 .

  • You might want to use an adequately sized type such as std::uint64_t or double directly to store the loop variable.
  • You compute pow for every single loop iteration. Compute this outside the loop once.
  • Your loop appears to throw away all but the last computation. Perhaps you could just compute that one?

The i < (long)pow(10, 7) condition in your for loop causes the integer (i * i) expression to become larger than its maximum value, resulting in integer overflow and undefined behavior . On some implementations, i might become 0 or 1 , causing the following expression: (i / i) to become 0 . This in turn might (in Visual Studio), for example, result in the Integer Division By Zero exception during the debugging.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM