121

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?

plinth
  • 48,267
  • 11
  • 78
  • 120
Steve Armstrong
  • 5,252
  • 7
  • 32
  • 43

11 Answers11

142

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) suppose 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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
51

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
Jimmy Kane
  • 16,223
  • 11
  • 86
  • 117
Brecht Yperman
  • 1,209
  • 13
  • 27
27

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.

erickson
  • 265,237
  • 58
  • 395
  • 493
17

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.

Andrew
  • 187
  • 1
  • 3
  • 1
    Worked for me. Is also very fast. Not sure why this is not marked as an answer. – Sameer May 10 '13 at 12:01
  • This is the method I use, and u don't need the new Float part... Just create the FloatingDecimal with the primitive. It will be automatically boxed... And works fast too... There is on;y one drawback, FloatingDecimal is immutable, so u need to create one for every single float... imagine a computational code of O(1e10)!!! Of course BigDecimal has the same drawback... – Mostafa Zeinali Aug 25 '14 at 04:38
  • 10
    The drawback of this solution is that sun.misc.FloatingDecimal is the internal class of JVM and the signature of its constructor has been changed in Java 1.8. Noone should use internal classes in real life application. – Igor Bljahhin Dec 29 '15 at 11:43
12

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();
}
GSD.Aaz
  • 178
  • 2
  • 6
7

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.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Lets say you have 10m float trade prices in your cache, do you still consider using BigDecimal and instantiate unique lets say ~6m objects ( to discount flyweight/immutable).. or there is more to it? – Sendi_t May 27 '15 at 17:06
  • 1
    @Sendi_t Please don't use comments to ask complex questions :-) – Aaron Digulla May 27 '15 at 19:25
  • Thanks for looking!.. we use floats right now as that was agreed a decade ago --I was trying to see your point of view on this situation on follow up -- . thanks! – Sendi_t May 27 '15 at 19:33
  • @Sendi_t Misunderstanding. I can't answer your question in 512 characters. Please ask a proper question and send me a link. – Aaron Digulla May 28 '15 at 07:20
  • @Sendi_t Long after you asked, I think the solution is to store prices in pence/cents as an int for fast, precise calculations. (Obviously if you use non-integer numbers of pence/cents then adjust to the degree of precision needed.) – GKFX Aug 16 '16 at 12:03
  • @GKFX That's exactly what `BigDecimal` does. It's a `BigInteger` plus the position of the decimal point. – Aaron Digulla Aug 16 '16 at 13:03
  • @AaronDigulla I thought the issue was with the performance/memory hit associated with creating (e.g.) 6 million BigDecimals. If you know in advance that the decimal point only ever goes at position 2, you can (probably) make things faster with an int[] array. – GKFX Aug 16 '16 at 13:08
  • 1
    @GKFX There is no "one size fits all" when it comes to handling decimals on computers. But since most people don't get it, I point them to `BigDecimal` since that will catch most of the common errors. If things are too slow, they need to learn more and find ways to optimize their problems. Premature optimization is the root of all evil - D.E. Knuth. – Aaron Digulla Aug 18 '16 at 14:56
2

A simple solution that works well, is to parse the double from the string representation of the float:

double val = Double.valueOf(String.valueOf(yourFloat));

Not super efficient, but it works!

Alessandro Roaro
  • 4,665
  • 6
  • 29
  • 48
1

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?

NotMe
  • 87,343
  • 27
  • 171
  • 245
  • 1
    Decimal arithmetic is inexact too. (e.g. 1 / 3 * 3 == 0.9999999999999999999999999999) It is, of course, better for representing exact **decimal** quantities like money, but for physical measurements it has no advantage. – dan04 Aug 25 '10 at 14:39
  • 2
    But 1 == 0.9999999999999999999999999999 :) – Emmanuel Bourg Jul 04 '12 at 21:46
0

Does this work?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;
Zizouz212
  • 4,908
  • 5
  • 42
  • 66
AesmaDiv
  • 9
  • 1
0

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.

mR_fr0g
  • 8,462
  • 7
  • 39
  • 54
-1

There is a way to convert Float value into Double without adding the extra precision

Float aFloat= new Float(0.11);
String s = aFloat.toString();
Double aDouble = Double.parseDouble(s);

This Approach will not add an extra precisions to your Float value while converting. The only Problem with this approach is memory usage of the JVM by creating an extra tamp String object.

When calling an toString() (aDouble.toString()) on Double will never add an extra precisions. The precisions will be added while type conversion.

clog02
  • 1