Strange behavior by RoundingMode.DOWN [duplicate] - java

I have a BigDecimal defined like this:
private static final BigDecimal sd = new BigDecimal(0.7d);
if i print it, i get the value:
0.6999999999999999555910790149937383830547332763671875
which causes some wrong calculations. Does anyone know a way to get the exact value of 0.7 as BigDecimal? Change it to 0.71 would view the right result, but it shouldn't be like that

Use a String literal:
private static final BigDecimal sd = new BigDecimal("0.7");
If you use a double, actually public BigDecimal(double val) is called. The reason you do not get 0.7 is that it cannot be exactly represented by a double. See the linked JavaDoc for more information.

Perhaps if you bothered to read the documentation, i.e. the javadoc of the constructor you're using, you'd already know the answer.
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.
When you then look at the javadoc of BigDecimal.valueOf(double), you'll find:
Note: This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double).
So there is your answer: Use BigDecimal.valueOf(0.7d), not new BigDecimal(0.7d).

You should use the declared value in String literal such as new BigDecimal("0.7");

Here are three ways:
private static final BigDecimal sd = new BigDecimal("0.7");
private static final BigDecimal sd = new BigDecimal(0.7d, MathContext.DECIMAL32);
private static final BigDecimal sd = new BigDecimal(0.7d, MathContext.DECIMAL64)

Constructing a BigDecimal from a double is surprisingly complicated. First, it can only be done via the detour of a string. (You can't get the constructor with double and a MathContext right. I've tried a lot. At the latest in cases in which the number of places before the decimal point would need to change due to rounding, it becomes difficult. Hence the warning in the Javadoc that you shouldn’t use it.)
However, even there, it is not enough with a simple String.format(), since String.format() is sensitive to the default Locale and outputs different decimal separators depending on system/VM settings, while the BigDecimal constructor always requires a dot as a decimal separator. So you have to construct your own Formatter with Locale.US. If you have this up and running, you will get a warning of an unclosed resource.
I found this to work:
static BigDecimal toBigDecimal(double value, int decimalPlaces) {
String format = "%." + decimalPlaces + "f";
try (Formatter formatter = new Formatter(Locale.US)) {
String formatted = formatter.format(format, value).toString();
return new BigDecimal(formatted);
}
}

Related

Converting BigDecimal to double value

I want to ask how to transform all my String to double with exponential.
when I use the string that length is over seven it's doing fine .
new BigDecimal("12345678").doubleValue() => 1.2345678E7
but seven and under I can't export exponential number.
new BigDecimal("1234567").doubleValue() => 1234567.0
what I want is like 1.234567E6.
Is there any way to do this? I've been searching for a while ,but got nothing.
The problem is the type I return must be double . After transforming the value under seven I can only get the value without exponential.
double test = new BigDecimal("1.234567E6").doubleValue() ;//output 1234567.0
but I need it to be 1.234567E6 and return to caller. Is that Impossible?
You should know that 1.2345678e7 and 12345678.0 are exactly the same value, only with different textual representations. You could represent 1234567.0 as 1.234567e6 too. Also exactly the same double, just a different way of writing it out.
The default output shows values with more than a certain number of significant digits in exponential format ("e-form"), otherwise as plain decimal format.
So you may want to change the formatting of the doubles you receive. This can be done with e.g. DecimalFormat or String.format() or similar. That does not change the doubles, only the way they are presented in a string.
For your problem, you want to convert the value to the BigDecimal with exponential, you can use the DecimalFormat. You can also change the scale for the output value digits.
import java.math.*;
import java.text.*;
public class HelloWorld{
public static void main(String []args){
double a = new BigDecimal("1234567").doubleValue();
String b;
System.out.println(a);
NumberFormat formatter = new DecimalFormat("0.0E0");
formatter.setRoundingMode(RoundingMode.DOWN);
formatter.setMinimumFractionDigits(5); //<---Scale
b = formatter.format(a);
System.out.println(b);
}
}
The output will be like:
1234567.0 //Unformatted Value
1.23456E6 //Formatted Value
See the section about Scientific Notation in java.text.DecimalFormat.
For example,
DecimalFormat scientificFormat = new DecimalFormat("0.###E0");
System.out.println(scientificFormat.format(BigDecimal.valueOf(123456L)));
System.out.println(scientificFormat.format(BigDecimal.valueOf(1234567L)));
scientificFormat.setMinimumFractionDigits(10);
System.out.println(scientificFormat.format(BigDecimal.valueOf(12345678L)));
would give you
1,235E5
1,235E6
1,2345678000E7
Change the pattern to match what you're looking for.

