20

I remarked two things:

  1. std::numeric_limits<float>::max()+(a small number) gives: std::numeric_limits<float>::max().

  2. std::numeric_limits<float>::max()+(a large number like: std::numeric_limits<float>::max()/3) gives inf.

Why this difference? Does 1 or 2 results in an OVERFLOW and thus to an undefined behavior?

Edit: Code for testing this:

1.

float d = std::numeric_limits<float>::max();
float q = d + 100;
cout << "q: " << q << endl;

2.

float d = std::numeric_limits<float>::max();
float q = d + (d/3);
cout << "q: " << q << endl;
WildThing
  • 665
  • 2
  • 9
  • 17
  • 1
    can you please provide the code by means of which you "deduced" the behaviour 1 and 2? – Stefano Falasca Jul 11 '13 at 08:35
  • 1
    Intuitively: float can store 7 significant digits. So adding a small number to 3.4E38 doesn't add enough digits to make a difference. You'll have to add at least ~3.4E31. – Hans Passant Jul 11 '13 at 08:52
  • Here is the code: 1- float d=std::numeric_limits::max(); float q=d+100; cout<<"q: "<::max(); float q=d+(d/3); cout<<"q: "< – WildThing Jul 11 '13 at 08:55

2 Answers2

15

Formally, the behavior is undefined. On a machine with IEEE floating point, however, overflow after rounding will result in Inf. The precision is limited, however, and the results after rounding of FLT_MAX + 1 are FLT_MAX.

You can see the same effect with values well under FLT_MAX. Try something like:

float f1 = 1e20;     // less than FLT_MAX
float f2 = f1 + 1.0;
if ( f1 == f2 ) ...

