How do you truncate extra decimals from BigDecimal - java

I'm building a service that uses a currency converter and forwards the BigDecimal amount to another service. Sometimes, the conversion rate makes it so that the converted amount has close to 34 decimal places, which the downstream service does not accept.
Is there a way to simply truncate (not round) the BigDecimal. So, for example, if the converted amount is 1.23456789 I want neither 1.24, nor 1.3, nor 1.20, or anything of that sort. I simply want to get rid of the decimals that appear after 4. So what I want is 1.23.
I saw a lot of questions on SO related to this, but they all rounded the BigDecimal in some way.
Thanks!

RoundingMode.DOWN effectively truncates your decimal values:
Javadoc says:
Rounding mode to round towards zero. Never increments the digit prior
to a discarded fraction (i.e., truncates). Note that this rounding
mode never increases the magnitude of the calculated value.
BigDecimal dec = new BigDecimal(10.2384235254634623524);
System.out.println(dec.setScale(2, RoundingMode.DOWN));
Will give:
10.23

BigDecimal provides RoundingMode, which you need here is RoundingMode.FLOOR,
System.out.println(new BigDecimal("1.234567").setScale(2, RoundingMode.FLOOR)); // 1.23
System.out.println(new BigDecimal("1.236567").setScale(2, RoundingMode.FLOOR)); // 1.23

you could try treating it like a string
System.out.println(new DecimalFormat("#0.##").format(new BigDecimal("1.23456789")));

BigDecimal also provides Rounding Modes. Try this
BigDecimal bd = new BigDecimal(2.2964556655);
System.out.println(bd.setScale(2,BigDecimal.ROUND_DOWN));

Related

Java BigDecimal Rounding while having pointless 0's in the number

Problem
I used BigDecimal.setScale(7, RoundingMode.HALF_UP) to round the number to 7 decimal places, however now if I get a number without any decimal places or with them being fewer then 7 I get useless 0's in the number, for example after rounding 40 that way I'll get 40.0000000.
Question
Is it possible to round numbers a certain number of decimal places using SetScale and get rid of pointless 0's at the same time?
Providing you have already performed your rounding, you can simply use DecimalFormat("0.#").
final double value = 5.1000;
DecimalFormat format = new DecimalFormat("0.#");
System.out.println(format.format(value));
The result here will be 5.1 without the trailing zeroes.
So the other way to work this problem out is using BigDecimals methods such as .stripTrailingZeros().toPlainString()
It is quite lesser code to write, than the other solution, but as said iт the solution could be less comfortable to change level of precision in the future, if you'll need to

Is there a way to remove the even-odd rule of rounding for 5 and round up?

