Difference of Two numbers - BigDecimal - java

I am just trying to learn more about BigDecimal, but below code makes me confuse.
Double x = 1.2;
String y = "1.2";
BigDecimal a = BigDecimal.ZERO;
BigDecimal b = BigDecimal.ZERO;
a = new BigDecimal(x);
b = new BigDecimal(y);
int res = res = b.compareTo(a);
if(res==1){
System.out.println("Die");
}else if(res ==0){
System.out.println("Live");
}else if (res==-1){
System.out.println("God Loves you");
}
Result = Die
I am not ready to "Die", why BigDecimal is hell bent on killing me.

This statement:
Double x = 1.2;
assigns the nearest double-representable value to 1.2 to x. That's just less than 1.2 - the value 1.2 itself can't be represented exactly in binary.
Now when you create a BigDecimal from that value, that "not quite 1.2" value is retained exactly. From the constructor's documentation:
Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value.
... whereas when you use new BigDecimal("1.2") the result is exactly 1.2 - BigDecimal parses the string, and any decimal string representation can be represented exactly by BigDecimal, as that's the whole point of it.
1.2 is slightly bigger than "the nearest double representation of 1.2" hence res is 1.

a = new BigDecimal(x); // a= 1.1999999999999999555910790149937383830547332763671875
b = new BigDecimal(y); // b=1.2
This is due to conversion issue since translates a double into a BigDecimal which is the exact decimal representation.
int res = res = b.compareTo(a); //res= 1

Because 1.2 as double is not what you expect it to be.

Try this:
BigDecimal a = new BigDecimal(String.valueOf(x));
BigDecimal b = new BigDecimal(y);

Related

Subtract and round double values by multiplier

I have two double values
double a = 1.07522;
double b = 1.0752;
and rounding multiplier value
public static final double ROUND_MULTIPLIER = 100000.0;
So there always should be 5 decimal places.
I need to subtract two double values and get result as a - b = 0.00002.
How can I do this with using ROUND_MULTIPLIER ?
I tried using BigDecimal as
BigDecimal.valueOf(a).subtract(BigDecimal.valueOf(b)).round(new MathContext((int)ROUND_MULTIPLIER));
but it not always works, sometimes return 2E-16, it returns weird value when try add to second value as below
BigDecimal.valueOf(a).subtract(BigDecimal.valueOf(b + 0.00002)).round(new MathContext((int)ROUND_MULTIPLIER));
I need to use ROUND_MULTIPLIER.
I don't understand why you must use ROUND_MULTIPLYER and what the exact reason of it's existance is, so I can only guess.
Forcing the code to use ROUND_MULTIPLYER:
public static final double ROUND_MULTIPLIER = 100000.0;
public void foobar()
{
double a = 1.07522;
double b = 1.0752;
BigDecimal opA = BigDecimal.valueOf(a);
BigDecimal opB = BigDecimal.valueOf(b);
BigDecimal result = opA.subtract(opB);
result = result.multiply(BigDecimal.valueOf(ROUND_MULTIPLIER));
int cutResult = result.intValue();
result = BigDecimal.valueOf(cutResult / ROUND_MULTIPLIER);
System.out.println(result);
}
The output of this is
0.000020
Is that what you want? The code is definitly object to optimization, but I let that up to you to do ;-)
Use bigDEcimal and set the scale giving the number of decimals you need
final BigDecimal a = new BigDecimal(1.07522);
final BigDecimal b = new BigDecimal(1.0752);
final BigDecimal result = a.subtract(b);
int newScale = 10; //10 decimals
System.out.println(result.setScale(newScale, BigDecimal.ROUND_UP));
BigDecimal decimal = new BigDecimal(1.07522);
BigDecimal decimal1 = new BigDecimal(1.0752);
System.out.println(decimal.subtract(decimal1).setScale(5, RoundingMode.DOWN));
BigDecimal is the way to go, but why is the ROUND_MULTIPLIER a double value if you cast it to int anyways. Maybe that is where you get your rounding issues from? Give it a spin with an int-multiplier :)
Edit: using setScale() setting a proper rounding mode is usually a better choice, but you insist on using the multiplier, right?

Java Floating Point Precision Issue

