19

Logically speaking, given the nature of floating point values, the maximum and minimum representable values of a float are positive and negative infinity, respectively.

Why, then, are FLT_MAX and FLT_MIN not set to them? I understand that this is "just how the standard called for". But then, what use could FLT_MAX or FLT_MIN have as they currently lie in the middle of the representable numeric range of float? Other numeric limits have some utility because they make guarantees about comparisons (e.g. "No INT can test greater than INT_MAX"). Without that kind of guarantee, what use are these float limits at all?

A motivating example for C++:

#include <vector>
#include <limits>

template<typename T>
T find_min(const std::vector<T> &vec)
{
    T result = std::numeric_limits<T>::max();
    for (std::vector<T>::const_iterator p = vec.start() ; p != vec.end() ; ++p)
        if (*p < result) result = *p;
    return result;
}

This code works fine if T is an integral type, but not if it is a floating point type. This is annoying. (Yes yes, the standard library provides min_element, but that is not the point. The point is the pattern.)

Nemo
  • 70,042
  • 10
  • 116
  • 153
tenfour
  • 36,141
  • 15
  • 83
  • 142
  • 4
    Infinity is not a *value*. FLT_MAX is the largest representable *real value*. – Paul R Nov 01 '11 at 22:31
  • Great question. Of course, in C++ you can (and should) use `numeric_limits::min()`, `numeric_limits::max()`, and `numeric_limits::infinity()`... But I always wondered the same thing myself. – Nemo Nov 01 '11 at 22:32
  • 2
    "they currently lie in the middle of the representable numeric range of float" - what do you mean by that? `FLT_MAX` should lie at the *edge* of the representable numeric range. – Robᵩ Nov 01 '11 at 22:37
  • @Rob: the edge would be `INF`. `FLT_MAX` is one value less than the actual "no value can be represented greater than this constant". The distinction between "finite" / "real" seems much more like an implementation detail to float than one useful to *users* of float. If INF must test greater than all other values of a float, it seems like it should logically be FLT_MAX. I get that I'm probably wrong; I just want to understand why. – tenfour Nov 01 '11 at 22:40
  • @tenfour: The distinction between finite / non-finite numbers is not an implementation detail. (It's implementation-specified, but definitely not a detail.) – Dietrich Epp Nov 01 '11 at 22:45
  • @tenfour: I added a "motivating example" for C++. If you do not like it, feel free to delete. – Nemo Nov 01 '11 at 22:46
  • @Nemo thanks, it's a good illustration – tenfour Nov 01 '11 at 22:48
  • @tenfour, thanks for explaining your reasoning. FLT_MAX makes sense to me, because I think of +inf as an exceptional condition, not a value at all. I think of FLT_MAX as the greatest possible value. So, I couldn't see your reasoning. I'll let the other explanations convince you of why FLT_MAX is reasonably defined. – Robᵩ Nov 01 '11 at 23:17
  • @tenfour Would you explain mere mortals like us on why the code above will not work for non-integral types? Or could you point me to some question is SO? – legends2k Dec 13 '12 at 14:01
  • @Nemo Is it the case where one of the members of `vector` is set to `infinity` where a `max` check will fail since for non-integral types max < infinity? OTOH this would work for all integrals since they don't've infinity but only the max, which is the largest that can be represented in that type and hence max > infinity for them. – legends2k Dec 13 '12 at 15:20
  • http://stackoverflow.com/questions/13861629/why-is-numeric-limitsintmax-numeric-limitsintinfinity – legends2k Dec 13 '12 at 15:23

4 Answers4

22

The purpose of FLT_MIN/MAX is to tell you what the smallest and largest representable floating-point numbers are. Infinity isn't a number; it's a limit.

what use could FLT_MAX or FLT_MIN have as they currently lie in the middle of the representable numeric range of float?

They do not lie in the middle of the representable range. There is no positive float value x which you can add to FLT_MAX and get a representable number. You will get +INF. Which, as previously stated, is not a number.

This code works fine if T is an integral type, but not if it is a floating point type. This is annoying. (Yes yes, the standard library provides min_element, but that is not the point. The point is the pattern.)

And how doesn't it "work fine?" It gives you the smallest value. The only situation where it doesn't "work fine" is if the table contains only +INF. And even in that case, it returns an actual number, not an error-code. Which is probably the better option anyway.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
18

FLT_MAX is defined in section 5.2.4.2.2(9) as

maximum representable finite floating-point number

Positive infinity is not finite.

FLT_MIN is defined in section 5.2.4.2.2(10) as

minimum normalized positive floating-point number

Negative infinity is neither normalized nor positive.

Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
  • I believe the question is, why does the standard define them this way instead of the (more natural/useful) +-inf? – Nemo Nov 01 '11 at 22:37
  • 8
    FLT_MAX and FLT_MIN are more useful than +-inf, you know what the latter are anyway. One could say the names aren't perfect, but the values are what one needs to know. – Daniel Fischer Nov 01 '11 at 22:45
  • @DanielFischer: in order to do what? – tenfour Nov 01 '11 at 22:45
  • 10
    In order to know at what point your calculations are going to break down. – Raymond Chen Nov 01 '11 at 22:47
  • In theory that makes sense but I am struggling to think of an actual example where it could work, reliably. Because of precision issues I don't see how you can use FLT_MAX to reliably test in advance for overflow situations. Anyone have an example? – tenfour Nov 01 '11 at 23:16
  • @RaymondChen: Can you cite an actual example where you have seen them used this way? I ask because I use +- infinity all the time (they are often the most natural "default" value) but I have never used FLT_MAX or FLT_MIN – Nemo Nov 01 '11 at 23:25
  • 2
    Most people just cross their fingers and hope that `FLT_MIN` and `FLT_MAX` are satisfactory. I guess you might write `if (v < FLT_MIN * 2) error("Dangerously close to underflow, results not trustworthy");` But since most implementations have settled on IEEE format, the values of `FLT_MIN` and `FLT_MAX` are pretty much constants now, so you can precondition your calculations instead of having to do dynamic checks. Setting them to +Inf and -Inf don't really tell you much about the meaningful range of your `float` type. – Raymond Chen Nov 02 '11 at 00:03
  • @Nemo: I have an example in my answer. – R.. GitHub STOP HELPING ICE Nov 02 '11 at 07:10
8

Unlike integer types, floating-point types are (almost?) universally symmetric about zero, and I think the C floating-point model requires this.

On two's-complement systems (i.e., almost all modern systems), INT_MIN is -INT_MAX-1; on other systems, it may be -INT_MAX. (Quibble: a two's-complement system can have INT_MIN equal to -INT_MAX if the lowest representable value is treated as a trap representation.) So INT_MIN conveys information that INT_MAX by itself doesn't.