Making a application for ordering tickets. For the total, I added the subtotal, hst, and service fees together, and decimal formatted the sum for the total. If the total is x.85, it rounds down to x.8 but I want x.9
Is there a way to remove the even-odd rule for rounding 5 and round up?
I have tried BigDecimal but it doesn't seem to work for me.
DecimalFormat df = new DecimalFormat("$#,###.00");
grandTotal = subTotal + hst + serviceFees;
System.out.printf("%-40s%11s\n", "TOTAL:", df.format(grandTotal));
I expect the output to round up when there is a 5 to round, but it just rounds based on the even-odd rule.
You need only 1 fraction digit.
You current code is rounding correctly, just too many fraction digits.
The rounding mode you are asking for is called HALF UP, which is a default for DecimalFormat and the only possible rounding mode for printf().
(You can also use format $#,###.# if you don't want to display zeros as fraction.)
Therefore you can use your decimal format just fine or even System.out.printf() but limit number of fraction digits to 1:
DecimalFormat df = new DecimalFormat("$#,###.0"); //set MIN and MAX fraction digits to 1
df.setRoundingMode(RoundingMode.HALF_UP); //default but showing usage if needed
System.out.println(df.format(123454.84d));
System.out.println(df.format(123454.85d));
System.out.printf("$%,.1f", 123454.85d); //HALF UP rounding the only option
prints:
$123,454.8
$123,454.9
$123,454.9
You are using floating point (double), which is just an approximation of real values.
Especially with a list and a total, you will always have trouble to get everything right.
You have no control, whether the total is 2.35 (actually never) or 2.349999998, the latter which would round down.
Use BigDecimal with String constructors: new BigDecimal("1.20"), having a fraction of 2 decimals.
Mind you must use add/multiply i.o. +/*.
Guys i just switched the double data type into a float type and it worked ayyyyy
!!!!!!!

Calculations without loss of precision

I have
BigDecimal a = new BigDecimal(7);
BigDecimal b = new BigDecimal(13);
BigDecimal c = new BigDecimal(26);
System.out.print((a.divide(b)).multiply(c));
this code generates an exception:
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
It means that I need to set RoundingMode.
But I need to get result only without loss of precision.
How can I achieve it?
Seeing as 7/13 goes on to infinity what you're asking for is not possible. Your only possible option is to have a large precision when you divide.
Dividing 7 by 13 will giving you long and non terminating decimal(0.53846153846.....). So you need to set the precision limit to it. Try this:
a.divide(b, x, RoundingMode.HALF_UP).multiply(c)
where x is the precision limit you want to set.
The Javadocs says:
When a MathContext object is supplied with a precision setting of 0
(for example, MathContext.UNLIMITED), arithmetic operations are exact,
as are the arithmetic methods which take no MathContext object. (This
is the only behavior that was supported in releases prior to 5.)
As a corollary of computing the exact result, the rounding mode
setting of a MathContext object with a precision setting of 0 is not
used and thus irrelevant. In the case of divide, the exact quotient
could have an infinitely long decimal expansion; for example, 1
divided by 3.
Decimal places of 7/3 is going to infinity. You must round it AFAIK. You can keep 6 decimal places because first 6 places repeats infinitely
http://m.wolframalpha.com/input/?i=7%2F13
Apache commons library can it
Fractions

How to always display a BigDecimal object in full decimal format instead of scientific notation?

I have a BigDecimal object, myNumber, with unknown length. For example: 12345678.
I always want to divide this number by 1 million, so I do:
myNumber.divide(BigDecimal.valueOf(1000000))
I get 12.345678.
I want to display this as a string "12.345678", without cutting off ANY decimal places.
So I do
myNumber.divide(BigDecimal.valueOf(1000000)).toString()
This works fine with the above example. But if myNumber is something ridiculously small or big, such as:
0.00000001
After dividing 0.00000001 by a million and converting to string, it displays as scientific notation, which is not what I want. I want it to always display in full decimal format (in this case, 0.00000000000001).
Any ideas?
You have to perform the division using the variant of divide() that includes a rounding mode and a scale, and set the scale large enough to include all the fractional digits.
int s = myNumber.scale();
BigDecimal result = myNumber.divide(BigDecimal.valueOf(1000000), s+6, RoundingMode.UNNECESSARY);
Then use toPlainString() to format.
I think that BigDecimal.toPlainString() is the method you need. However, note that the division itself will throw an exception when the decimal representation is infinite, such as with 1/3.
BigDecimal.toString or toPlainString would help.
You can use BigDecimal.toPlainString() to return "a string representation of this BigDecimal without an exponent field".
The scientific notation on the other hand is returned by BigDecimal.toEngineeringString().

Use of java.math.MathContext

Recently I tried understanding the use of java.math.MathContext but failed to understand properly. Is it used for rounding in java.math.BigDecimal. If yes why does not it round the decimal digits but even mantissa part.
From API docs, I came to know that it follows the standard specified in ANSI X3.274-1996 and ANSI X3.274-1996/AM 1-2000 specifications but I did not get them to read online.
Please let me know if you have any idea on this.
For rounding just the fractional part of a BigDecimal, check out the BigDecimal.setScale(int newScale, int roundingMode) method.
E.g. to change a number with three digits after the decimal point to one with two digits, and rounding up:
BigDecimal original = new BigDecimal("1.235");
BigDecimal scaled = original.setScale(2, BigDecimal.ROUND_HALF_UP);
The result of this is a BigDecimal with the value 1.24 (because of the rounding up rule)
#jatan
Thanks for you answer. It makes sense. Can you please explain me MathContext in the context of BigDecimal#round method.
There's nothing special about BigDecimal.round() vs. any other BigDecimal method. In all cases, the MathContext specifies the number of significant digits and the rounding technique. Basically, there are two parts of every MathContext. There's a precision, and there's also a RoundingMode.
The precision again specifies the number of significant digits. So if you specify 123 as a number, and ask for 2 significant digits, you're going to get 120. It might be clearer if you think in terms of scientific notation.
123 would be 1.23e2 in scientific notation. If you only keep 2 significant digits, then you get 1.2e2, or 120. By reducing the number of significant digits, we reduce the precision with which we can specify a number.
The RoundingMode part specifies how we should handle the loss of precision. To reuse the example, if you use 123 as the number, and ask for 2 significant digits, you've reduced your precision. With a RoundingMode of HALF_UP (the default mode), 123 will become 120. With a RoundingMode of CEILING, you'll get 130.
For example:
System.out.println(new BigDecimal("123.4",
new MathContext(4,RoundingMode.HALF_UP)));
System.out.println(new BigDecimal("123.4",
new MathContext(2,RoundingMode.HALF_UP)));
System.out.println(new BigDecimal("123.4",
new MathContext(2,RoundingMode.CEILING)));
System.out.println(new BigDecimal("123.4",
new MathContext(1,RoundingMode.CEILING)));
Outputs:
123.4
1.2E+2
1.3E+2
2E+2
You can see that both the precision and the rounding mode affect the output.
I would add here, a few examples. I haven't found them in previous answers, but I find them useful for those who maybe mislead significant digits with number of decimal places. Let's assume, we have such context:
MathContext MATH_CTX = new MathContext(3, RoundingMode.HALF_UP);
For this code:
BigDecimal d1 = new BigDecimal(1234.4, MATH_CTX);
System.out.println(d1);
it's perfectly clear, that your result is 1.23E+3 as guys said above. First significant digits are 123...
But what in this case:
BigDecimal d2 = new BigDecimal(0.000000454770054, MATH_CTX);
System.out.println(d2);
your number will not be rounded to 3 places after comma - for someone it can be not intuitive and worth to emphasize. Instead it will be rounded to the first 3 significant digits, which in this case are "4 5 4". So above code results in 4.55E-7 and not in 0.000 as someone could expect.
Similar examples:
BigDecimal d3 = new BigDecimal(0.001000045477, MATH_CTX);
System.out.println(d3); // 0.00100
BigDecimal d4 = new BigDecimal(0.200000477, MATH_CTX);
System.out.println(d4); // 0.200
BigDecimal d5 = new BigDecimal(0.000000004, MATH_CTX);
System.out.println(d5); //4.00E-9
I hope this obvious, but relevant example would be helpful...
If I'm understanding you correctly, it sounds like you're expecting the MathContext to control how many digits should be kept after the decimal point. That's not what it's for. It specifies how many digits to keep, total. So if you specify that you want 3 significant digits, that's all you're going to get.
For example, this:
System.out.println(new BigDecimal("1234567890.123456789",
new MathContext(20)));
System.out.println(new BigDecimal("1234567890.123456789",
new MathContext(10)));
System.out.println(new BigDecimal("1234567890.123456789",
new MathContext(5)));
will output:
1234567890.123456789
1234567890
1.2346E+9
It's not for fun. Actually I found some online example, which stated the use of MathContext to round the amounts/numbers stored in BigDecimal.
For example,
If MathContext is configured to have precision = 2 and rounding mode = ROUND_HALF_EVEN
BigDecimal Number = 0.5294, is rounded to 0.53
So I thought it is a newer technique and used it for rounding purpose. However it turned into nightmare because it started rounding even mentissa part of number.
For example,
Number = 1.5294 is rounded to 1.5
Number = 10.5294 is rounded to 10
Number = 101.5294 is rounded to 100
.... and so on
So this is not the behavior I expected for rounding (as precision = 2).
It seems to be having some logic because from patter I can say that it takes first two digits (as precision is 2) of number and then appends 0's till the no. of digits become same as unrounded amount (checkout the example of 101.5294 ...)

Categories

Resources