BigDecimal method call causes compile time error

I am trying to format and save a double using BigDecimal, as I want to use the rounding modes available to it. This code however causes a compilation error:
import java.math.*;
public class Format {
public static void main (String[] args) {
double result = 123.456;
int decimalPlaces = 2;
result = BigDecimal.valueOf(result).setScale(decimalPlaces, RoundingMode.HALF_UP);
System.out.println(result);
}
}
The error encountered:
Format.java:10: error: incompatible types: BigDecimal cannot be converted to double
result = BigDecimal.valueOf(result).setScale(decimalPlaces, RoundingMode.HALF_UP);
The value of result I am looking for is:
123.46
Please help me understand how to correct this.
Edit: I understand that my brevity in asking the question has lead to the question being down voted. It appears as if I encountered a compile error and immediately came to ask for a solution which is not the case. I'll take the time to give clearer exposition next time.
It fails because your variable result is of type double, while operations on a BigDecimal will usually produce values of type BigDecimal. You cannot put a BigDecimal in a variable of type double. You need a variable of type BigDecimal.
Which is what the error message was very clearly telling you, and it is rather concerning that you chose to ignore it and come ask here instead.
The value of result I am looking for is:
123.46
A double is a rather wrong tool for getting to that. doubles are approximate, and you're telling that you want a very exact result and nothing else than that result. Therefore, not an approximation.
If your only goal is to display 123.46 as a result of the number 123.456 you had and that you round to two decimals, then the key word is display. The right tool for displaying text is a String, not a double.
Use a DecimalFormat.
NumberFormat format = new DecimalFormat("0.00");
String s = format.format(result);
System.out.println(s);
No need for BigDecimal.
It might be too obvious, but you cannot assign a double to a BigDecimal.
BigDecimal decimal = BigDecimal.valueOf(result)
.setScale(decimalPlaces, RoundingMode.HALF_UP);
System.out.println(decimal);
Which gives the correct output of 123.46.
If you want a double instead, use BigDecimal.doubleValue().
BigDecimal::setScale return BigDecimal not a double, You have to use BigDecimal::doubleValue() to get the double value, like this :
result = BigDecimal.valueOf(result)
.setScale(decimalPlaces, RoundingMode.HALF_UP)
.doubleValue();
System.out.println(result);// 123.46
setScale() returns a BigDecimal which you are casting to double and hence the error.
https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#setScale(int)

Bigdecimal Not giving exact output in Java

