Check Multiple and module of two BigDecimal in java - java

BigDecimal x = new BigDecimal(1.95);
BigDecimal y = new BigDecimal(0.65);
BigDecimal rem = x.remainder(y);
if (rem.compareTo(BigDecimal.ZERO) != 0.) {
System.out.println("Not In mutilple of");
} else {
System.out.println("In mutilple of");
}
System.out.println(rem);
Not giving correct result for the above scenario.
given the input condition but giving incorrect result if it is in multiple of the second value

Explanation
Useing new BigDecimal(double) might lose precision, see the doc:
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.
and:
BigDecimal x= new BigDecimal(1.95);
BigDecimal y = new BigDecimal(0.65);
System.out.println(x); // 1.9499999999999999555910790149937383830547332763671875
System.out.println(y); // 0.65000000000000002220446049250313080847263336181640625
Solution
Use BigDecimal(String val):
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.
and
BigDecimal x = new BigDecimal("1.95");
BigDecimal y = new BigDecimal("0.65");
System.out.println(x); // 1.95
System.out.println(y); // 0.65
System.out.println(x.remainder(y)); // 0.00

Instantiate like
BigDecimal x= new BigDecimal("1.95");
BigDecimal y= new BigDecimal("0.65");
as not all float point number can be represented exactly as doubles.
The output in your code shows
0.65 ss 0.64999999999999995559107901499373838305473327636718750
see https://study.com/academy/lesson/java-floating-point-numbers.html

Related

Need Idempotence on rounding operation

In Java, I would like 0.101d, 0.109999999d, and 0.11000d to all be functionally equivalent. I have attempted to use BigDecimal and a MathContext with 2 digits of precision and RoundingMode.CEILING to do this, but my unit test shows that 0.11000 rounds to 0.12. I want 0.110000d to Round to 0.11.
private static MathContext targetMathContext = new MathContext(2, RoundingMode.CEILING);
public static double roundedTarget(double d) {
BigDecimal bd = new BigDecimal(d,targetMathContext);
return bd.doubleValue();
}
JUnit:
double c = 0.445d;
double s = 0.5d;
double p = (s-c)/s; // 0.1099999..... in dfp
double rgpp = roundedTarget(p); // 0.11
double rgppp = roundedTarget(rgpp); // 0.12
// operation is not idempotent as f(x) != f(f(x)) :(
Assert.assertEquals("These values should be equal",rgpp,rgppp);
Solution:
public static double roundedTarget(double d) {
return BigDecimal.valueOf(d)
.setScale(2,BigDecimal.ROUND_CEILING)
.doubleValue();
}
I'm reluctant to call this operation idempotent, since the input of your first application of the function would be different than the next (since the result you get would change the value of x).
In either event, main issue is that you're using doubles in one spot (and introducing floating-point inaccuracies), and BigDecimal in another (if used correctly, is less impacted by those inaccuracies).
The easiest thing to do would be to set a scale of 2 decimal places on your doubles, and then round them however you like. As an example, all of these values satisfy the conditions you mention you want in your comments.
BigDecimal firstDecimal = BigDecimal.valueOf(0.101).setScale(2, RoundingMode.CEILING);
BigDecimal secondDecimal = BigDecimal.valueOf(0.10999).setScale(2, RoundingMode.CEILING);
BigDecimal thirdDecimal = BigDecimal.valueOf(0.110000).setScale(2, RoundingMode.CEILING);
BigDecimal fourthDecimal = BigDecimal.valueOf(0.1101).setScale(2, RoundingMode.CEILING);
System.out.println(firstDecimal); // 0.11
System.out.println(secondDecimal); // 0.11
System.out.println(thirdDecimal); // 0.11
System.out.println(fourthDecimal); // 0.12
The main takeaway here is: if you're going to use BigDecimal, be consistent with it throughout. There's no real reason to interlace or interweave working with raw doubles and BigDecimal, as it will only lead to headaches like this.

Setting precision for double in java

