Java DecimalFormat rounding giving unexpected output - decimalformat

As per DecimalFormat documentation, the default rounding mode is HALF_EVEN.
HALF_EVEN mode definition :
Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.
https://docs.oracle.com/javase/7/docs/api/java/math/RoundingMode.html#HALF_EVEN
However, the below code gives the output 0.01
private static final DecimalFormat decimalFormat = new DecimalFormat("#.##");
System.out.print(decimalFormat.format(0.015));
For rounding to precision 2, the 'neighbours' here would be 1 and 2, both are equidistant from 5, so ideally it should pick the even one and round to 0.02. Am I missing something?

Related

Why does Math.Round(1.275, 2) equal 1.27?

Rounding under Visual Studio 2019 does not work as expected as outlined in the documentation in certain cases for me.
Under Visual Studio 2019, when I run Math.Round(1.275, 2) I get 1.27. Based on the default rounding it should round to the nearest even digit 1.28. If I run Math.Round(0.275, 2) I get 0.28.
The documentation does also give you the reason for this unexpected behavior:
When rounding midpoint values, the rounding algorithm performs an equality test. Because of problems of binary representation and precision in the floating-point format, the value returned by the method can be unexpected. For more information, see Rounding and precision.
Floating point numbers are stored according to IEEE 754 which is not always a precise representation of the actual value you want to store. You will find lots of ressources where you can learn about how floating point numbers are represented in binary and how exactly IEEE 754 works.
Two things here.
First, this is likely a result of the underlying representation of floats. The documentation itself warns:
When rounding midpoint values, the rounding algorithm performs an equality test. Because of problems of binary representation and precision in the floating-point format, the value returned by the method can be unexpected.
Second, The Math.Round function also takes a rounding strategy argument as the third parameter. Based on the default rounding strategy, this behavior actually seems inline with what the documentation specified. Check out this example they have:
// 3.4 = Math.Round( 3.45, 1)
// -3.4 = Math.Round(-3.45, 1)
// 3.4 = Math.Round(3.45, 1, MidpointRounding.ToEven)
// 3.5 = Math.Round(3.45, 1, MidpointRounding.AwayFromZero)
// 3.4 = Math.Round(3.47, 1, MidpointRounding.ToZero)
// -3.4 = Math.Round(-3.45, 1, MidpointRounding.ToEven)
// -3.5 = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero)
// -3.4 = Math.Round(-3.47, 1, MidpointRounding.ToZero)
It seems to me that the default tries to round to the the closest integer. For example, 2.75 is closer to 3 than 2 so it gets rounded to 2.8. The opposite applies to 1.275. Maybe I'm mistaken, but either way, check out the MidpointRounding argument — that should probably solve your problem.

Kotlin BigDecimal multiplication wrong results

I need to use BigDecimal for some computation but am a bit surprised by the behaviour:
val thousand = BigDecimal(1000)
val fee = BigDecimal(0.005)
println(thousand * fee)
You'd expect the console to contain 5 but the result is 5.000000000000000104083408558608425664715468883514404296875000
I know that I can limit the precision and do some rounding with setScale but the real question is Why is this needed in the first place. This result is obviously wrong.
What am I missing?
The issue is likely to be with the construction of the fee BigDecimal. This is taking a double value and converting it to a BigDecimal. Unfortunately, some fairly simple decimal fractions are impossible to precisely represent as doubles or floats, and this constructor for BigDecimal will take that imprecise double as its value.
From the documentation:
The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.
The way around this is to use the String constructor, which gets round the issue of having to convert "via" a double.

kotlin rounding off in BigDecimal

import java.math.BigDecimal
BigDecimal(0.235).setScale(2, BigDecimal.ROUND_HALF_UP) // 0.23
BigDecimal("0.235").setScale(2, BigDecimal.ROUND_HALF_UP) // 0.24
In kotlin, when input 0.235 is given as double then the output is 0.23.
when input 0.235 is given as string then the output is 0.24
Here is the definition of ROUND_HALF_UP given in the documentation:
Rounding mode where values are rounded towards the nearest
neighbor. Ties are broken by rounding up.
From the BigDecimal docs:
The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a
BigDecimal which is exactly equal to 0.1 (an unscaled value of 1,
with a scale of 1), but it is actually equal to
0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that
matter, as a binary fraction of any finite length). Thus, the value
that is being passed in to the constructor is not exactly equal to
0.1, appearances notwithstanding.
The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which
is exactly equal to 0.1, as one would expect. Therefore, it is
generally recommended that the String
constructor
be used in preference to this one.
The issue here is that in the first case you are calling the BigDecimal constructor using a floating point (read: not exact) literal. Consider the following script (in Java):
BigDecimal blah = new BigDecimal(0.235d);
System.out.println(blah);
This prints 0.23499999999999998667732370449812151491641998291015625 in my demo tool. That is, you are not actually passing in literal 0.235, but rather a floating point approximation to it. It so happens, in this case, that the actual literal value is slightly less than 0.235, leading the round half up to result in 0.23 rather than 0.24.

