I think that question is pretty straight. but here is an examples.
Example below is OK. I can take rounding and no truncating was done here.
public static void main(String[] args) {
double d = 9.9;
long l = (long)d;
System.out.println(l);
}
Output:
9
And now number out of range of long:
public static void main(String[] args) {
double d = 99999999999999999999999999999999.9;
long l = (long)d;
System.out.println(l);
}
Output:
9223372036854775807
This one troubles me. I cannot continue work with completely different number. I would rather get an error or an exception.
Is there any way to detect this in Java?
Because floats and doubles cannot accurately represent the base 10 multiples that we use for money. This issue isn't just for Java, it's for any programming language that uses base 2 floating-point types. In base 10, you can write 10.25 as 1025 * 10-2 (an integer times a power of 10).
This will tell us the remainder; however, it doesn't take away the fact that Java won't round the value. It only truncates, rounding down toward zero. If we want to view the real value, we would have to use type casting - changing from one type to another - to convert to a double or float to get the precision.
Double rounding is often harmless, giving the same result as rounding once, directly from n 0 digits to n 2 digits. However, sometimes a doubly rounded result will be incorrect, in which case we say that a double rounding error has occurred.
In this article, I’ll show example conversions in C that are tainted by double rounding errors, and how attaching the ‘f’ suffix to floating-point literals prevents them-in gcc C at least, but not in Visual C++!
It’s 1 ULP above the correctly rounded value, due to a double rounding error. The compiler converted it by first rounding it up to 53 bits, giving 0.100000000000000000000001 1, and then rounding that up to 24 bits, giving 0.10000000000000000000001.
As in example 1, Visual C++ ignores the ‘f’ suffix, resulting in double rounding for both f and fd. Summary Floating-point literals are subject to double rounding when assigned to single-precision variables, resulting in incorrectly rounded decimal to floating-point conversions.
You can compare it with Long.MIN_VALUE
and Long.MAX_VALUE
:
public static boolean fitsLong(double d) {
return d >= Long.MIN_VALUE && d < Long.MAX_VALUE;
}
Somewhat more sofisticated approach is to use BigDecimal
:
double value = 1234567.9;
long l = BigDecimal.valueOf(value)
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // 1234568
double value = 99999999999999999999999999999999.9;
long l = BigDecimal.valueOf(value)
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ArithmeticException
This way you can control how the rounding is performed.
You may ask, why there's strict inequality in fitsLong
: d < Long.MAX_VALUE
. Actually that's because the Long.MAX_VALUE
itself cannot be represented as double number. When you cast (double)Long.MAX_VALUE
, there's not enough precision in double
type to represent it, so the closest representable value is selected which is 9223372036854775808.0
(Long_MAX_VALUE+1.0
). So were it d <= Long.MAX_VALUE
it would return true
for number which is actually a little bigger as in this comparison Long.MAX_VALUE
constant is promoted to double type. On the other hand Long.MIN_VALUE
can be exactly represented in double
type, thus here we have >=
.
Also it's interesting why the following works:
double value = -9223372036854775809.9; // Long.MIN_VALUE-1.9
System.out.println(fitsLong(value)); // returns true
That's because you actually did not subtract anything from the Long.MIN_VALUE
. See:
double d1 = Long.MIN_VALUE;
double d2 = -9223372036854775809.9;
System.out.println(d1 == d2); // true
The double precision is not enough to distinguish between -9223372036854775808
and -9223372036854775809.9
, so it's actually the same double number. During the compilation it's converted to binary form, and binary form for these two numbers is the same. Thus having compiled program you cannot distinguish whether -9223372036854775808
or -9223372036854775809.9
was in the source code.
If you feel that it's still the issue, construct the BigDecimal
from the String
:
long l = new BigDecimal("-9223372036854775808.2")
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ok, -9223372036854775808
long l = new BigDecimal("-9223372036854775808.9")
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ArithmeticException
When you cast a floating point type to an int
or long
, the result is either the nearest integer (rounding towards zero), or the MIN_VALUE
or MAX_VALUE
for int
or long
. See JLS 5.1.3.
Hence, one alternative would be to do the typecast and then test for the appropriate MIN_VALUE
or MAX_VALUE
.
Note that Long.MAX_VALUE
is 9223372036854775807
... which is the number your test program outputs!
(However, this doesn't work if you are casting a floating point type to byte
, char
or short
. See the link above for the explanation.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With