可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a primitive float and I need as a primitive double. Simply casting the float to double gives me weird extra precision. For example:
float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375
However, if instead of casting, I output the float as a string, and parse the string as a double, I get what I want:
System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35
Is there a better way than to go to String and back?
回答1:
It\'s not that you\'re actually getting extra precision - it\'s that the float didn\'t accurately represent the number you were aiming for originally. The double is representing the original float accurately; toString
is showing the \"extra\" data which was already present.
For example (and these numbers aren\'t right, I\'m just making things up) support you had:
float f = 0.1F;
double d = f;
Then the value of f
might be exactly 0.100000234523. d
will have exactly the same value, but when you convert it to a string it will \"trust\" that it\'s accurate to a higher precision, so won\'t round off as early, and you\'ll see the \"extra digits\" which were already there, but hidden from you.
When you convert to a string and back, you\'re ending up with a double value which is closer to the string value than the original float was - but that\'s only good if you really believe that the string value is what you really wanted.
Are you sure that float/double are the appropriate types to use here instead of BigDecimal
? If you\'re trying to use numbers which have precise decimal values (e.g. money), then BigDecimal
is a more appropriate type IMO.
回答2:
I find converting to the binary representation easier to grasp this problem.
float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));
You can see the float is expanded to the double by adding 0s to the end, but that the double representation of 0.27 is \'more accurate\', hence the problem.
111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000
回答3:
This is due the contract of Float.toString(float)
, which says in part:
How many digits must be printed for
the fractional part […]? There
must be at least one digit to
represent the fractional part, and
beyond that as many, but only as many,
more digits as are needed to uniquely
distinguish the argument value from
adjacent values of type float. That
is, suppose that x is the exact
mathematical value represented by the
decimal representation produced by
this method for a finite nonzero
argument f. Then f must be the float
value nearest to x; or, if two float
values are equally close to x, then f
must be one of them and the least
significant bit of the significand of
f must be 0.
回答4:
I\'ve encountered this issue today and could not use refactor to BigDecimal, because the project is really huge. However I found solution using
Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()
And this works.
Note that calling result.doubleValue() returns 5623.22998046875
But calling doubleResult.doubleValue() returns correctly 5623.23
But I am not entirely sure if its a correct solution.
回答5:
Use a BigDecimal
instead of float
/double
. There are a lot of numbers which can\'t be represented as binary floating point (for example, 0.1
). So you either must always round the result to a known precision or use BigDecimal
.
See http://en.wikipedia.org/wiki/Floating_point for more information.
回答6:
I found the following solution:
public static Double getFloatAsDouble(Float fValue) {
return Double.valueOf(fValue.toString());
}
If you use float and double instead of Float and Double use the following:
public static double getFloatAsDouble(float value) {
return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}
回答7:
Floats, by nature, are imprecise and always have neat rounding \"issues\". If precision is important then you might consider refactoring your application to use Decimal or BigDecimal.
Yes, floats are computationally faster than decimals because of the on processor support. However, do you want fast or accurate?
回答8:
For information this comes under Item 48 - Avoid float and double when exact values are required, of Effective Java 2nd edition by Joshua Bloch. This book is jam packed with good stuff and definitely worth a look.
回答9:
Does this work?
float flt = 145.664454;
Double dbl = 0.0;
dbl += flt;
回答10:
If you need to do it with wrappers:
Double d = new Float(123f).doubleValue();