I'm searching for the best practice to convert Float to Double without loosing precision. So far I only found that a proper way to do so is to convert the Float to String and the String to Double.
Searching the Float API I stumbled upon this method doubleValue(). I thought this is a static constructor that will return a double from my Float without loosing precision but the following code behaves like a cast:
public class Main {
public static void main(String[] args) {
Float floatNumber= 4.95f;
Double doubleNumber= floatNumber.doubleValue();
System.out.println(doubleNumber);
}
}
The output is 4.949999809265137
Searching any other documentation about this from the Float API I didn't find any documentation to tell me what exactly happens when I call that method. Does anybody have any idea? Or can someone confirm that all the method does is perform a cast my Float to a double and unbox it?
The simple primitive-type cast (or even an implicit conversion) will do all you need, if you really want to preserve the value of a float:
float f = 4.95f;
double d = f;
Fundamentally 4.95f is already inaccurate - you can't represent that number exactly as a float. The exact value of floatNumber is represented in doubleNumber too - it's just that that value is not 4.95.
If you really care about the exact decimal digits, you should be using BigDecimal instead (or a scaled integer).
Widening float to double conversion doesn't lose precision in java.
When you use floating point, you get rounding errors which can be potentially "corrected" by rounding the result (assuming you know the precision you want). BigDecimal handles this by always knowing the precision.
float floatNumber= 4.95f;
double doubleNumber = (double) floatNumber;
System.out.println("After cast " + doubleNumber);
System.out.printf("Show two decimal places. %.2f%n", doubleNumber);
doubleNumber = Math.round(doubleNumber * 100) / 100.0;
System.out.println("After rounding to two places " + doubleNumber);
prints
After cast 4.949999809265137
Show two decimal places. 4.95
After rounding to two places 4.95
Related
I've got float in my app, which generates infinity:
float fx = eddystoneAdvEvtCurr_mA * txTime_ms / (eddystoneFrame.interval / 1000);
It returns 61,333333(3). I tried to convert it to String and split, convert to int, but nothing works. Can I just make 61,33 or 61,3 from it? When I cast it to int it returns strange value: 2147483647. It looks like a simple thing but I've spent lot of time to figure it out.
To understand how the floats are stored in the JVM, you can take a look at this wikipedia article. (you can also take a look at the JLS)
If you want to have a fix precision, you shall use BigDecimal. example :
BigDecimal ten = BigDecimal.valueOf(10);
BigDecimal three = BigDecimal.valueOf(3);
int precision = 2;
System.out.println(ten.divide(three, precision, BigDecimal.ROUND_CEILING));
Output :
3.34
You can cut of the digits like this:
float fx = eddystoneAdvEvtCurr_mA * txTime_ms / (eddystoneFrame.interval / 1000);
float fxRounded= ((int)(100*fx))/100.f
fxRounded should be 61,33
Here the idea:
multiply the float by a power of 10 accordingly to the number of decimal places you want.
cast the result to an int (this cuts of all digits behind the comma)
divide by the the same power of 10 (as in 1.) but make it a float or double division (therefore use /100. and not /100) to keep the resulting digits right of the comma.
Here is my tested code: it prints: 61.33
public class T{
public static void main(String[] args){
float fx = 61.3333333333f;
float fxRounded=((int)(100*fx))/100.f;
System.err.println(""+fxRounded);
}
}
you can use
Math.round();
to round of a double or a float value
I am facing an issue related to converting double to float. Actually, I store a float type, 23423424666767, in a database, but when we get data from the database in the below code, getInfoValueNumeric(), it's of double type. The value we get is in the 2.3423424666767E13 form.
So how do we get a float format data like 23423424666767?
2.3423424666767E13 to 23423424666767
public void setInfoValueNumeric(java.lang.Double value) {
setValue(4, value);
}
#javax.persistence.Column(name = "InfoValueNumeric", precision = 53)
public java.lang.Double getInfoValueNumeric() {
return (java.lang.Double) getValue(4);
}
Just cast your double to a float.
double d = getInfoValueNumeric();
float f = (float)d;
Also notice that the primitive types can NOT store an infinite set of numbers:
float range: from 1.40129846432481707e-45 to 3.40282346638528860e+38
double range: from 1.7e–308 to 1.7e+308
I suggest you to retrieve the value stored into the Database as BigDecimal type:
BigDecimal number = new BigDecimal("2.3423424666767E13");
int myInt = number.intValue();
double myDouble = number.doubleValue();
// your purpose
float myFloat = number.floatValue();
BigDecimal provide you a lot of functionalities.
Convert Double to Float
public static Float convertToFloat(Double doubleValue) {
return doubleValue == null ? null : doubleValue.floatValue();
}
Convert double to Float
public static Float convertToFloat(double doubleValue) {
return (float) doubleValue;
}
This is a nice way to do it:
Double d = 0.5;
float f = d.floatValue();
if you have d as a primitive type just add one line:
double d = 0.5;
Double D = Double.valueOf(d);
float f = D.floatValue();
Converting from double to float will be a narrowing conversion. From the doc:
A narrowing primitive conversion may lose information about the
overall magnitude of a numeric value and may also lose precision and
range.
A narrowing primitive conversion from double to float is governed by
the IEEE 754 rounding rules (§4.2.4). This conversion can lose
precision, but also lose range, resulting in a float zero from a
nonzero double and a float infinity from a finite double. A double NaN
is converted to a float NaN and a double infinity is converted to the
same-signed float infinity.
So it is not a good idea. If you still want it you can do it like:
double d = 3.0;
float f = (float) d;
To answer your query on "How to convert 2.3423424666767E13 to
23423424666767"
You can use a decimal formatter for formatting decimal numbers.
double d = 2.3423424666767E13;
DecimalFormat decimalFormat = new DecimalFormat("#");
System.out.println(decimalFormat.format(d));
Output : 23423424666767
The problem is, your value cannot be stored accurately in single precision floating point type. Proof:
public class test{
public static void main(String[] args){
Float a = Float.valueOf("23423424666767");
System.out.printf("%f\n", a); //23423424135168,000000
System.out.println(a); //2.34234241E13
}
}
Another thing is: you don't get "2.3423424666767E13", it's just the visual representation of the number stored in memory. "How you print out" and "what is in memory" are two distinct things. Example above shows you how to print the number as float, which avoids scientific notation you were getting.
First of all, the fact that the value in the database is a float does not mean that it also fits in a Java float. Float is short for floating point, and floating point types of various precisions exist. Java types float and double are both floating point types of different precision. In a database both are called FLOAT. Since double has a higher precision than float, it probably is a better idea not to cast your value to a float, because you might lose precision.
You might also use BigDecimal, which represent an arbitrary-precision number.
Use dataType casting. For example:
// converting from double to float:
double someValue;
// cast someValue to float!
float newValue = (float)someValue;
Cheers!
Note:
Integers are whole numbers, e.g. 10, 400, or -5.
Floating point numbers (floats) have decimal points and decimal places, for example 12.5, and 56.7786543.
Doubles are a specific type of floating point number that have greater precision than standard floating point numbers (meaning that they are accurate to a greater number of decimal places).
Float.parseFloat(String.valueOf(your_number)
I am writing a function round:
static float round(float number, precision){}
The function should work like this: round(12.3456f, 3) = 12.345
My definition of function is like this:
public static float round(float value, int precision) {
float result;
if(precision <= 0){
throw new RuntimeException("Precision can not be zero or less");
}
int number = (int) power(10,precision);
value = value * number;
result = (float)Math.round(value)/number;
return result;
}
But the issue is that, my unit test case for this function doesn't pass,
public void mathTestNew() {
assertEquals("MathTest",12.341,OOTBFunctions.round(12.3416f,3));
}
The result is
junit.framework.AssertionFailedError: MathTest expected:<12.341> but was:<12.342>
I am not sure how to overcome this error. I am not sure if BigDecimal will help me in this.
Rounding normally occurs towards the nearest integer. So 12.3416 is correctly rounded to 12.342
If you want the rounding behaviour you seem to be asking for (where the number is rounded down towards negative infinity) then you should use Math.floor(x) instead of Math.round(x)
Also be careful with rounding floats / doubles as they both suffer from numerical inaccuracy. If you really want high accuracy on decimal places, you may be better using BigDecimal instead.
Math.round is "round-to-nearest". You probably want Math.floor.
If you did want to use BigDecimal:
public static float round(float value, int precision) {
if (precision <= 0) {
throw new IllegalArgumentException("Precision cannot be zero or less.");
}
BigDecimal decimal = BigDecimal.valueOf(value);
return decimal.setScale(precision, RoundingMode.FLOOR).floatValue();
}
You may lose accuracy when converting from BigDecimal to float, so if accuracy is a must, do not convert; keep the value as a BigDecimal.
As mentioned in other answers, float is an approximation of a base 10 number. The following demonstrates just that:
System.out.println(BigDecimal.valueOf(12.3416f)); // outputs 12.34160041809082
System.out.println(new BigDecimal("12.3416")); // outputs 12.3416
12.3416 rounded is 12.342. There in lies your problem. You probably want Math.Floor instead. I would recomend against constantly multiplying as that can ruin the number. However, by 10 does not lower precision.
You can't write such a method. Floating point doesn't have decimal places, it has binary places. Ergo you cannot round to a specified number of decimal places. If you want decimmal places you must use a decimal radix, i.e. BigDecimal, or DecimalFormat. Apart from the error in expectation noted by #ColeJohnson, code like you have written will fail in over 90% of cases.
In Java, I want to convert a double to an integer, I know if you do this:
double x = 1.5;
int y = (int)x;
you get y=1. If you do this:
int y = (int)Math.round(x);
You'll likely get 2. However, I am wondering: since double representations of integers sometimes look like 1.9999999998 or something, is there a possibility that casting a double created via Math.round() will still result in a truncated down number, rather than the rounded number we are looking for (i.e.: 1 instead of 2 in the code as represented) ?
(and yes, I do mean it as such: Is there any value for x, where y will show a result that is a truncated rather than a rounded representation of x?)
If so: Is there a better way to make a double into a rounded int without running the risk of truncation?
Figured something: Math.round(x) returns a long, not a double. Hence: it is impossible for Math.round() to return a number looking like 3.9999998. Therefore, int(Math.round()) will never need to truncate anything and will always work.
is there a possibility that casting a double created via Math.round() will still result in a truncated down number
No, round() will always round your double to the correct value, and then, it will be cast to an long which will truncate any decimal places. But after rounding, there will not be any fractional parts remaining.
Here are the docs from Math.round(double):
Returns the closest long to the argument. The result is rounded to an integer by adding 1/2, taking the floor of the result, and casting the result to type long. In other words, the result is equal to the value of the expression:
(long)Math.floor(a + 0.5d)
For the datatype Double to int, you can use the following:
Double double = 5.00;
int integer = double.intValue();
Double perValue = 96.57;
int roundVal= (int) Math.round(perValue);
Solved my purpose.
I wanted to see if anyone can explain why the following code works with valueOf but not others.
import java.math.BigDecimal;
public class Change {
public static void main(String args[]) {
double a = 4.00d;
double b = 3.10d;
BigDecimal a1 = new BigDecimal(a);
BigDecimal b1 = new BigDecimal(b);
BigDecimal diff = a1.subtract(b1);
System.out.println("Double difference");
System.out.println(diff);
float c = 4.00f;
float d = 3.10f;
BigDecimal a2 = new BigDecimal(c);
BigDecimal b2 = new BigDecimal(d);
BigDecimal diff2 = a2.subtract(b2);
System.out.println("Float difference");
System.out.println(diff2);
System.out.println("Valueof Difference");
System.out.println(BigDecimal.valueOf(4.00).subtract(BigDecimal.valueOf(3.10)));
}
}
The output looks like:
>java Change
Double difference
0.899999999999999911182158029987476766109466552734375
Float difference
0.900000095367431640625
Valueof Difference
0.9
My question is: What does valueOf() do to get the precision?
Is there any other way of getting the correct result without rounding off to the 2 digits manually?
thanks,
Looking at the source code for BigDecimal, it does:
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));
}
From its JavaDoc:
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).
Because of floating-point representation, a double value is not exactly what you set it as. However, during String representation, it rounds off what it displays. (All of the rules are on it's JavaDoc).
Furthermore, because of this rounding, if you did:
BigDecimal d = BigDecimal.valueOf(4.00000000000000000000000000000000001));
you would get the wrong value. (d == 4.0)
So, it's pretty much always better to initialize these with strings.
BigDecimal.valueOf(double) first does a conversion from double to String, then String to BigDecimal.
In the first case, you're starting with a double or float, converting to BigDecimal, calculating the difference. In the second case, you're starting with double or float, converting to a String, then converting to BigDecimal, then calculating the difference.
From the Javadocs:
public static BigDecimal valueOf(double val)
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).
I think this answers both of your questions.
Cheers,
The valueOf works because it calls Double.toString. from the Javadoc:
public static BigDecimal valueOf(double val)
Translates a double into a BigDecimal, using the double's
canonical string representation
provided by the
Double.toString(double) method.
When you pass a double into the BigDecimal constructor, the constructor takes the floating-point value and reproduces it exactly. The toString code finds an approximation for the floating point value.
In case you didn't notice, using System.out.println() to show a floating point number doesn't show the same results as if you wrap the floating point number in a BigDecimal first (using the BigDecimal constructor that takes a double).