3

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

Can I use following code for this?

long a = 111L;
float b = 111.1f

if (a > b) {
  ...
}

I know, that float and float can be compared to some accuracy with using epsilon value and etc.

But how can I perform comparison for my case more correctly?

Thanks to all.

Developer87
  • 2,448
  • 4
  • 23
  • 43
  • I gave an answer [here](http://stackoverflow.com/questions/30628945/codeblocks-c-bug) which can help you. – mazhar islam Jul 31 '15 at 11:22
  • 2
    check out this http://stackoverflow.com/questions/7392167/comparing-float-and-double-primitives-in-java – Ajith John Jul 31 '15 at 11:25
  • How do you define “correctly”? The code above will tell you that `111` is *not* bigger than `111.1f`, which is what I call “correct” for myself. So I don’t see any reason to do it differently… – Holger Jul 31 '15 at 11:39

4 Answers4

3

You can wrap both of them in BigDecimal and them 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) { ... }

In order to understand why we're interested to have both of the 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 is convertible to a numeric type, the following rules apply, in order:

  1. If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).

  2. Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:

    • If either operand is of type double, the other is converted to double.

    • Otherwise, if either operand is of type float, the other is converted to float.

    • Otherwise, if either operand 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 the comparison a > b the long operand will be implicitly promoted to float. This, however, can end up with loss of precision, as stated in JLS 5.1.2 Widening primitive conversion:

A widening conversion of an int or a long value to float, or of a long value to double, may result in loss of precision - that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value, using IEEE 754 round-to-nearest mode (§4.2.4).

Konstantin Yovkov
  • 62,134
  • 8
  • 100
  • 147
1

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 assume non-negative numbers.

However, using BigDecimal may be clearer in this case, see @kocko's answer.

BTW: The simplest change which would improve your accuracy is to use double instead of float. double is literally half a billion times more accurate and unless you have billions of them the extract memory you use won't matter.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • The differences in the accuracy of `float` and `double` are unlikely to be ever relevant when comparing the value with a `long`. What matters more is that `long` with a large absolute value can’t be precisely expressed in a `float`, however, if the source value doesn’t exist in a higher precision, converting it to `double` doesn’t magically add more precision. But I don’t see how an epsilon can improve a `>` comparison anyway. Using an epsilon makes sense for an equality test but not for ordering… – Holger Jul 31 '15 at 11:59
  • @Holger What is you doubt about comparison making sense. Surely a:100 > b:10 even if the error is 1. Does the error have to be 0 for you to be able to say 100 > 10 with the bounds of error? – Peter Lawrey Jul 31 '15 at 17:47
  • Well, let’s stay at the `b=10` example. The original code is `a>b`, thus effectively performing `a>10.0`. Now you replace it by `a > b + Math.signum(b) * Math.ulp(b)` which means you will effectively perform `a > 10.000000000000002`. Why do you think `a > 10.000000000000002` is an improvement over `a > 10.0`? It makes no difference if `a` is `long` anyway, but if `a` was a floating point value, the introduction of an epsilon meant, the condition is `false` if `a` happens to be `10.000000000000001`. Can’t see the improvement here. – Holger Jul 31 '15 at 17:59
  • The point is, an equality check changes to a range check by the introduction of an epsilon which makes it more tolerant. In contrast, a `>` or `<` check alone will always be a sharp condition as it always has to make a cut telling smaller and bigger values apart. Shifting that hard border by an epsilon doesn’t make it more tolerant, it just modifies the border value. – Holger Jul 31 '15 at 18:03
  • @Holger say we are comparing one billion, one hundred and one billion. You could not say 1,000,000,100 > 1,000,000,000 as float has an error of about 1 in 10^6 `long` has up to 18 digits of precision, but float only has about 6-7. This means that the long can be a trillion more than the float, but still be inside representation error. – Peter Lawrey Aug 01 '15 at 05:55
  • @Holger I don't "consider 999,999,900 to be bigger than 1,000,000,100" nor does the code. – Peter Lawrey Aug 17 '15 at 08:52
  • 1
    I misread you comment— So your saying you consider code which says that `1000000100L` is not bigger than `1000000000F` as an improvement? I don’t. As said before, when someone tests for equality, it makes sense. Then, there are values not considered bigger or smaller (in other words, equal according to an epsilon), but for a single, “bigger than” operator, it’s impossible to say which behavior is better without context. – Holger Aug 17 '15 at 09:25
  • 1
    @Holger I agree context does matter. This is why I suggested a conservative approach. Even in the case of equality, you can't say two number are equal, you can only say when two numbers are not equal. – Peter Lawrey Aug 17 '15 at 09:27
0

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

If you compare a long and float, the long is converted to a float because float is considered a wider type than a long. However, this conversion might lose precision: the long value will be cast to the nearest float value; so for a long a and a float b, a > b might be false although a is larger than b if b is actually the float value nearest to b.

Every long and float value can be represented by a BigDecimal, so you can also use the BigDecimal class to compare a long an a float:

long a = ...;
float b = ...;
if (BigDecimal.valueOf(a).compareTo(BigDecimal.valueOf(b)) > 0) {
    ...
}
Hoopje
  • 12,677
  • 8
  • 34
  • 50
0

use this code easily:

Double.compare(longVar * 1., floatVar)
M2E67
  • 937
  • 7
  • 23