I have a question regarding floating point arithmetic in java and its precision. I did do my research on here and via google and come across some solutions but am having difficulties implementing them in my design. So in Java I am making use of the BigDecimal class in getting my calculations accurate. Note that the variables are double and the values can have a precision up to 8 decimal places to the right when doing calculations. The result (precision) to display is known and thats what I'll be storing as the current value. Also, all values come in dynamically (through a method). The argument passed should be the currentValue + the step size.
public void newValue(float value) {
//Clip to valid range, can't go over minimum/max value
value = Math.max(minimumValue, Math.min(maximumValue, value));
// TODO Implement better Floating Point Arithmetic precision
MathContext mcI = new MathContext(0, RoundingMode.HALF_UP);
MathContext mcF = new MathContext(8, RoundingMode.HALF_UP);
BigDecimal valueBD = new BigDecimal(value, mcF);
BigDecimal minimumBD = new BigDecimal(minimumValue, mcF);
BigDecimal stepBD = new BigDecimal(step, mcF);
BigDecimal currentValueBD = new BigDecimal(currentValue, mcF);
BigDecimal totalStepsBD = valueBD.subtract(minimumBD, mcF);
//Ensure value is divisible by stepsize
totalStepsBD = totalStepsBD.divide(stepBD, mcI);
valueBD = stepBD.multiply(totalStepsBD, mcF);
valueBD = valueBD.add(minimumBD, mcF);
// arithmetic without using BigDecimal (old)
//int totalSteps = (int) ((value- minimumValue)/ step);
//value = totalSteps * step + minimumValue;
if(!(valueBD.equals(currentValueBD))) {
valueBD = valueBD.setScale(displayPrecision, RoundingMode.HALF_UP);
currentValue = valueBD.floatValue();
dispatch();
}
}
Now, it works with some values but not all. Especially when I mess with the step size. So if step = 0.1 it was fine. If I made it 0.005, I'd get an AirthmeticException - non terminating decimal expansion on the step where
totalStepsBD = totalStepsBD.divide(stepBD, mcI);
When .005 is set for the step variable, after making a BigDeciaml (stepBD) it comes out to .0049999999... Not sure if that helps but if you have any ideas please let me know. Thank you.
Pass a String step (and a String value) to the BigDecimal constructor. You can't precisely represent 0.005 as a double (or a float).
BigDecimal stepBD = new BigDecimal("0.005"); // <-- works as a `String`.
Edit
Or as noted below, use BigDecimal.valueOf(double)
BigDecimal stepBD = BigDecimal.valueOf(0.005);

BigDecimal doesn't calculate divide

I divided 124/13. but the app forced close.
this is my code:
Float x =Float.valueOf(a.getText().toString());
Float y =Float.valueOf(b.getText().toString());
BigDecimal xx= new BigDecimal (x);
BigDecimal yy= new BigDecimal (y);
BigDecimal rx= xx.divide(yy);
res.setText("=" + rx);
It is possible that the app is crashing, because BigDecimal.toString() does something unexpected. Also a, or b or rx may be null.
In any way, I would consider using BigDecimal with the String constructor, such that no rounding errors occur:
String x = a.getText().toString();
String y = b.getText().toString();
BigDecimal xx = new BigDecimal(x);
BigDecimal yy = new BigDecimal(y);
BigDecimal rx = xx.divide(yy);
res.setText("=" + rx.toPlainString());
Also write new BigDecimal(x) instead of new BigDecimal (x). Note the omitted space, that may be the very reason why your app crashes, it is not allowed in Java.
You have encountered:
java.lang.ArithmeticException: Non-terminating decimal expansion;
no exact representable decimal result.
Consider this example, which will yeld this exact exception:
BigDecimal x = new BigDecimal(1);
BigDecimal y = new BigDecimal(3);
BigDecimal result = x.divide(y);
That's because there's no exact representation of 0.3333(3).
What you need to do is to specify rounding and precision:
BigDecimal result = x.divide(y, 10 /*scale*/, RoundingMode.HALF_UP);
System.out.println(result); //will print "0.3333333333"
Also note that you should create the BigDecimal directly from String, as float is not a precise representation. Consider this:
String s = "0.123456789";
System.out.println(Float.parseFloat(s)); //prints 0.12345679
System.out.println(new BigDecimal(s)); //prints 0.123456789
It may be the case that you want an approximate result. Then just go with float or double only:
dobule x = Double.parseDouble(a.getText());
dobule y = Double.parseDouble(b.getText());
res.setText("=" + (x/y));

How to properly truncate a double