Im adding three big decimals here, but it should give me accurate answer. I'm having two strings here and then converting to big decimal. Please dont ask why Im using strings. There is some business where I will get these values as string then I need to convert. Please find the code
BigDecimal a= new BigDecimal(100.05); --> This value I receive from web service. Its a decimal value from the service.
String b= "100.05";
String c= "200.03";
System.out.println(a.add(new BigDecimal(b).add(new BigDecimal(c))));
Output it gives
400.1299999999999971578290569595992565155029296875
Where as it should be 400.13
The problem is your use of new BigDecimal(100.05). The value of a is then 100.0499999999999971578290569595992565155029296875.
If you had specified that value as a string instead, all would be well:
BigDecimal a = new BigDecimal("100.05");
String b = "100.05";
String c = "200.03";
System.out.println(a.add(new BigDecimal(b).add(new BigDecimal(c))));
// Output: 400.13
If you only have the input as a double, you can use BigDecimal.valueOf(double) instead of calling the constructor:
BigDecimal a = BigDecimal.valueOf(100.05); // a is now exactly 100.05
Compare the BigDecimal(double) documentation:
Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value. (...)
With that of BigDecimal.valueOf(Double):
Translates a double into a BigDecimal, using the double's canonical string representation provided by the Double.toString(double) method.
Note: This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double).
new BigDecimal(100.05)
This gives 100.0499999999999971578290569595992565155029296875, because 100.05 cannot be represented exactly as a double.
You have to use string here as well:
new BigDecimal("100.05")
As you get this value from a web-service, you probably convert it from a String to a float/double. If this is the case, just skip that conversion step.
If your web-service stub maps the return value to float/double, you can consider mapping it to a String directly and then feed it to BigDecimal constructor, like this:
double v = 100.05; // Value from web service
BigDecimal a= new BigDecimal(String.valueOf(v));
String b= "100.05";
String c= "200.03";
System.out.println(a.add(new BigDecimal(b).add(new BigDecimal(c))));
Live Example
That works because the string will only contain as many digits as are needed to differentiate the almost-100.05 value from the next value on either side that can be represented, and so we get the string "100.05", which then BigDecimal can process correctly.
You can format the answer to Decimal places using String.format and specifiying how many digits.
System.out.println(String.format("%.2f", a.add(new BigDecimal(b).add(new BigDecimal(c)))));

String to BigDecimal, which one is good approch

I am trying to convert string to BigDecimal. Please tell which one is below is good approch
BigDecimal selAmount = BigDecimal.ZERO;
String amount = "1234";
selAmount = BigDecimal.valueOf(Double.parseDouble(amount));
or
selAmount = new BigDecimal(amount);
Don't use the first approach. If the string represents a value which can't be exactly represented by a double, you'll get accuracy issues.
You can't use the second approach either, since there is no overload of BigDecimal.valueOf which accepts a String.
So, option 3:
BigDecimal selAmount = new BigDecimal(amount);
You can pass string directly to the constructor of BigDecimal.
The second approach is better to proceed with, simply pass the string to the constructor of BigDecimal. The first approach may have precision issues.
From JavaDocs has
public BigDecimal(String val)
Translates the string representation of a BigDecimal into a
BigDecimal. The string representation consists of an optional sign,
'+' ( '\u002B') or '-' ('\u002D'), followed by a sequence of zero or
more decimal digits ("the integer"), optionally followed by a
fraction, optionally followed by an exponent.
BigDecimal.valueOf(double)); calls return new BigDecimal(Double.toString(val)); implicitly. So, your second case would be more efficient, and as Thilo says, more correct.
Code :
*/
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
Passing string as a constructor is better way. You will lose precision in the first case.
e.g
String s = "123";
BigDecimal bigDecimal = new BigDecimal(s);

How can I impose decimalFormat on the result of a method?

I have this code:
public double theInterest(){
return (accountBalance*i) +accountBalance;
My question is, is there a way I can impose DecimalFormat to the result of the equation so that it will display up to 2 decimal places?
Any help is really appreciated.
Your question is as posted nonanswerable since the method returns a double, and DecimalFormat can only return a String. It makes no sense to try to return a formatted double. I don't recommend that you change the method, but consider creating a separate method, say getInterestString() that takes the result of theInterest(), and formats it with your DecimalFormatter and then returns this formatted String.
i.e.,
public String getInterestString() {
NumberFormat moneyFormat = NumberFormat.getCurrencyInstance();
return moneyFormat.format(theInterest();
}
Or more generally,
private NumberFormat moneyFormat = NumberFormat.getCurrencyInstance();
public String currencyFormat(double numberValue) {
moneyFormat.format(numberValue);
}
Edit: and as svc well states, you should strive to avoid using floating point numbers for monetary calculations as the inaccuracies matter. Better to use BigDecimal.
You should not use double at all for financial work. Typically, you use a BigDecimal where your numbers are valued by the lowest currency unit for your country:
BigDecimal tenDollars = new BigDecimal(1000L, 2);
// Alternatively, use the BigDecimal(BigInteger, int) constructor.
You can set the rounding mode by using MathContexts. Internally, you store the BigDecimal for your currency values; only when you display to the user do you convert to a string using a format.

Categories