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);
Related
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
}
I need to format a float to "n"decimal places.
was trying to BigDecimal, but the return value is not correct...
public static float Redondear(float pNumero, int pCantidadDecimales) {
// the function is call with the values Redondear(625.3f, 2)
BigDecimal value = new BigDecimal(pNumero);
value = value.setScale(pCantidadDecimales, RoundingMode.HALF_EVEN); // here the value is correct (625.30)
return value.floatValue(); // but here the values is 625.3
}
I need to return a float value with the number of decimal places that I specify.
I need Float value return not Double
.
You may also pass the float value, and use:
String.format("%.2f", floatValue);
Documentation
Take a look at DecimalFormat. You can easily use it to take a number and give it a set number of decimal places.
Edit: Example
Try this this helped me a lot
BigDecimal roundfinalPrice = new BigDecimal(5652.25622f).setScale(2,BigDecimal.ROUND_HALF_UP);
Result will be
roundfinalPrice --> 5652.26
Of note, use of DecimalFormat constructor is discouraged. The javadoc for this class states:
In general, do not call the DecimalFormat constructors directly, since the NumberFormat factory methods may return subclasses other than DecimalFormat.
https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html
So what you need to do is (for instance):
NumberFormat formatter = NumberFormat.getInstance(Locale.US);
formatter.setMaximumFractionDigits(2);
formatter.setMinimumFractionDigits(2);
formatter.setRoundingMode(RoundingMode.HALF_UP);
Float formatedFloat = new Float(formatter.format(floatValue));
Here's a quick sample using the DecimalFormat class mentioned by Nick.
float f = 12.345f;
DecimalFormat df = new DecimalFormat("#.00");
System.out.println(df.format(f));
The output of the print statement will be 12.35. Notice that it will round it for you.
Kinda surprised nobody's pointed out the direct way to do it, which is easy enough.
double roundToDecimalPlaces(double value, int decimalPlaces)
{
double shift = Math.pow(10,decimalPlaces);
return Math.round(value*shift)/shift;
}
Pretty sure this does not do half-even rounding though.
For what it's worth, half-even rounding is going to be chaotic and unpredictable any time you mix binary-based floating-point values with base-10 arithmetic. I'm pretty sure it cannot be done. The basic problem is that a value like 1.105 cannot be represented exactly in floating point. The floating point value is going to be something like 1.105000000000001, or 1.104999999999999. So any attempt to perform half-even rounding is going trip up on representational encoding errors.
IEEE floating point implementations will do half-rounding, but they do binary half-rounding, not decimal half-rounding. So you're probably ok
public static double roundToDouble(float d, int decimalPlace) {
BigDecimal bd = new BigDecimal(Float.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
}
This is a much less professional and much more expensive way but it should be easier to understand and more helpful for beginners.
public static float roundFloat(float F, int roundTo){
String num = "#########.";
for (int count = 0; count < roundTo; count++){
num += "0";
}
DecimalFormat df = new DecimalFormat(num);
df.setRoundingMode(RoundingMode.HALF_UP);
String S = df.format(F);
F = Float.parseFloat(S);
return F;
}
I was looking for an answer to this question and later I developed a method! :) A fair warning, it's rounding up the value.
private float limitDigits(float number) {
return Float.valueOf(String.format(Locale.getDefault(), "%.2f", number));
}
I think what you want ist
return value.toString();
and use the return value to display.
value.floatValue();
will always return 625.3 because its mainly used to calculate something.
You can use Decimal format if you want to format number into a string, for example:
String a = "123455";
System.out.println(new
DecimalFormat(".0").format(Float.valueOf(a)));
The output of this code will be:
123455.0
You can add more zeros to the decimal format, depends on the output that you want.
I would like to convert a double (for example price with a value of 1.90) to an integer without losses! I am making a program that processes money, and I have to input them as doubles, and then convert them to integers so that I can proccess them and for example when I have 1.90 euros and i want to convert it to cents it will appear as 189 cents instead of 190! please help me :) thanks in advance
Check the details on how doubles and floats behave in java in the JLS
Well, you could round the value to desired precision. As long as you're within given format's precision (~15 digits for double ~7 for float) you'll get good results.
double fractValue = 1.90; // 1.8999999....
int val = (int)Math.round(fractValue*100);
It's much better idea to use BigDecimal though. You will never lose any precision then.
double x = 1.89; //Or whatever
String[] y = String.valueOf(x).split("[.]"); // Returns array having 1 and 89
String z = y[0] + y[1]; //String which is = 189
if(y[1].length()==1)
z +="0"; // Add a 0 in end if required
int finalInt = Integer.parseInt(z); //Convert String to Integer
finalInt should be what you want.
I need to format a float to "n"decimal places.
was trying to BigDecimal, but the return value is not correct...
public static float Redondear(float pNumero, int pCantidadDecimales) {
// the function is call with the values Redondear(625.3f, 2)
BigDecimal value = new BigDecimal(pNumero);
value = value.setScale(pCantidadDecimales, RoundingMode.HALF_EVEN); // here the value is correct (625.30)
return value.floatValue(); // but here the values is 625.3
}
I need to return a float value with the number of decimal places that I specify.
I need Float value return not Double
.
You may also pass the float value, and use:
String.format("%.2f", floatValue);
Documentation
Take a look at DecimalFormat. You can easily use it to take a number and give it a set number of decimal places.
Edit: Example
Try this this helped me a lot
BigDecimal roundfinalPrice = new BigDecimal(5652.25622f).setScale(2,BigDecimal.ROUND_HALF_UP);
Result will be
roundfinalPrice --> 5652.26
Of note, use of DecimalFormat constructor is discouraged. The javadoc for this class states:
In general, do not call the DecimalFormat constructors directly, since the NumberFormat factory methods may return subclasses other than DecimalFormat.
https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html
So what you need to do is (for instance):
NumberFormat formatter = NumberFormat.getInstance(Locale.US);
formatter.setMaximumFractionDigits(2);
formatter.setMinimumFractionDigits(2);
formatter.setRoundingMode(RoundingMode.HALF_UP);
Float formatedFloat = new Float(formatter.format(floatValue));
Here's a quick sample using the DecimalFormat class mentioned by Nick.
float f = 12.345f;
DecimalFormat df = new DecimalFormat("#.00");
System.out.println(df.format(f));
The output of the print statement will be 12.35. Notice that it will round it for you.
Kinda surprised nobody's pointed out the direct way to do it, which is easy enough.
double roundToDecimalPlaces(double value, int decimalPlaces)
{
double shift = Math.pow(10,decimalPlaces);
return Math.round(value*shift)/shift;
}
Pretty sure this does not do half-even rounding though.
For what it's worth, half-even rounding is going to be chaotic and unpredictable any time you mix binary-based floating-point values with base-10 arithmetic. I'm pretty sure it cannot be done. The basic problem is that a value like 1.105 cannot be represented exactly in floating point. The floating point value is going to be something like 1.105000000000001, or 1.104999999999999. So any attempt to perform half-even rounding is going trip up on representational encoding errors.
IEEE floating point implementations will do half-rounding, but they do binary half-rounding, not decimal half-rounding. So you're probably ok
public static double roundToDouble(float d, int decimalPlace) {
BigDecimal bd = new BigDecimal(Float.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
}
This is a much less professional and much more expensive way but it should be easier to understand and more helpful for beginners.
public static float roundFloat(float F, int roundTo){
String num = "#########.";
for (int count = 0; count < roundTo; count++){
num += "0";
}
DecimalFormat df = new DecimalFormat(num);
df.setRoundingMode(RoundingMode.HALF_UP);
String S = df.format(F);
F = Float.parseFloat(S);
return F;
}
I was looking for an answer to this question and later I developed a method! :) A fair warning, it's rounding up the value.
private float limitDigits(float number) {
return Float.valueOf(String.format(Locale.getDefault(), "%.2f", number));
}
I think what you want ist
return value.toString();
and use the return value to display.
value.floatValue();
will always return 625.3 because its mainly used to calculate something.
You can use Decimal format if you want to format number into a string, for example:
String a = "123455";
System.out.println(new
DecimalFormat(".0").format(Float.valueOf(a)));
The output of this code will be:
123455.0
You can add more zeros to the decimal format, depends on the output that you want.
I have a problem concerned with losing of precision
my task is to print numbers as strings
int exponent = ...
int[] Mantissas = { 1, 2, 5 };
double dataStep = java.lang.Math.pow(10.0, exponent) * Mantissas[mantissaIndex];
...
for (int i = 0; i < NSteps; i++)
steps[i] = firstStep + i * dataStep;
draw(steps);
for example, 0.2*7=1.4000000000000001; 0.0000014/10=1.3999999999999998E-7
how to figure out this problem?
UPD: The main problem is string output formating. i don't bother about losting of about 0.00000001 value.
Now I solved it as String.format("%f", value),
but I think it's not good approach
As mentioned by others you have to use java.math.BigDecimal instead of float/double. This however comes with its own set of problems.
For instance when you call BigDecimal(double) the value you pass in will be expanded to its full representation:
BigDecimal oneTenth = new BigDecimal(0.1);
BigDecimal oneMillion = new BigDecimal(1000000);
oneTenth.multiply(oneMillion)
out> 100000.0000000000055511151231257827021181583404541015625000000
But when you use the BigDecimal(String) constructor the eact value is represented and you get
BigDecimal oneTenth = new BigDecimal("0.1");
BigDecimalr oneMillion = new BigDecimal(1000000);
oneTenth.multiply(oneMillion)
out> 100000.0
You can read more on BigDecimal's limitations in Joshua Bloch and Neal Gafter's splendid Java puzzlers book and in this informative article. Finally note that toString on BigDecimal's will print in scientific notation so you will properly have to use toPlainString instead.
The double type does not have infinite precision and cannot represent decimals exactly. You are observing normal rounding errors. For arbitrary precision arithmetic, you will need to use java.math.BigDecimal instead.
Search for "floating point numbers" on SO and you'll get a slew of answers as to why this happens. It has to do with how floating point numbers are represented in computers.
How is floating point stored? When does it matter?
Another article on the matter - Floating Point Approximation