Convert Java Number to BigDecimal : best way - java

I am looking for the best way to convert a Number to a BigDecimal.
Is this good enough?
Number number;
BigDecimal big = new BigDecimal(number.toString());
Can we lose precision with the toString() method ?

This is fine, remember that using the constructor of BigDecimal to declare a value can be dangerous when it's not of type String. Consider the below...
BigDecimal valDouble = new BigDecimal(0.35);
System.out.println(valDouble);
This will not print 0.35, it will infact be...
0.34999999999999997779553950749686919152736663818359375
I'd say your solution is probably the safest because of that.

Can we lose precision with toString() method ?
Kind of ... Both Float.toString() and Double.toString() only output the number of digits after the decimal separator, which is required for the output uniquely to correspond to a float or double value.
To use the 0.35 example in david99world's answer, consider the following code:
BigDecimal bd1 = new BigDecimal(0.35);
Number n = 0.35;
BigDecimal bd2 = new BigDecimal(n.toString());
System.out.println(bd1);
System.out.println(bd2);
An intuitive expectation may be that the two BigDecimal instances are identical, but the output shows that they are not:
0.34999999999999997779553950749686919152736663818359375
0.35
The first line is the exact value of the double, since 0.35 cannot be represented exactly. The second line is 0.35, since no more fractional digits are required to represent the distinct value. E.g. the statement 0.34999999999999997779553950749686919152736663818359375 == 0.35 will evaluate to true.
This is actually not a loss of precision when creating the BigDecimal, the uncertainty is already there in your "source" value. The problem is rather that the discrete values possible using e.g. a float or double value as source not necessarily will be represented by the exact equivalent in the BigDecimal instance.

I think this is the best way to convert Number to BigDecimal:
public static BigDecimal convertToBigDecimal(Number number) {
if (number instanceof Integer
|| number instanceof Long
|| number instanceof Short
|| number instanceof Byte) {
return BigDecimal.valueOf(number.longValue());
}
return BigDecimal.valueOf(number.doubleValue());
}

For Number, your solution is probably the best.
For doubles, probably the best way is
BigDecimal.valueOf(myDouble);
It works also for longs and it's optimized for frequently used longs value.
Source

Related

BigDecimal Java. How to append zeros in front

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);

Why is BigDecimal returning an approximation of my large double in Java?

I'd like to round my large double so the first thing I decided to do, was to convert it into a BigDecimal in the following way.
BigDecimal amount = BigDecimal
.valueOf(getAmount())
.setScale(2, RoundingMode.HALF_UP);
System.out.println(amount);
In my example, getAmount() returns 123456789123123424113.31.
Therefore, I expect the exact same value to be printed out by my snippet.
Instead, I get the following value:
123456789123123430000.00
Can someone explain why BigDecimal is returning an approximation of my double?
In my example, getAmount() returns 123456789123123424113.31.
No, it does not. That is not a value that a double can represent exactly.
You can easily verify that with this code:
double d = 123456789123123424113.31d;
System.out.println(d);
Which outputs
1.2345678912312343E20
This value has the minimum amount of digits to uniquely distinguish it from any other double value. Meaning that there aren't any more relevant digits in that double. You've already lost the precision before converting the value to BigDecimal.
While an integer data type such as long and int can exactly represent every (integer) value within its range, the same can't be said about floating point numbers: they have an immense range of values that they can represent, but at the cost of not being able to represent every possible value within the range. Effectively there's a limited number of digits that a floating point number can represent (about 16 decimal digits for double and about 7 decimal digits for float). Everything else will be cut off.
If you need arbitrary precision then something like BigDecimal can help: it will allocate as much memory as necessary to hold all digits (or round according to your specification, if required), making it much more complex but also more powerful.
BigDecimal bd = new BigDecimal("123456789123123424113.31");
System.out.println(bd);
will print
123456789123123424113.31
Make sure not to initialize the BigDecimal from a double value, as you'll only get the cut-off value even then.

BigDecimal - to use new or valueOf

