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.
source to share
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 todouble
.Otherwise, if any of the operands is of type
float
, the other is converted tofloat
.Otherwise, if any of the operands is of type
long
, the other is converted tolong
.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
orlong
infloat
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).
source to share
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.
source to share
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) {
...
}
source to share