How to properly compare long and floating primitives?

For some reason I need to compare two primitives: long and float.

Can the following code be used for this?

long a = 111L;
float b = 111.1f

if (a > b) {
  ...
}

      

I know float and float can be compared with some precision using epsilon value etc.

But how can I make the comparison more correct for my case?

Thanks everyone.

+3


source to share


3 answers


You can wrap both of them in BigDecimal

and they compare them:

long a = 111L;
float b = 111.1f;
BigDecimal first = new BigDecimal(a);
BigDecimal second = new BigDecimal(b, MathContext.DECIMAL32);
if (first.compareTo(second) > 0) { ... }

      

To understand why we are interested in having both operands under the same type, let's dig a bit into JLS 5.6.2 Binary Numeric Promotion :



When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that can be converted to a numeric type, the following rules apply so that:

  • If any operand is of a reference type, it is unboxed (ยง5.1.8).

  • A primitive conversion extension (ยง5.1.2) is applied to convert either or both operands, as specified in the following rules:

    • If any of the operands is of type double

      , the other is converted to double

      .

    • Otherwise, if any of the operands is of type float

      , the other is converted to float

      .

    • Otherwise, if any of the operands is of type long

      , the other is converted to long

      .

    • Otherwise, both operands are converted to type int

      .

With this, we can conclude that for comparison, the a > b

operand long

will be implicitly promoted to float

. This, however, can result in a loss of precision, as stated in JLS 5.1.2 Extending Primitive Conversion :

Expanding the conversion value int

or long

in float

or long doubled value, may cause loss of accuracy - i.e. the result may lose some of the least significant bit values. In this case, the resulting floating point value will be correctly rounded off the integer version, using the IEEE 754 closest mode (ยง4.2.4).

+3


source


If you want to use epsilon you can do

// allow for the smallest rounding error
if (a > b + Math.sign(b) * Float.ulp(b))

      

or

// assume six digits of precision
static final double ERR = 1e-6;

// allow for the smallest rounding error
if (a > b + Math.sign(b) * b * ERR)

      



You can drop Math.sign (b) if you can accept non-negative numbers.

However, the use of BigDecimal might be clearer in this case, see @kocko's answer.

BTW: The simplest change that will improve your accuracy is to use double

instead float

. double

literally one and a half billion times more accurate and if you don't have a billion of them then the extracted memory you are using doesn't matter.

+1


source


Comparison of longs and floats is directly supported by Java. However, there are some caveats:

If you are comparing long and float, long is converted to float because float is considered wider than long. However, this conversion may lose precision: the long value will be passed to the nearest float value; so for long a

and float b

, it a > b

may be false, although a

more than b

if b

in fact is the floating point value closest to b

.

Each long and float value can be represented BigDecimal

, so you can also use a class BigDecimal

to compare a long float:

long a = ...;
float b = ...;
if (BigDecimal.valueOf(a).compareTo(BigDecimal.valueOf(b)) > 0) {
    ...
}

      

0


source







All Articles