I came across two ways of getting BigDecimal object out of a double d.
new BigDecimal(d)
BigDecimal.valueOf(d)
Which would be a better approach? Would valueOf create a new object?
In general (not just BigDecimal), what is recommended - new or valueOf?
Those are two separate questions: "What should I use for BigDecimal?" and "What do I do in general?"
For BigDecimal: this is a bit tricky, because they don't do the same thing. BigDecimal.valueOf(double) will use the canonical String representation of the double value passed in to instantiate the BigDecimal object. In other words: The value of the BigDecimal object will be what you see when you do System.out.println(d).
If you use new BigDecimal(d) however, then the BigDecimal will try to represent the double value as accurately as possible. This will usually result in a lot more digits being stored than you want. Strictly speaking, it's more correct than valueOf(), but it's a lot less intuitive.
There's a nice explanation of this in the JavaDoc:
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.
In general, if the result is the same (i.e. not in the case of BigDecimal, but in most other cases), then valueOf() should be preferred: it can do caching of common values (as seen on Integer.valueOf()) and it can even change the caching behaviour without the caller having to be changed. new will always instantiate a new value, even if not necessary (best example: new Boolean(true) vs. Boolean.valueOf(true)).
If you are using your BigDecimal objects to store currency values, then I strongly recommend that you do NOT involve any double values anywhere in their calculations.
As stated in another answer, there are known accuracy issues with double values and these will come back to haunt you big time.
Once you get past that, the answer to your question is simple. Always use the constructor method with the String value as the argument to the constructor, as there is no valueOf method for String.
If you want proof, try the following:
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal("0.01");
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);
You'll get the following output:
bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01
See also this related question
Basically valueOf(double val) just does this:
return new BigDecimal(Double.toString(val));
Therefore -> yep, a new object will be created :).
In general I think it depends upon your coding style. I would not mixure valueOf and "new", if both are the same outcome.
Foreword
Why are we having these discussions about floating-point type, numbers & arithmetic? Simple. We count in base 10, but the machine count in base 2.
BigDecimal - Need for an exact representation (not approximation)
If you are using BigDecimal, this means that you want an exact representation of 0.1 and other negative powers of ten (usually you would be dealing with money or arithmetic involving decimals).
Double means trouble (where BigDecimal is concerned)
Then, if you are finding yourself having to manipulate double(or float) values using BigDecimal, then you are in double trouble, because it is impossible to represent 0.1 as a double in base 2. The machine "stores" doubles(IEEE-754 standard for floating-point arithmetic) as base 2. Here is a good write-up of what's really happening if you are interested.). Duncan's answer illustrates what i am trying to say, of what to do and not do.
Any programming language that you think can store 0.1 accurately is actually not. It is just an approximation.
System.out.println(0.1d);
//Prints 0.1 or so you think ;-)
//If you are not convinced, try this:
double x = 1.1; double y = 1.0;
if (x-y == 0.1) {// print true } else {// print false}
//or perhaps this:
double amount1 = 2.15;
double amount2 = 1.10;
System.out.println("Difference: " + (amount1 - amount2));
Examples
double smallD = 0.0001;
double smallDNoScientificNotation = 0.001; //>= 10E-3
double normalD = 10.345678;
double bigDNoScientificNotation = 1234567.123456789; //<=10E7
double bigD = 56_789_123_456_789.123456789;
//double
System.out.println(smallD); //1.0E-4, computerized scientific notation, this is how Double toString works
System.out.println(smallDNoScientificNotation); //0.001, OK
System.out.println(normalD); //10.345678, OK
System.out.println(bigDNoScientificNotation); //1234567.123456789, OK
System.out.println(bigD); //5.6789123456789125E13, computerized scientific notation, this is how Double toString works
//new BigDecimal(double): not OK, don't use! Attempting to representing the base-2 representation as accurately as possible
System.out.println(new BigDecimal(smallD)); //0.000100000000000000004792173602385929598312941379845142364501953125
System.out.println(new BigDecimal(smallDNoScientificNotation)); //0.001000000000000000020816681711721685132943093776702880859375
System.out.println(new BigDecimal(normalD)); //10.34567799999999948568074614740908145904541015625
System.out.println(new BigDecimal(bigDNoScientificNotation)); //1234567.12345678894780576229095458984375
System.out.println(new BigDecimal(bigD)); //56789123456789.125
//BigDecimal.valueOf (Dont use if the range is >= 10E-3, >= 10E7), under the hood it's using Double.toString
System.out.println(BigDecimal.valueOf(smallD)); //0.00010 - notice the extra 0, stemming from 1.0E-4
System.out.println(BigDecimal.valueOf(smallDNoScientificNotation)); //0.001
System.out.println(BigDecimal.valueOf(normalD)); //10.345678
System.out.println(BigDecimal.valueOf(bigDNoScientificNotation)); //1234567.123456789
System.out.println(BigDecimal.valueOf(bigD)); //56789123456789.125 //loss of accuracy
Computerized scientific notation - more here.
BONUS 1 - Pitfalls
Here
BONUS 2 - Effective Java 3rd edition (Joshua Bloch)
Item 60: Avoid float or double if exact answers are required
The float and double types are particularly ill-suited for monetary calculations because it is impossible to represent 0.1 (or any other negative power of ten) as a float or double exactly.
:
There are, however, two disadvantages to using BigDecimal: it's a lot less convenient than using a primitive arithmetic type, and it's a lot slower. The latter disadvantage is irrelevant if you're solving a single short problem, but the former may annoy you.
:
An alternative to using BigDecimal is to use int or long, depending on the amounts involved, and to keep track of the decimal point yourself. In this example, the obvious approach is to do all computation in cents.
Extra reading for the mathematically inclined ;-)
What Every Computer Scientist Should Know About Floating-Point Arithmetic

How do you trim the BigDecimal division results

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.

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().

Categories

Resources