DecimalFormat is not working properly - java

I am trying to apply formatting (, after 3 digits and rounding after 4 digits), using below code -
double a = 1231254125412512.231515235346;
NumberFormat formatter = new DecimalFormat("#,###");
formatter.setRoundingMode(RoundingMode.HALF_UP);
formatter.setMinimumFractionDigits(4);
formatter.setMaximumFractionDigits(4);
System.out.println("Number : " + formatter.format(a));
Above code is working properly for the number -54125412512.231515235346 (result was -54,125,412,512.2315).
But it is not working for the number -1231254125412512.231515235346 (result -1,231,254,125,412,512.2000).

Double has a precision of 53 bit which is about 16 digits.

Problem is you use double variable, and and hit max precision Double.MIN_VALUE.
SOURCE:
double: 64 bits (8 bytes) where 52 bits are used for the mantissa (15 to 17 decimal digits, about 16 on average). 11 bits are used for the exponent and 1 bit is the sign bit.
To avoid this problem use BigDecimal instead:
BigDecimal a = new BigDecimal("-54125412512.231515235346");
BigDecimal b = new BigDecimal("-1231254125412512.231515235346");
NumberFormat formatter = new DecimalFormat("#,###");
formatter.setRoundingMode(RoundingMode.HALF_UP);
formatter.setMinimumFractionDigits(4);
formatter.setMaximumFractionDigits(4);
System.out.println("Number : " + formatter.format(a));
System.out.println("Number : " + formatter.format(b));
OUTPUT:
Number : -54.125.412.512,2315
Number : -1.231.254.125.412.512,2315

Related

BigDecimal Incorrect Value

Does anyone know why BigDecimals in the case below outputs the incorrect result?
double a = 2400;
double b = 3600;
double c = 8;
MathContext mc = new MathContext(14);
BigDecimal aa = new BigDecimal(2400);
BigDecimal bb = new BigDecimal(3600);
BigDecimal cc = new BigDecimal(8);
System.out.println("Doubles: " + (a/b*c));
System.out.println("Big Ds: " + (aa.divide(bb, mc).multiply(cc)));
Output:
Doubles: 5.333333333333333
Big Ds: 5.33333333333336
Thoughts and ideas are welcome.
It is exactly as calculated manually.
2/3: 0.66666666666667
*8: 5.33333333333336
2/3: 0.66666666666667
8 x
----------------
56
48
... +
----------------
*8: 5.33333333333336
This is maybe counter-intuitive to many people's sense of numbers.
But the last 7 has an error of 0.1/3 too much; times 8 becomes 3 rounded up as error, so the last digit becomes 3 + error 3 == 6.
To minimize the rounding error on division:
aa.multiply(cc).divide(bb, mc)
The double calculation uses a precision of around 16 digits, just a bit more that 14.

Java - double constant number of decimal places [duplicate]