The if will evaluate to true, at least with IEEE arithmetic. (There do exist, or at least have existed, machines where float has enough precision for the if to evaluate to false, but they aren't very common today.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Can i use the result of (FLT_MAX+ something positive) in further operations or tests without problem (undefined behavior)? – WildThing Jul 11 '13 at 08:52
  • 1
    @user2114690 Maybe. If you get `Inf`, this will propagate, and you can check for it at the end. If you're adding small values to `FLT_MAX` (and getting `FLT_MAX`), however, there's probably something wrong with your algorithm. There will be no undefined behavior, but there's a very good chance that your results are wrong. – James Kanze Jul 11 '13 at 09:10
  • `std::numeric_limits::is_iec559`. C requires `__STDC_IEC_559__` to be defined as a preprocessor symbol; there's a chance that this will also be defined in C++, if you need it in the preprocessor. – James Kanze Jul 11 '13 at 09:43
  • Ok, but In IEEE float implementation when an Overflow leads to INF, is it considered as undefined bahavior? – WildThing Jul 11 '13 at 09:45
  • 3
    @user2114690 Inf is a perfectly well-defined value as all the other actual number values, too. So no, if it is an IEEE-conformant implementation, overflow leading to Inf is in no way undefined behaviour. You can compute with Inf in every other reasonable way (i.e. compare it to other values or whatever), there is nothing wrong with it. In which way your particular computations can make sense of an Inf is up to you, though. – Christian Rau Jul 11 '13 at 09:56
  • @ChristianRau **"overflow leading to Inf is in no way undefined behaviour"** Thank you, that's all I wanted to know. – WildThing Jul 11 '13 at 10:03
  • @user2114690 The C++ standard defers to the IEC559 standard here. And what happens depends on whether you've activated the floating point exceptions. If you do nothing, all of the operations result in a value (possibly an `NaN` or `Inf`, which propagate), but on most platforms, you can modify this so that e.g. overflow causes a signal. – James Kanze Jul 11 '13 at 10:30
  • @JamesKanze What do you mean by **PROPAGATE**? – WildThing Jul 11 '13 at 11:00
  • 1
    @WildThing That operations involving an `Inf` or a `NaN` will typically return an `Inf` or a `NaN`. So if you overflow anywhere in an expression, the results of the expression will be `Inf` or `NaN`. (There are a few exceptions, like `1 / Inf` returning `0`, where the results are mathematically defined, or `Inf / Inf` returning `NaN`, where the results are indeterminate. – James Kanze Jul 11 '13 at 15:52
1

It depends on what you are doing. If the float "overflow" comes in an expression which is directly returned, i.e.

return std::numeric_limits::max() + std::numeric_limits::max();

the operation might not result in an overflow. I cite from the C standard [ISO/IEC 9899:2011]:

The return statement is not an assignment. The overlap restriction of subclause 6.5.16.1 does not apply to the case of function return. The representation of floating-point values may have wider range or precision than implied by the type; a cast may be used to remove this extra range and precision.

See here for more details.

Stefano Falasca
  • 8,837
  • 2
  • 18
  • 24
  • 2
    That's a completely different issue. Typically, machines will do the arithmetic in `double` precision, or more, and most compilers will return the value in a register with `double` precision or more. This can lead to some surprising results, but it's not the issue he's complaining about. (Whether this is legal C++ is very debatable---I don't think it is. But it _is_ what compilers do.) – James Kanze Jul 11 '13 at 08:30
  • This is indeed allowed by the standard. You are right, this can lead to some (very) surprising results like the one cited in the reference above – Stefano Falasca Jul 11 '13 at 08:33
  • You quote the C standard. In C++, returning a value is the same as initializing a variable of the given type using copy initialization (see §8.5/14). Unless there's text elsewhere that makes an explicit exception, this means that the return value must be "as if" a `float` variable was initialized and then read. (Not that this makes any difference in practice, since compilers don't respect it.) – James Kanze Jul 11 '13 at 08:36
  • 2
    The real surprise is when you have a pure function (results depend only on its arguments), and you do `if ( f(x) == f(x) )`, and the conditional evaluates false. (The compiler spills one of the results to memory, with the correct precision, and then compares it with the returned value of the second call, with extra precision.) – James Kanze Jul 11 '13 at 08:39
  • from this http://stackoverflow.com/questions/10239741/do-doubles-suffer-from-overflow i expect that float or double does not OVERFLOW thus there can not be an undefined behavior. Am I right? – WildThing Jul 11 '13 at 09:01
  • 1
    @JamesKanze very nice example of why floating-point comparison is not reliable/dangerous --- I was recently shouted down/booed for making this statement in the C++ lounge (but I didn't have this nice example). – Walter Jul 11 '13 at 09:01
  • @user2114690 **No** overflow of double does not mean wrapping around as for integers, but going outside the representable range of numbers toward plus or minus infinity (toward zero for underflow). – Walter Jul 11 '13 at 09:03
  • @Walter what about the undefined behavior? is it generated or not? – WildThing Jul 11 '13 at 09:04
  • @user2114690 If doubles or floats overflow, the behaviour is undefined, so the compiler/implementation can do what they want. What actually happens depends on your hardware settings etc. One frequent possibility is silent NaN, when most subsequent operations with that NaN cause further NaN, but comparisons may give you surprises... – Walter Jul 11 '13 at 09:07
  • @Walter It's a very special case, and doesn't apply everywhere. In particular, it doesn't apply for `double` on a Sparc, or if you're using the `xmm` registers on an Intel. – James Kanze Jul 11 '13 at 09:12
  • @Walter Formally, overflow is undefined behavior. On an IEEE implementation, it leads to `Inf`. On a lot of others, it will cause the program to abort. – James Kanze Jul 11 '13 at 09:13
  • @Walter I've never seen overflow give a `NaN`. `NaN` is for undefined operations, like `0.0 / 0.0`. Overflow on an IEEE gives `Inf`. – James Kanze Jul 11 '13 at 09:14
  • @JamesKanze You are absolutely correct, I confused this. (but it's UB anyway). – Walter Jul 11 '13 at 09:16
  • 1
    I suggest some clarifications: Change the first sentence to “Formally, the behavior is not defined by the C standard.” (It is important to understand that multiple specifications may apply to a program, and definitions of behavior can be provided by more than just the C standard.) Change the second sentence to “In a C implementation with IEEE-754 floating point…”. (The C implementation determines the behavior, not the machine. A C implementation may provide IEEE-754 in software or may neglect IEEE-754 features of the machine, often by running in a compromised mode, such as flushing denormals.) – Eric Postpischil Jul 12 '13 at 02:06
  • Also, explain overflow after rounding. An intuitive consideration of overflow in round-to-nearest mode could say that any finite result above the greatest representable finite value is rounded to that greatest value, since the exact result is obviously nearer to it than to infinity. Therefore, overflow after rounding would be impossible for any operation with a finite mathematical result. So the rules by which IEEE 754 rounding operates in this region ought to be explained. – Eric Postpischil Jul 12 '13 at 02:09