DecimalFormat df = new DecimalFormat("#.000000");
int a[] = { 2, 2, 3, 3, 4, 4 };
double sum = 0.000000;
for (int i = 0; i < a.length; i++)
{
sum = sum + (double) a[i];
}
output1=Double.valueOf(df.format(sum / a.length));
where sum/a.length value is 3. output1 is double variable. Now the result I wanted is 3.000000 and it must be store in double variable output1 but I can't get it.
Although in certain cases it might work, in general there is no way to determine/force the decimal precision of a double value, or indeed any IEEE floating point number.
If you want decimal precision in Java, use BigDecimal. This is even more important if the numbers you work with represent money.
If an approximate result is good enough (and there are lots of calculations where it is), you can use double but be aware that it's a binary floating point number and accurate rounding to decimals might not always be possible.
The primitive type double is an approximation of a real number, with a sequence of (negative) powers of 2.
Hence the decimal notation 0.2 = 0*2-1 + ... + 1*2-4 + ... with an error as one would need an infinite sequence in base 2.
If one wants a precision with the value, one needs BigDecimal:
BigDecimal oneFifth = new BigDecimal("0.200"); // Precision/scale 3
BigDecimal hundredPlusOnefifth =
oneFifth.multiply(BigDecimal.valueOf(501)); // 100.200
Using a String in the constructor, BigDecimal can set the precision.
Not so nice writing expressions in BigDecimal though.
With double one might live, while carefully rounding at appropriate points in the code. There always will be a small error and, outputting needs a formatter as the number of digits is lost.
The value of 3.0 and 3.00000 are the same in a double variable. When you print it, format it the way you want:
System.out.println( df.format( output1 ) );
Looks like sum is int and you have the result of integer division (because a.length is int). Just multiply one of those values by 1.0:
output1 = Double.valueOf(df.format((sum * 1.0) / a.length));
With your edited code, your problem is not in obtaining the value of output1 but how you show it. Don't print output1 directly, instead use the DecimalFormat you used previously:
System.out.println(df.format(output1));

Problems with BigDecimal.ROUND_UP

Why I am getting 184.84 as sell rate? Why is it acting like this in BigDecimal.ROUND_UP.
I checked with the BigDecimal.ROUND_HALF_EVEN. It works fine. But I wanna know why is it acting like this.
for(int i = 0; i < 50; i++){
double sellrate = 184.83;
BigDecimal sellRate = new BigDecimal(sellrate);
sellRate = sellRate.setScale(2,BigDecimal.ROUND_UP);
System.out.println("sellRate : "+sellRate);
}
This is why:
double sellrate = 184.83;
BigDecimal sellRate = new BigDecimal(sellrate);
System.out.println("sellRate: " + sellRate);
prints
sellRate: 184.830000000000012505552149377763271331787109375
This is explained in the BigDecimal 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.
Use the String constructor
BigDecimal sellRate = new BigDecimal("184.83");
if you want to get the exact value for your BigDecimal.
The BigDecimal(double) constructor is not very reliable, check the API javadoc. The preferred way to construct a BigDecimal is to use the BigDecimal(String) constructor.
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.

java BigDecimal subraction failing

