This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 1 year ago.
I have a function that rounds a float to n number of digits using BigDecimal.setScale
private float roundPrice(float price, int numDigits) {
BigDecimal bd = BigDecimal.valueOf(price);
bd = bd.setScale(numDigits, RoundingMode.HALF_UP);
float roundedFloat = bd.floatValue();
return roundedFloat;
}
public void testRoundPrice() {
float numberToRound = 0.2658f;
System.out.println(numberToRound);
float roundedNumber = roundPrice(numberToRound, 5);
System.out.println(roundedNumber);
BigDecimal bd = BigDecimal.valueOf(roundedNumber);
System.out.println(bd);
}
Output:
0.2658
0.2658
0.26579999923706055
How can I prevent BigDecimal from adding all these extra digits at the end of my rounded value?
NOTE: I can't do the following, because I dont have access to the number of digits in the api call function.
System.out.println(bd.setScale(5, RoundingMode.CEILING));
It’s the other way around. BigDecimal is telling you the truth. 0.26579999923706055 is closer to the value that your float has got all the time, both before and after rounding. A float being a binary rather than a decimal number cannot hold 0.2658 precisely. Actually 0.265799999237060546875 is as close as we can get.
When you print the float, you don’t get the full value. Some rounding occurs, so in spite of the float having the aforementioned value, you only see 0.2658.
When you create a BigDecimal from the float, you are really first converting to a double (because this is what BigDecimal.valueOf() accepts). The double has the same value as the float, but would print as 0.26579999923706055, which is also the value that your BigDecimal gets.
If you want a BigDecimal having the printed value of the float rather than the exact value in it or something close, the following may work:
BigDecimal bd = new BigDecimal(String.valueOf(roundedNumber));
System.out.println(bd);
Output:
0.2658
You may get surprises with other values, though, since a float hasn’t got that great of a precision.
EDIT: you were effectively converting float -> double -> String -> BigDecimal.
These insightful comments by Dawood ibn Kareem got me researching a bit:
Actually 0.265799999237060546875.
Well, 0.26579999923706055 is the value returned by calling
toString on the double value. That's not the same as the number
actually represented by that double. That's why
BigDecimal.valueOf(double) doesn't in general return the same value
as new BigDecimal(double). It's really important to understand the
difference if you're going to be working with floating point values
and with BigDecimal.
So what really happened:
Your float internally had the value of 0.265799999237060546875 both before and after rounding.
When you are passing your float to BigDecimal.valueOf(double), you are effectively converting float -> double -> String -> BigDecimal.
The double has the same value as the float, 0.265799999237060546875.
The conversion to String rounds a little bit to "0.26579999923706055".
So your BigDecimal gets the value of 0.26579999923706055, the value you saw and asked about.
From the documentation of BigDecimal.valueOf(double):
Translates a double into a BigDecimal, using the double's
canonical string representation provided by the
Double.toString(double) method.
Links
Stack Overflow question: Is floating point math broken?
Documentation: BigDecimal.valueOf(double)
Stack Overflow question: BigDecimal - to use new or valueOf
I've decided to modify my program to use BigDecimal as the base type for my property price in my object instead of type float. Although tricky at first it is definitely the cleaner solution in the long run.
public class Order {
// float price; // old type
BigDecimal price; // new type
}
Related
This question already has answers here:
Int division: Why is the result of 1/3 == 0?
(19 answers)
Closed 4 years ago.
int totalOptCount = 500;
int totalRespCount=1500;
float percentage =(float)(totalOptCount/totalRespCount);
Why does this always return value 0.0? Also I want to format this into 00.00 format and convert into string?
Because the conversion to float happens after the division has been done. You need:
float percentage = ((float) totalOptCount) / totalRespCount;
You should be able to format using something like:
String str = String.format("%2.02f", percentage);
If you are using int values, using a double may be a better choice and have less rounding error. float can represent int values without error up to ~16 million. double can accurately represent all int values.
double percentage =(double) totalOptCount / totalRespCount;
Percentages are usually multiplied by 100, meaning you can drop the cast.
double percentage = 100.0 * totalOptCount / totalRespCount;
(totalOptCount/totalRespCount)
here both dividend and divisor are of type int which means they will allow only integer values and the answer of such equation will always be an integer literal.
if I break this it will be something like below
(double)(500/1500)
According to the actual calculation, 500/1500 will give you 0.33333 but compiler will convert this into integer literal because both operands are of type int
(double)(0)
Compiler gets an instruction to cast this 0 value to double so you got 0.0 as result
0.0
and then you can change the result to any format as suggeted by #Zach Janicki.
keep in mind if both the operands are of same type than result will be of same type too.
Integer division (which includes long, short, byte, char, int) in Java always returns an int (or long, if one of the parameters is long), rounding towards zero. Your conversion occurs after this calculation.
(The formatting question is already answered by the other answers - alternatively you could also have a look at java.text.NumberFormat, specially java.text.DecimalFormat.)
String.format("%2.02f", (float)totalOptCount/totalRespCount);
to format a double and print out as a percentage, you can use use
System.out.println(new DecimalFormat("##.##").format(yourDouble) + "%"));
This question already has answers here:
Int division: Why is the result of 1/3 == 0?
(19 answers)
Closed 4 years ago.
int totalOptCount = 500;
int totalRespCount=1500;
float percentage =(float)(totalOptCount/totalRespCount);
Why does this always return value 0.0? Also I want to format this into 00.00 format and convert into string?
Because the conversion to float happens after the division has been done. You need:
float percentage = ((float) totalOptCount) / totalRespCount;
You should be able to format using something like:
String str = String.format("%2.02f", percentage);
If you are using int values, using a double may be a better choice and have less rounding error. float can represent int values without error up to ~16 million. double can accurately represent all int values.
double percentage =(double) totalOptCount / totalRespCount;
Percentages are usually multiplied by 100, meaning you can drop the cast.
double percentage = 100.0 * totalOptCount / totalRespCount;
(totalOptCount/totalRespCount)
here both dividend and divisor are of type int which means they will allow only integer values and the answer of such equation will always be an integer literal.
if I break this it will be something like below
(double)(500/1500)
According to the actual calculation, 500/1500 will give you 0.33333 but compiler will convert this into integer literal because both operands are of type int
(double)(0)
Compiler gets an instruction to cast this 0 value to double so you got 0.0 as result
0.0
and then you can change the result to any format as suggeted by #Zach Janicki.
keep in mind if both the operands are of same type than result will be of same type too.
Integer division (which includes long, short, byte, char, int) in Java always returns an int (or long, if one of the parameters is long), rounding towards zero. Your conversion occurs after this calculation.
(The formatting question is already answered by the other answers - alternatively you could also have a look at java.text.NumberFormat, specially java.text.DecimalFormat.)
String.format("%2.02f", (float)totalOptCount/totalRespCount);
to format a double and print out as a percentage, you can use use
System.out.println(new DecimalFormat("##.##").format(yourDouble) + "%"));
I've been trying to sum up decimal values using double in java and it doesn't work well, got a wrong answer.
I've already tried with Double, Float, BigDecimal.
{
double a = 2595.00;
double b = -1760.76;
double c = -834.00;
double d = -.24;
System.out.print(a+b+c+d);
}
The expected result should be "0" But Got 9.1038288019262836314737796783447265625E-15
You can use BigDecimal for this purpose and make sure you input the numbers as String in the BigDecimal constructor:
BigDecimal a = new BigDecimal("2595.00");
BigDecimal b = new BigDecimal("-1760.76");
BigDecimal c = new BigDecimal("-834.00");
BigDecimal d = new BigDecimal("-.24");
System.out.println(a.add(b).add(c).add(d));
Live Example
Output is:
0.00
From the Java docs for BigDecimal(String):
This is generally the preferred way to convert a float or double into
a BigDecimal, as it doesn't suffer from the unpredictability of the
BigDecimal(double) constructor.
Check this SO thread for why double results in a loss of precision.
As already pointed by the previous answers about double precision, the value here is very close to zero. You can see it with System.out.format as well.
System.out.format("%.14f%n",a+b+c+d);
System.out.format("%1.1f%n",a+b+c+d); //to print 0.0
I need to convert a string with value 12.10 to a float value without losing the zero. How can I achieve this in Java.
if you aren't worried about memory then
String str = "12.00";
BigDecimal bd= new BigDecimal(str);
System.out.println(bd);//12.00
a) It makes no sense to store trailing zeroes in a float.
b) 12.1 will not map precisely to a floating point value (although this may not be immediately apparent)
From Bloch, J., Effective Java, 2nd ed, Item 48:
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.
For example, suppose you have $1.03
and you spend 42c. How much money do
you have left?
System.out.println(1.03 - .42);
prints out 0.6100000000000001.
The right way to solve this problem is
to use BigDecimal, int or long
for monetary calculations.
Example:
BigDecimal price = new BigDecimal("12.10");
Use a java.text.DecimalFormat:
DecimalFormat fmt = new DecimalFormat("0.00");
double floatval = fmt.parse("12.10").doubleValue();
String sval = fmt.format(floatval);
Okay. I have been bashing my head against the wall for like 2 hours now trying to figure out why in the world double answer = 364/365; is telling me that answer is 0. Or any other combination of double for that matter, its just truncating the decimal and I just don't know why.
364/365 performs integer division (truncates the decimal).
Try double answer = 364.0/365; to force it to perform floating point division.
Something like:
double days_in_year = 365;
double answer = 364/days_in_year;
would work as well, since one of the operands isn't an integer.
You're taking an int type (364) and dividing by another int type (365) - the answer is going to be an int. This is then stored in a double type answer. You could do the following:
double answer = 364d / 365d;
More info here:
http://mindprod.com/jgloss/division.html
You need do do double division. Right now Java is interpreting it as integer division and returning the truncated int.
What you need is:
double answer = 364 / 365.0;
or
double answer = 364 / (double) 365;
The reason is that the default type of integer literals in java is int and all the result of all int based arithemetic is type casted back to int. Hence though your answer is 0.997, when it is typecasted back it becomes 0:
(int)0.997 = 0
So you can do like this:
364.0/365.0
or
((float)364)/365
All the above answers are right, would just like to add that it is all about GIGO.
double answer = 364/365;
in above code double type implies only to answer variable and arithmetic expression has both operands of int type. So the output of the arithmetic expression is also int type which is then through auto up-casting to double type gives output 0.0, just like below examples:
double ans = 4.0/0;
the above code will give output Infinity as one of the operand is Floating-point number so through auto type-casting 0 is converted to 0.0 and the result is as per the Floating-point datatype.
whereas
double ans = 4/0;
will give java.lang.ArithmeticException: / by zero exception at runtime since both the operands are of datatype int and so the output is as per the Integer datatype irrespective of ans variable datatype being double.