I want to know if there is any advantage of using BigDecimal RoundingMode method over DoubleRounder.round().
Also I observe the differences in the result when I try to use the same precision in two methods. For Example :
Using DoubleRounder.round()
System.out.println(""+DoubleRounder.round(0.0897435897435897, 5)); //gives 0.08974
Using BigDecimal RoundingMode
BigDecimal b= new BigDecimal(0.0897435897435897);
System.out.println(""+b.round(new MathContext(5,RoundingMode.CEILING)); //gives 0.089744
I see that there are 6 digits in decimal places while using BigDecimal instead of 5. Can anyone explain me why this is happening. And in case I want to have exact 5 digit after decimal using BigDecimal, how do I need to modify this part of code?
MathContext accept precision, it means starts from the leftmost nonzero digit.
you can use bigDecimal.setScale(5, RoundingMode.CEILING).
scale is the number of digits to the right of the decimal point.
e.g.
System.out.println(b.setScale(5, RoundingMode.CEILING)); // gives you 0.08975
It will give you 5 digit
System.out.println(""+b.round(new MathContext(4,RoundingMode.CEILING))); //gives 0.08975
Related
My question is basically the following:
When I use a value with BigDecimal, how do I append zeros in front of a random number?
Say I want to have a number <10 following an entirely random pattern. Now i want to add zeros in front of the number, so the actual amount adds up to 10 numbers.
Here's an example:
BigDecimal num = new BigDecimal(2353);
Now I want to have that ouput:
0000002353
Is there a function that appends numbers to a BigDecimal type?
I couldn't find any.
I tried using a while loop that checks whether the number is less than ten. But I don't understand the Big Decimal well enough to actually compare integral values to the BigDecimal types.
Thanks for any help in advance!
If you use a BigInteger instead (or any integer type, such as int or long) you can format the value with
String.format("%010d", BigInteger.valueOf(2353))
The leading 0 in the format strings means pad with 0, the following 10 is the desired length...
BigDecimal is meant to be used for storing large floating point numbers. Since in a floating-point number there isn't any difference between 0000002353 and 2353, there is no reasonable way to append leading 0's to a BigDecimal just as there is no reasonable way to append leading 0's to a normal float. According to the behavior you're looking for, I would suggest using a String to store your number, and then convert to and from BigDecimal when you want to perform any operations.
To compare an integral type to a BigDecimal, first convert the variable to a BigDecimal and then call BigDecimal's compareTo method. More info is in this question.
Since you're interested in formatting the number, you might want to look at DecimalFormat class, which allows to format floating point and integer numbers according to the specified pattern.
BigDecimal num = new BigDecimal(2353);
DecimalFormat f1 = new DecimalFormat("0000000000");
DecimalFormat f2 = new DecimalFormat("0,000,000,000");
System.out.println(f1.format(num));
System.out.println(f2.format(num));
Output:
0000002353
0,000,002,353
If the maximum number of digits is 10 and only whole numbers are allowed you don't need anything more than to use long with standard formatting:
long myNumber = 123456;
System.out.printf("%010d%n", myNumber);
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
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
BigDecimal numerator = new BigDecimal(numerator);
BigDecimal denominator = new BigDecimal(denominator);
double result = numerator.divide(denominator).doubleValue();
Division on certain conditions results in a zero at the end (e.g. 0.0060).
Dividing equal numbers results in a zero at the end (e.g 1.0).
I would prefer to trim the trailing zero in both cases. How should I do this?
How about keeping the result as a BigDecimal and then you can set the scale on it to only represent the significant figures that you want.
An easy way to do this, for some numbers, is to use BigDecimal#stripTrailingZeros(). However, if the number is an integer with trailing zeros you'll get an engineering representation e.g. 600.0 will give you 6E+2. If this isn't what you want, you'll have to detect this condition and manually use BigDecimal#setScale() to set the scale appropriately.
If you need to keep to a restricted maximum number of decimal digits you'll need to use alternative formatting/rounding mechanisms before applying this technique.
It's also a good idea to only do this on values that you're going to display, not on the internal values of your model. Treat it as a view/presentation layer modification.
If you must convert to a double, then it's only the formatted representation you can alter. In this case, if you've got a variable number of decimal places that you want to format to, I'd just drop it into a string/character array, scan backwards for the first non-zero character and truncate it there. Not the most performant means, but simple and reliable.
You could even use a regex for this purpose.
you can do this by using DecimalFormat. something like:
DecimalFormat df = new DecimalFormat(".0");
double formatResult = df.format(result);
will create something of 1.0 if the result is 1.278494890. there are many possible patterns that could be used here
Ok, so you've got this code:
BigDecimal numerator = new BigDecimal(numerator);
BigDecimal denominator = new BigDecimal(denominator);
double result = numerator.divide(denominator).doubleValue();
and you want more control over your output. Since result is a double, which is a primitive, you won't have much control.
From my understanding, you don't want to do any rounding to n decimal places, you want original precision paired with desired formatting.
You have few options.
BigDecimal numerator = new BigDecimal(numerator);
BigDecimal denominator = new BigDecimal(denominator);
BigDecimal div = numerator.divide(denominator);
If you stay with BigDecimal, your output will be better. If you put 10 as the numerator and denominator in above code, System.out.println(div) will yield 1.
Generally, be careful of using above code because some combinations of numerator and denominator will throw
java.lang.ArithmeticException: "Non-terminating decimal expansion; no exact representable decimal result."
If you want to avoid such situations, and not worry about precision beyond double's internal representation, use double directly.
System.out.println(2312 / 2.543); //909.1624066063704
System.out.println(1.0 / 1.0); //1.0
System.out.println(1 / 1); //1
When using double numbers, you might get a 0 at the end, such as 0.0060 in your case. If you want to be sure what you're getting, you'll have to convert your result to a String using
String dec = String.valueOf(10.0/10.0); //1.0
and then using
String newDec = dec.endsWith("0") ? dec.substring(0, dec.length() - 1) : dec;
to eradicate that last 0. Of course, if your string ends with .0, you have a choice based on your preferences whether you want to leave that leading . or not.
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 ...)