This question already has answers here:
fixed point arithmetics in java with fast performance
(4 answers)
Closed 7 years ago.
I have two double variables:
double a = 1.109
double b = 5.0E-5;
But b is changable and I want to achieve fixed numbers of decimal places depending of b number, for example above I want achieve this result:
Result = 1.10900
But not only print, I need to send it to other method and my double must have fixed numbers of decimal places like in example.
It sounds like you want arbitrary precision on the actual value (as opposed to just output). double doesn't give you that. BigDecimal does though. Its BigDecimal(String) constructor sets the value and the scale (number of places to the right of the decimal) from a string, so:
BigDecimal d = new BigDecimal("1.10900");
BigDecimal then gives you various math operations to stay within that scale, with various rounding options.
If at some point you need to get the double value of the BigDecimal, you can use its doubleValue method. But note that at that point, again, you don't have a fixed number of places to the right of the decimal anymore.
Here's an example contrasting BigDecimal and double (Live Copy):
import java.math.*;
class Example
{
public static void main (String[] args) throws java.lang.Exception
{
BigDecimal bd = new BigDecimal("1.10900");
bd = bd.divide(new BigDecimal("27"), BigDecimal.ROUND_HALF_DOWN);
System.out.println("1.109 / 27 using BigDecimal to five places: " + bd);
double d = 1.109;
d = d / 27.0;
System.out.println("1.109 / 27 using double: " + d);
}
}
Output:
1.109 / 27 using BigDecimal to five places: 0.04107
1.109 / 27 using double: 0.041074074074074075
Try using a number formatter:
NumberFormat formatter = new DecimalFormat("#0.00000");
double a = 1.109;
double b = 5.0E-5;
System.out.println(a);
System.out.println(b);
Output:
1.10900
0.00005
A simple solution is to round the result as needed. This is not only faster than using BigDecimal it can be less error prone as Java doesn't have language support for BigDecimal making it harder to write/read and validate. A simple method for rounding half up for 5 decimal spaces is
public static double round5(double d) {
final double factor = 1e5;
return d > Long.MAX_VALUE / factor || d < -Long.MAX_VALUE / factor ? d :
(long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor;
}
Note: when you print the double you will still need to specify the number of decimal places you need e.g.
System.out.printf("%.5f", value);
Use java printf-like routine (note it produces platform dependent decimal separators):
String.format("%.5f", a)

Issue with rounding aftrer division of BigDecimal values. Java

I've got a problem with the result of division of BigDecimal instances.
My program:
public class DecimalPrecision
public static void main(String[] args) {
MathContext mathContext = new MathContext(25, RoundingMode.HALF_UP);
System.out.println(new BigDecimal("0.1111111111111111111111111").divide(new BigDecimal("3"), mathContext).toString());
}
The result is the following:
0.03703703703703703703703703
But the correct result is:
0.03703703703703703703703704
Basically my program rounded the last digit, which is 7, to 3, not to 4. Why is it so? In mathContext I specified rounding mode HALF_UP. Probably I've got a gap in knowlegde on the issue but I was sure that HALF_UP rounds to a bigger value.
This problem can be sorted out in the next way:
value1 = value1.divide(value2, 10000, RoundingMode.HALF_UP);
return new BigDecimal(value1.toString()).round(mathContext);
But in my taste there's something wrong with parsing the result to a String and then create new properly rounded BigDecimal. I'm asking for your help, how can I resolve this problem without resorting to any strings?
Thanks in advance.
You are starting with a 25 digit number and performing an operation with a precision of 25 digits so you can expect that the last digit might be wrong. The solution is to use more digits than you need and round the result.
BigDecimal oneNinth25 = BigDecimal.ONE.divide(BigDecimal.valueOf(9), 25, BigDecimal.ROUND_HALF_UP);
BigDecimal oneNinth26 = BigDecimal.ONE.divide(BigDecimal.valueOf(9), 26, BigDecimal.ROUND_HALF_UP);
MathContext mathContext = new MathContext(25, RoundingMode.HALF_UP);
System.out.println("1/9 to 25 digits / 3 = " +
oneNinth25.divide(BigDecimal.valueOf(3), mathContext));
System.out.println("1/9 to 26 digits / 3 = " +
oneNinth26.divide(BigDecimal.valueOf(3), mathContext));
prints
1/9 to 25 digits / 3 = 0.03703703703703703703703703
1/9 to 26 digits / 3 = 0.03703703703703703703703704

Extracting the integer and fractional part from Bigdecimal in Java

I want to extract the integer part and decimal part from the bigdecimal in java.
I am using the following code for that.
BigDecimal bd = BigDecimal.valueOf(-1.30)
String textBD = bd.toPlainString();
System.out.println("length = "+textBD.length());
int radixLoc = textBD.indexOf('.');
System.out.println("Fraction "+textBD.substring(0,radixLoc)+"Cents: " + textBD.substring(radixLoc + 1, textBD.length()));
I am getting the output as
-1 and 3
But I want the trailing zero also from -1.30
Output should be -1 and 30
If you like to not get involved with Strings (which I think it's not good practice - except the part of creating de BigDecimal) you could do it just with Math:
// [1] Creating and rounding (just like GriffeyDog suggested) so you can sure scale are 2
BigDecimal bd = new BigDecimal("-1.30").setScale(2, RoundingMode.HALF_UP);
// [2] Fraction part (0.30)
BigDecimal fraction = bd.remainder(BigDecimal.ONE);
// [3] Fraction as integer - move the decimal.
BigDecimal fraction2 = fraction.movePointRight(bd.scale());
// [4] And the Integer part can result of:
BigDecimal natural = bd.subtract(fraction);
// [5] Since the fraction part of 'natural' is just Zeros, you can setScale(0) without worry about rounding
natural = natural.setScale(0);
I know, my english is terrible. Feel free to correct if you could understand what I tried to say. Thanks.
The floating point representation of -1.30 is not exact. Here is a slight modification of your code:
BigDecimal bd = new BigDecimal("-1.30").setScale(2, RoundingMode.HALF_UP);
String textBD = bd.toPlainString();
System.out.println("text version, length = <" + textBD + ">, " + textBD.length());
int radixLoc = textBD.indexOf('.');
System.out.println("Fraction " + textBD.substring(0, radixLoc)
+ ". Cents: " + textBD.substring(radixLoc + 1, textBD.length()));
I have put a RoundingMode on the setScale to round fractional pennies like 1.295 "half up" to 1.30.
The results are:
text version, length = <-1.30>, 5
Fraction -1. Cents: 30
Initialize with a String to avoid problems with floating point accuracy. Then use setScale to set your desired number of decimal places:
BigDecimal bd = new BigDecimal("-1.30").setScale(2);
String textBD = bd.toPlainString();
System.out.println("length = "+textBD.length());
int radixLoc = textBD.indexOf('.');
System.out.println("Fraction "+textBD.substring(0,radixLoc)+"Cents: " + textBD.substring(radixLoc + 1, textBD.length()));

NumberFormat Parse Issue

I am quite confused about this peculiar 'error' I am getting when parsing a String to a Double.
I've already set up the NumberFormat properties and symbols.
When passing a String with 15 digits and 2 decimals (ex. str = "333333333333333,33")
and parsing it with Number num = NumberFormat.parse(str) the result is omitting a digit.
The actual value of num is 3.333333333333333E14.
It seems to be working with Strings with all 1's, 2's and 4's though...
Anyone can enlighten me?
Cheers
Enrico
The short answer; due to round error
(double) 111111111111111.11 != (double) 111111111111111.1
but
(double) 333333333333333.33 == (double) 333333333333333.3
If you want more precision, use setParseBigDecimal and parse will return a BigDecimal.
Why does this happen? This is because you are at the limit of the precision of double. The 17 ones is fine as it can just be represented. The 2's is just double this and as double stores powers of two, every power of two of all 17 ones, so 17 fours and 17 eights is fine.
However, 17 threes takes one more bit than double has to represent the value and this last bit is truncated. Similarly 17 fives, sixes and nines also have rounding errors.
double[] ds = {
111111111111111.11,
222222222222222.22,
333333333333333.33,
444444444444444.44,
555555555555555.55,
666666666666666.66,
777777777777777.77,
888888888888888.88,
999999999999999.99};
for (double d : ds) {
System.out.println(d + " - " + new BigDecimal(d));
}
prints the following. The double is rounded slightly before printing and the BigDecimal shows you the exact values the double represents.
1.1111111111111111E14 - 111111111111111.109375
2.2222222222222222E14 - 222222222222222.21875
3.333333333333333E14 - 333333333333333.3125
4.4444444444444444E14 - 444444444444444.4375
5.5555555555555556E14 - 555555555555555.5625
6.666666666666666E14 - 666666666666666.625
7.777777777777778E14 - 777777777777777.75
8.888888888888889E14 - 888888888888888.875
1.0E15 - 1000000000000000
The DecimalFormat.parse method will in this case return a Double, which has limited precision.
You can't expect it to always be able to return a Number that represents the input exactly.
You can use BigDecimal.setParseBigDecimal to allow the number format to return a BigDecimal from the parse method. This Number is capable of representing your values with arbitrary precision. (Thanks #Peter Lawrey for pointing that out!)

Categories