Extracting the integer and fractional part from Bigdecimal in Java - 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()));

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.

Correct way to format a notation of a number with decimal scale in Java without dividing

I have done a reading about number conversions in Java because I need to format a number inside my Android application.
I'm currently holding a long variable with 4 digits
long variable_1 = 2203;
And this is what I'm looking to print
220.3
What I have done so far
variable_1 = 2203;
BigDecimal bd = new BigDecimal(variable_1);
bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP);
txtView_variable_1.setText(String.valueOf(bd));
Result
2203.0
What am I doing wrong here?
If you change the scale of a BigDecimal, it will return a new BigDecimal with the specified scale but with the same numerical value. It won't change the interpretation of the unscaled number. The underlying scaled number will be rescaled.
You should give the scale at the initialization of the BigDecimal in order for it to interpret correctly your unscaled number :
long variable_1 = 2203;
BigDecimal bd = new BigDecimal(BigInteger.valueOf(variable_1), 1);
System.out.println(String.valueOf(bd));
Which outputs :
220.3
I had the same problem to round at 0.1
I managed it like this:
private BigDecimal roundBigDecimal(BigDecimal valueToRound) {
int valeurEntiere = valueToRound.intValue();
String str = String.valueOf(valeurEntiere);
int rounding = valeurEntiere == 0 ? 1 : str.length() + 1;
return new BigDecimal(String.valueOf(valueToRound), new MathContext(rounding, RoundingMode.HALF_UP));
}
In fact the int rounding depends of the number of digit of the number.

Multiply long or BigInteger by double without loss of precision

I want to multiply a high precision integer (long or BigInteger) by a small double (think about something > 0 and < 1) and get the arithmetically rounded integer (long or BigInteger) of the exact arithmetic value of the operation as result.
Converting the double to integer does not work, because its fractional value gets lost.
Converting the integer to double, then multiply and converting the result back to integer will not work either, because double is not precise enough.
Of course you could argue, that because the double operand is not precise enough in the first place, it might not matter that the result is not precise with the same order of magnitude, but in this case, it does.
Bonus question:
Using BigDecimal works, but seems to be very inefficient. Converting long to double and then multiplying and converting back seems to run 500 times faster (albeit losing precision) than converting to BigDecimal. Is there a more efficient possibility? Is it possible to gain performance when multiplying several different long each with the same double?
You want to use BigDecimal in order to preserve precision.
BigInteger myBI = new BigInteger("99999999999999999");
Double d = 0.123;
BigDecimal bd = new BigDecimal(myBI);
BigDecimal result = bd.multiply(BigDecimal.valueOf(d));
Using BigDecimal indeed works. You still have to be carefull about using the exact value the double represents and rounding arithmetically.
BigInteger myBI = new BigInteger("1000000000000000000000000000000000000000000000000000000");
double d = 0.1;
BigDecimal bd = new BigDecimal(myBI);
BigInteger doubleWithStringValue = bd.multiply(BigDecimal.valueOf(d)).toBigInteger();
BigDecimal bdresult = bd.multiply(new BigDecimal(d));
BigInteger unrounded = bdresult.toBigInteger();
BigInteger correct = bdresult.add(new BigDecimal("0.5")).toBigInteger(); // this way of rounding assumes positive numbers
BigInteger lostprecision = new BigDecimal(myBI.doubleValue() * d).toBigInteger();
System.out.println("DoubleString: " + doubleWithStringValue);
System.out.println("Unrounded: " + unrounded);
System.out.println("Correct: " + correct);
System.out.println("Lost precision: " + lostprecision);
Output:
DoubleString: 100000000000000000000000000000000000000000000000000000
Unrounded: 100000000000000005551115123125782702118158340454101562
Correct: 100000000000000005551115123125782702118158340454101563
Lost precision: 100000000000000020589742799994816764107083808679919616
The best solution I can see is you use the Math.round function. with code like this.
long l; //your long value
double d;//your fraction
long answer;
answer = Math.round((double)(l * d));
This will give you the answer without a lost prevention error.
The other option would be to truncate it.
same declares as above code.
String s;
s = "" + (l*d);
StringTokenizer token = new StringTokenizer(s);
s = token.nextToken();
answer = Long(s);
Double has a precission of 52 bit. How about:
multiplying your double by (1<<52)
convert double to BigInteger (no loss as full precision is on left of decimal point)
multiply with other BigIngeger
partially correct binary exponent of result (BigInteger>>51)
If odd, do your rounding by adding 1 or BigInteger.Sign (depending on your preferences of rounding)
Finally shift result one more bit (BigInteger>>1)
BigInteger myBI = BigInteger("99999999999999999");
Double d = 0.123;
BigInteger bigDouble = (BigInteger)(d * ((ulong)1 << 52));
BigInteger result = (myBI * bigDouble) >> 51;
if (!result.IsEven)
result += result.Sign;
result = result >> 1;

BigDecimal from Double incorrect value?