Is MidpointRounding.AwayFromZero working right in .NET Core 3.1?

It's my understanding from the docs that MidpointRounding.ToEven is the default behavior of Math.Round(). I am using MidpointRounding.AwayFromZero to override that and it does not appear to work. I'm either confused about how MidpointRounding.AwayFromZero works or it isn't working right.
For example, I have a double: 0.285. In my simple mind, when rounding that to two decimal places, that should round to 0.29. However, .NET Core 3.1 Math.Round is rounding it to 0.28, which is exactly the same behavior as the default for Math.Round and also the MidpointRounding.ToEven. Because, 0.29 is further away from zero than 0.28, then one would think that MidpointRounding.AwayFromZero would return 0.29, right? Why name it AwayFromZero and then return a number that is closer to zero. That doesn't make sense.
Math.Round(0.285, 2, MidpointRounding.AwayFromZero) // .NET says this is 0.28
By default, the Math. Round method uses Banker's Rounding not normal rounding.
In banker's rounding a number with a final digit of 5 is rounded to the nearest even number rather than to the next larger number as you might expect. The idea is that statistically half of a sample of numbers are rounded up and half are rounded down.
The reasons with your digital number in normal rounding are described in the office doc.
Math.Round(0.285, 2, MidpointRounding.AwayFromZero) // .NET says this is 0.28
Notes to Callers
Because of the loss of precision that can result from
representing decimal values as floating-point numbers or performing
arithmetic operations on floating-point values, in some cases the
Round(Double, Int32, MidpointRounding) method may not appear to round
midpoint values as specified by the mode parameter. This is
illustrated in the following example, where 2.135 is rounded to 2.13
instead of 2.14. This occurs because internally the method multiplies
value by 10digits, and the multiplication operation in this case
suffers from a loss of precision.
This is indeed due to the fragile precision of floating-point numbers. 0.5 can be stored perfectly in IEEE floating point, but 0.45, 0.445 etc. cannot. For example, the actual value that is stored when you specify 2.44445 is 11009049289107177/4503599627370496 which is 2.44449999999999989519494647... It should now be obvious why the number is rounded the way it is.#cdhowie
If you need to store fractional numbers precisely, consider using the decimal type instead.
Solution
using public static decimal Round(decimal d, int decimals, MidpointRounding mode);
Math.Round((decimal)0.285, 2, MidpointRounding.AwayFromZero); // .NET is 0.29
More details about Math.Round you can see answer from #Sergey Berezovskiy.
.NET Math.Round(,,MidpointRounding.AwayFromZero) not working correctly

Kotlin double greater than float wrong expression resolving

I ran into an issue which I was wondering if the expected behavior is correct or whether I found a bug.
Given this:
0.08 > 0.08 == false
0.08 > 0.08F == true
0.08 > 0.08F.toDouble() == true
0.08.toFloat() > 0.08F == false
Why does the third expression is not false?
Any ideas?
It is not a bug, it's based on rounding errors.
Executing the following code:
val d = 0.08
val f = 0.08F
val fd = f.toDouble()
print("%.20f".format(d) + "\n")
print("%.20f".format(f) + "\n")
print("%.20f".format(fd))
gives you the following output:
0.08000000000000000000
0.07999999821186066000
0.07999999821186066000
So as you can see, 0.08 double value is (till the 20th decimal place) exact to 0.08 while the float is (due to lower precision) not able to be represented as exact so it contains a rounded value, which is slightly lower than 0.08
Converting your approximate (a little lower) 0.08 float to a double doesn't increase your precision, you still have your rounding error of the float, which results in being the converted double to be a little bit lower.
// Edit: If you are interested in how exactly floating point numbers work, I would recommend you to have a look at the wikipedia article on floating point arithmetic and at this question: Is floating point math broken?
This is a supplement to the existing answer. Java BigDecimal has two useful properties for analyzing floating point behavior. Conversion from float or double to BigDecimal is exact, and so is its default string conversion. The simplest use of these properties is to print the exact value of a floating point expression. BigDecimal arithmetic can also be used, for example, to find the half way points between a double and its neighbors when studying rounding.
This program:
import java.math.BigDecimal;
public strictfp class Test {
public static void main(String[] args) {
System.out.println(new BigDecimal(0.08));
System.out.println(new BigDecimal(0.08f));
}
}
outputs:
0.08000000000000000166533453693773481063544750213623046875
0.07999999821186065673828125
which confirms that 0.08 is represented to the expected precision in each format, and the double representation is strictly greater than the float.