How to properly truncate a double in Java, so for example 1.99999999999999999 is always truncated to 1 and not rounded upto 2 as is the case in the sample below.
double d1 = 1.999999999999999999;
double d2 = 1.0;
long i1 = (long)Math.floor(d1);
long i2 = (long)Math.floor(d2);
System.out.println("i1="+i1 + " i2="+i2); //i1 == 2
Running sample here: http://www.browxy.com/SubmittedCode/42603
Solution
Use BigDecimal which has arbitary precision
BigDecimal bd = new BigDecimal("0.9999999999999999999999999999");
bd = bd.setScale(0, BigDecimal.ROUND_DOWN); //truncate
1.99999999999999999 is not rounded up to 2, it is 2.
System.out.println(1.999999999999999999); // 2
System.out.println(1.999999999999999999 == 2); // true
Floating-point numbers aren't infinitely precise. There aren't enough bits in the storage format Java uses (IEEE double) to distinguish between 1.999999999999999999 and 2.
If you need more accuracy than that, try a different number format, such as BigDecimal.
I think your d1 has too much precision to be expressed as a double.
If you print d1 directly to System.out you get 2.0.
So any call to floor or round has no effect.
If you need such a precision you should go for BigDecimal.
Some of the examples are explained here
Documentation
double d1 = 1.999999999999999999;
double d2 = 1.0;
long i1 = (long)Math.round(d1);
long i2 = (long)Math.floor(d2);
System.out.println("i1="+i1 + " i2="+i2);
Then the output will be
i1=2 i2=1

Java BigDecimal without E

I have a BigDecimal variable
BigDecimal x = new BigDecimal("5521.0000000001");
Formula:
x = x.add(new BigDecimal("-1")
.multiply(x.divideToIntegralValue(new BigDecimal("1.0"))));
I want to remove the integer part, to get the value x = ("0.0000000001"), but my new value is 1E-10 and not the 0.0000000001.
To get a String representation of the BigDecimal without the exponent part, you can use
BigDecimal.toPlainString(). In your example:
BigDecimal x = new BigDecimal("5521.0000000001");
x = x.add(new BigDecimal("-1").
multiply(x.divideToIntegralValue(new BigDecimal("1.0"))));
System.out.println(x.toPlainString());
prints
0.0000000001
Try using BigDecimal.toPlainString() to get value as plain string as you require.
Perhaps using BigDecimal isn't really helping you.
double d = 5521.0000000001;
double f = d - (long) d;
System.out.printf("%.10f%n", f);
prints
0.0000000001
but the value 5521.0000000001 is only an approximate representation.
The actual representation is
double d = 5521.0000000001;
System.out.println(new BigDecimal(d));
BigDecimal db = new BigDecimal(d).subtract(new BigDecimal((long) d));
System.out.println(db);
prints
5521.000000000100044417195022106170654296875
1.00044417195022106170654296875E-10
I suspect whatever you are trying to is not meaningful as you appear to be trying to obtain a value which is not what you think it is.
If you want to do this at your BigDecimal object and not convert it into a String with a formatter you can do it on Java 8 with 2 steps:
stripTrailingZeros()
if scale < 0 setScale to 0 if don't like esponential/scientific
notation
You can try this snippet to better understand the behaviour
BigDecimal bigDecimal = BigDecimal.valueOf(Double.parseDouble("50"));
bigDecimal = bigDecimal.setScale(2);
bigDecimal = bigDecimal.stripTrailingZeros();
if (bigDecimal.scale()<0)
bigDecimal= bigDecimal.setScale(0);
System.out.println(bigDecimal);//50
bigDecimal = BigDecimal.valueOf(Double.parseDouble("50.20"));
bigDecimal = bigDecimal.setScale(2);
bigDecimal = bigDecimal.stripTrailingZeros();
if (bigDecimal.scale()<0)
bigDecimal= bigDecimal.setScale(0);
System.out.println(bigDecimal);//50.2
bigDecimal = BigDecimal.valueOf(Double.parseDouble("50"));
bigDecimal = bigDecimal.setScale(2);
bigDecimal = bigDecimal.stripTrailingZeros();
System.out.println(bigDecimal);//5E+1
bigDecimal = BigDecimal.valueOf(Double.parseDouble("50.20"));
bigDecimal = bigDecimal.setScale(2);
bigDecimal = bigDecimal.stripTrailingZeros();
System.out.println(bigDecimal);//50.2

Categories

Resources