I am trying to make a BigDecimal from a string. Don't ask me why, I just need it! This is my code:
Double theDouble = new Double(".3");
System.out.println("The Double: " + theDouble.toString());
BigDecimal theBigDecimal = new BigDecimal(theDouble);
System.out.println("The Big: " + theBigDecimal.toString());
This is the output I get?
The Double: 0.3
The Big: 0.299999999999999988897769753748434595763683319091796875
Any ideas?
When you create a double, the value 0.3 cannot be represented exactly. You can create a BigDecimal from a string without the intermediate double, as in
new BigDecimal("0.3")
A floating point number is represented as a binary fraction and an exponent. Therefore there are some number that cannot be represented exactly. There is an analogous problem in base 10 with numbers like 1/3, which is 0.333333333..... Any decimal representation of 1/3 is inexact. This happens to a DIFFERENT set of fractions in binary, and 0.3 is one of the set that is inexact in binary.
Another way is to use MathContext.DECIMAL32 which guarantees 7 digit precision (which is good enough in our case):
Double theDouble = new Double(".3");
System.out.println("The Double: " + theDouble.toString());
BigDecimal theBigDecimal = new BigDecimal(theDouble, MathContext.DECIMAL32); // <-- here
System.out.println("The Big: " + theBigDecimal.toString());
OUTPUT
The Double: 0.3
The Big: 0.3000000
Since new Double(".3") can't be represented exactly, the nearest value is 0x1.3333333333333P-2 or .299999999999999988897769753748434595763683319091796875, what would be need to is this:
Double theDouble = new Double(".3");
System.out.println("The Double: " + theDouble.toString());
BigDecimal theBigDecimal = new
BigDecimal(theDouble).setScale(2, RoundingMode.CEILING); // <-- here
System.out.println("The Big: " + theBigDecimal.toString());
This will print:
The Double: 0.3
The Big: 0.30
You can give a big decimal a specified precision. e.g. append to your example:
Double theDouble = new Double(".3");
theBigDecimal = new BigDecimal(theDouble, new MathContext(2));
System.out.println("The Big: " + theBigDecimal.toString());
This will print out "0.30"

"new BigDecimal(13.3D)" results in imprecise "13.3000000000000007105.."?

How is it that Java's BigDecimal can be this painful?
Double d = 13.3D;
BigDecimal bd1 = new BigDecimal(d);
BigDecimal bd2 = new BigDecimal(String.valueOf(d));
System.out.println("RESULT 1: "+bd1.toString());
System.out.println("RESULT 2: "+bd2.toString());
RESULT 1: 13.300000000000000710542735760100185871124267578125
RESULT 2: 13.3
Is there any situation where Result 1 would be desired? I know that Java 1.5 changed the toString() method but was this the intended consequence?
Also I realise that BigDecimal has doubleValue() etc, but the library that I am working with helpfully uses a toString() and I can't change that :-(
Cheers.
Well, the API does address this apparent inconsistency in the constructor BigDecimal(double val):
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.
When a double must be used as a source for a BigDecimal, note that
this constructor provides an exact
conversion; it does not give the same
result as converting the double to a
String using the
Double.toString(double) method and
then using the BigDecimal(String)
constructor. To get that result, use
the static valueOf(double) method.
Moral of the story: The pain seems self-inflicted, just use new BigDecimal(String val) or BigDecimal.valueOf(double val) instead =)
Your problem has nothing to do with BigDecimal, and everything with Double, which cannot represent 13.3 accurately, since it uses binary fractions internally.
So your error is introduced in the very first line. The first BigDecimal simply preserves it, while String.valueOf() does some fishy rounding that causes the second one to have the desired content, pretty much through luck.
You might want to inform yourself about how floating-point values are implemented (IEEE 754-1985). And suddenly, everything will become crystal-clear.
This isn't the fault of BigDecimal - it's the fault of double. BigDecimal is accurately representing the exact value of d. String.valueOf is only showing the result to a few decimal places.
Fractions represented with binary number types(i.e. double, float) cannot be accurately stored in those types.
Double d = 13.3;
BigDecimal bdNotOk = new BigDecimal(d);
System.out.println("not ok: " + bdNotOk.toString());
BigDecimal bdNotOk2 = new BigDecimal(13.3);
System.out.println("not ok2: " + bdNotOk2.toString());
double x = 13.3;
BigDecimal ok = BigDecimal.valueOf(x);
System.out.println("ok: " + ok.toString());
double y = 13.3;
// pretty lame, constructor's behavior is different from valueOf static method
BigDecimal bdNotOk3 = new BigDecimal(y);
System.out.println("not ok3: " + bdNotOk3.toString());
BigDecimal ok2 = new BigDecimal("13.3");
System.out.println("ok2: " + ok2.toString());
Double e = 0.0;
for(int i = 0; i < 10; ++i) e = e + 0.1; // some fractions cannot be accurately represented with binary
System.out.println("not ok4: " + e.toString()); // should be 1
BigDecimal notOk5 = BigDecimal.valueOf(e);
System.out.println("not ok5: " + notOk5.toString()); // should be 1
/*
* here are some fractions that can be represented exactly in binary:
* 0.5 = 0.1 = 1 / 2
* 0.25 = 0.01 = 1 / 4
* 0.75 = 0.11 = 3 / 4
* 0.125 = 0.001 = 1 / 8
*/
output:
not ok: 13.300000000000000710542735760100185871124267578125
not ok2: 13.300000000000000710542735760100185871124267578125
ok: 13.3
not ok3: 13.300000000000000710542735760100185871124267578125
ok2: 13.3
not ok4: 0.9999999999999999
not ok5: 0.9999999999999999
Just use BigDecimal.valueOf(d) or new BigDecimal(s).

Categories