Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Standards compliant way to compare float to integral?

Let's say I have two objects i and f of respective types I and F. I know that std::is_integral<I>::value is true, and std::is_floating_point<F>::value is true.

Is there a fully standards-compliant way to find out if the value of i is smaller than the value of f? Note the emphasis on 'fully standards-compliant', for this question I'm only interested in answers that are backed up by guarantees from the C++ standard.


The trivial implementation i < I(f) doesn't work, because the value of f may not fit inside i. The trivial implementation F(i) < f doesn't work either, because the precision of f may not be enough to represent i, causing i to get rounded to a value equal to f (if you have IEEE754 floats, 16777219 < 16777220.f fails).

But here comes the real dilemma: if you want to use std::numeric_limits::max to alleviate these problems, your back to the original problem of comparing floats and integers! This is because the type of std::numeric_limits::max is equal to the original type.

like image 215
orlp Avatar asked Apr 27 '15 02:04

orlp


People also ask

Can you compare float and int?

Integers and floats are two different kinds of numerical data. An integer (more commonly called an int) is a number without a decimal point. A float is a floating-point number, which means it is a number that has a decimal place. Floats are used when more precision is needed.

What is a better way to compare floating point values?

To compare two floating point values, we have to consider the precision in to the comparison. For example, if two numbers are 3.1428 and 3.1415, then they are same up to the precision 0.01, but after that, like 0.001 they are not same.

What happens if we compare int and float?

Casting the int to a float explicitly will do absolutely nothing. The int will be promoted to a float for purposes of comparison anyway.

Why should we never compare floating point values by using exact equality comparison?

Comparing for equality Floating point math is not exact. Simple values like 0.1 cannot be precisely represented using binary floating point numbers, and the limited precision of floating point numbers means that slight changes in the order of operations or the precision of intermediates can change the result.


1 Answers

  1. If f is outside the range of I, you can tell the result just by its sign.

  2. If f is inside the range of I but too big to have a fractional part, compare it as an integer.

  3. Otherwise, it's safe to cast i to F because the rounding will not change the result of the comparison: f is already smaller than any value of I that would be rounded.

.

template< typename I, typename F >
std::enable_if_t< std::is_integral_v< I > && std::is_floating_point_v< F >,
bool > less( I i, F f ) {
    // Return early for operands of different signs.
    if ( i < 0 != f < 0 ) return i < 0;

    bool rev = i >= 0;
    if ( rev ) {
        f = - f; // Make both operands non-positive.
        i = - i; // (Negativity avoids integer overflow here.)
    }

    if ( f < /* (F) */ std::numeric_limits<I>::min() ) {
        // |i| < |f| because f is outside the range of I.
        return rev;
    }
    if ( f * std::numeric_limits<F>::epsilon() <= -1 ) {
        // f must be an integer (in I) because of limited precision in F.
        I fi = f;
        return rev? fi < i : i < fi;
    }
    // |f| has better than integer precision.
    // If (F) |i| loses precision, it will still be greater than |f|.
    return rev? f < i : i < f;
}

Demo: http://coliru.stacked-crooked.com/a/b5c4bea14bc09ee7

like image 102
Potatoswatter Avatar answered Oct 26 '22 19:10

Potatoswatter