And a macro for the smallest positive value would not be particularly useful; that's just 1.

In floating-point, on the other hand, the negative value with the greatest magnitude is just -FLT_MAX (or -DBL_MAX, or -LDBL_MAX).

As for why they're not Infinity, there's already a way to represent infinite values (at least in C99): the macro INFINITY. That might cause problems for some C++ applications, but these were defined for C, which doesn't have things like std::numeric_limits<T>::max().

Furthermore, not all floating-point systems have representations for infinity (or NaN).

If FLT_MAX were INFINITY (on systems that support it), then there would probably need to be another macro for the largest representable real value.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • "Furthermore, not all floating-point systems have representations for infinity (or NaN)." All the more reason to want a symbolic way to reference "the largest possible float", where the definition of "largest possible" is that it compares >= any other – Nemo Nov 01 '11 at 23:24
  • 2
    @Nemo: There is; it's called `HUGE_VAL` (or `HUGE_VALF`, or `HUGE_VALL`). – Keith Thompson Nov 02 '11 at 01:30
  • Then why is LDBL_MAX infinity? – Michael Feb 07 '20 at 02:48
  • @Michael: It isn't. On my system, for example, `printf("LDBL_MAX = %Lg\n", LDBL_MAX);` prints `LDBL_MAX = 1.18973e+4932`. Are you sure you're printing (or otherwise examining) it correctly? The standard specifically says the value of `LDBL_MAX` is finite. – Keith Thompson Feb 07 '20 at 05:22
  • 1
    Thanks, turned out I was missing an L in the format string – Michael Feb 07 '20 at 16:49
8

I would say the broken pattern you're seeing is only an artifact of poor naming in C, whereas in C++ with numeric_limits and templates, it's an actual semantic flaw that breaks template code that wants to handle both integer and floating point values. Of course you can write a little bit of extra code to test if you have an integer or floating point type (e.g. if ((T)1/2) /* floating point */ else /* integer */) and the problem goes away.

As for why somebody would care about the values FLT_MIN and FLT_MAX give you, they're useful for avoiding underflow and overflow. For example, suppose I need to compute sqrt(x²-1). This is well-defined for any floating point x greater than or equal to 1, but performing the squaring, subtraction, and square root could easily overflow and render the result meaningless when x is large. One might want to test whether x > FLT_MAX/x and handle this case some other way (such as simply returning x :-).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 2
    OK good answer. Although I think I would go with `std::max(std::numeric_limits::infinity(), std::numeric_limits::max()`. You never know if `T` is a custom fixed-point representation or something :-) – Nemo Nov 02 '11 at 16:17
  • Personally I prefer handle the overflow instead ovoiding it. Something like `y=sqrt(x²-1); if(isFinite(y)) return y; else return x;` – CodesInChaos Nov 05 '14 at 15:03