I tried the following code. but getting different result when subtracting using BigDecimal.
double d1 = 0.1;
double d2 = 0.1;
System.out.println("double result: "+ (d2-d1));
float f1 = 0.1F;
float f2 = 0.1F;
System.out.println("float result: "+ (f2-f1));
BigDecimal b1 = new BigDecimal(0.01);
BigDecimal b2 = new BigDecimal(0.01);
b1 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b1);
Result:
double result: 0.0
float result: 0.0
BigDecimal result: 0E-59
I am still working on this. can anyone please clarify.
[There are a lot of answers here telling you that binary floating-point can't exactly represent 0.01, and implying that the result you're seeing is somehow inexact. Whilst the first part of that is true, it's not really the core issue here.]
The answer is that "0E-59" is equal to 0. Recall that a BigDecimal is the combination of an unscaled value and a decimal scale factor:
System.out.println(b1.unscaledValue());
System.out.println(b1.scale());
displays:
0
59
The unscaled value is 0, as expected. The "strange" scale value is simply an artifact of the decimal expansion of the non-exact floating-point representation of 0.01:
System.out.println(b2.unscaledValue());
System.out.println(b2.scale());
displays:
1000000000000000020816681711721685132943093776702880859375
59
The next obvious question is, why doesn't BigDecimal.toString just display b1 as "0", for convenience? The answer is that the string representation needs to be unambiguous. From the Javadoc for toString:
There is a one-to-one mapping between the distinguishable BigDecimal values and the result of this conversion. That is, every distinguishable BigDecimal value (unscaled value and scale) has a unique string representation as a result of using toString. If that string representation is converted back to a BigDecimal using the BigDecimal(String) constructor, then the original value will be recovered.
If it just displayed "0", then you wouldn't be able to get back to this exact BigDecimal object.
Use constructor from String: b1 = new BigDecimal("0.01");
Java loss of precision
(slide 23)
http://strangeloop2010.com/system/talks/presentations/000/014/450/BlochLee-JavaPuzzlers.pdf
Interesting, the values appear to be equal and subtraction does give you zero, it appears to just be an issue with the printing code. The following code:
import java.math.BigDecimal;
public class Test {
public static void main(String args[]) {
BigDecimal b1 = new BigDecimal(0.01);
BigDecimal b2 = new BigDecimal(0.01);
BigDecimal b3 = new BigDecimal(0);
if (b1.compareTo(b2) == 0) System.out.println("equal 1");
b1 = b1.subtract(b2);
if (b1.compareTo(b3) == 0) System.out.println("equal 2");
System.out.println("BigDecimal result: "+ b1);
}
}
outputs both equal messages, indicating that the values are the same and that you get zero when you subtract.
You could try to raise this as a bug and see what Oracle comes back with. It's likely they'll just state that 0e-59 is still zero, so not a bug, or that the rather complex behaviour being described on the BigDecimal documentation page is working as intended. Specifically, the point that states:
There is a one-to-one mapping between the distinguishable BigDecimal values and the result of this conversion. That is, every distinguishable BigDecimal value (unscaled value and scale) has a unique string representation as a result of using toString. If that string representation is converted back to a BigDecimal using the BigDecimal(String) constructor, then the original value will be recovered.
That fact that the original value needs to be recoverable means that toString() needs to generate a unique string for each scale, which is why you're getting 0e-59. Otherwise, converting the string back to a BigDecimal may give you a different value (unscaled-value/scale tuple).
If you really want zero to show up as "0" regardless of the scale, you can use something like:
if (b1.compareTo(BigDecimal.ZERO) == 0) b1 = new BigDecimal(0);
You have to get the return value:
BigDecimal b3 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b3);
BigDecimal(double val)
1.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.
2.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.
3.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.
So the real question is: with the following code,
BigDecimal b1 = new BigDecimal(0.01);
BigDecimal b2 = new BigDecimal(0.01);
b1 = b1.subtract(b2);
why does b1.toString() evaluate to "0E-59" and not to something like "0.0", "0E0" or just "0"?
The reason is that toString() prints the canonical format of the BigDecimal. See BigDecimal.toString() for more information.
At the end, 0E-59 is 0.0 - it is 0*10^59 which mathematically evaluates to 0. So, the unexpected result is a matter of the internal representation of the BigDecimal.
To get the float or double values, use
b1.floatValue());
or
b1.doubleValue());
Both evaluate to 0.0.
It's a known issue, BigDecimal(double val) API The results of this constructor can be somewhat unpredictable. Though it looks really wierd in this interpertation. Actual reason is that new BigDecimal(0.01) produces a BigDecimal with approx values
0.01000000000000000020816681711721685132943093776702880859375
which has a long precision, and so the result of subtract has a long precision too.
Anyway, we can solves the "problem" this way
BigDecimal b1 = new BigDecimal("0.01");
BigDecimal b2 = new BigDecimal("0.01");
or we can use a constructor with setting a precision
BigDecimal b1 = new BigDecimal(0.01, new MathContext(1));
BigDecimal b2 = new BigDecimal(0.01, new MathContext(1));
Use like this:
BigDecimal b1 = BigDecimal.valueOf(0.01);
BigDecimal b2 = BigDecimal.valueOf(0.01);
b1 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b1);

"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