0

I'm consuming a REST service and I get a value like "0.15" . I need to store it a float variable so, for the conversion, I use:

Float.parseFloat(value); //value = "0.15"

However, the returned values is not a strict 0.15 float but instead I get 0.150000005960464 . Any idea why I'm getting this?

If i receive the value without any decimals (10 for instance) this doesn't happen.

Miguel Ribeiro
  • 8,057
  • 20
  • 51
  • 74
  • This is because a float doesn't have the necessary precision to store 0.15 exactly. If you can, use a `double` instead, although `double` will _also_ choke on some values... – fge Mar 14 '14 at 21:04
  • Bah... skip out on `float` and `double` and use `BigDecimal` – buzzsawddog Mar 14 '14 at 21:06
  • Is there any documentation around that explains why this happens? – Miguel Ribeiro Mar 14 '14 at 21:07
  • 1
    possible duplicate of [Strange floating-point behaviour in a Java program](http://stackoverflow.com/questions/327544/strange-floating-point-behaviour-in-a-java-program) – MarsAtomic Mar 14 '14 at 21:07
  • 2
    Read [this](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). Just so you know, what you're seeing isn't specific to Java. You'll find it in every language. I just ran into the issue myself with Ruby when I used a float instead of BigDecimal. – MarsAtomic Mar 14 '14 at 21:08
  • @MiguelRibeiro search Google for "IEEE 754" – fge Mar 14 '14 at 21:09
  • Double seems to be doing the trick. I get the value exactly as "0.15". Guess I'll read the docs and try to understand this a little better :) – Miguel Ribeiro Mar 14 '14 at 21:12
  • Don't use `double` or `Double` unless you really need to do floating point arrhythmic... And it does not sound like you really are if our worried about the 0.150000005960464 – buzzsawddog Mar 14 '14 at 21:15
  • Miguel you really need to read up on floating point if it is important to represent 0.15 exactly and you think a double does that. – Sean Owen Mar 14 '14 at 22:31
  • @buzzsawddog my problem is that exactly. I need to perform comparisons. For instance, (value <= 0.15) . If I get that big 0.150000005960464 as the "value" the condition returns false and should return true – Miguel Ribeiro Mar 14 '14 at 23:16

4 Answers4

0

If you really require parsing the value "0.15" as something that exactly represents the real value 0.15, then you need to use BigDecimal (http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html):

BigDecimal parsed = new BigDecimal(value)

Your "10" is also not exactly 10, BTW.

Sean Owen
  • 66,182
  • 23
  • 141
  • 173
  • This can still lead to a problem... passing in a float or double passes in the same imprecision he is talking about. – buzzsawddog Mar 14 '14 at 21:10
  • 1
    @buzzsawddog - not if `value` is `String`. – SergeyB Mar 14 '14 at 21:12
  • 1
    Also it is important for the OP to know that `BigDecimal` is immutable :-) That bites people in the but from time to time... – buzzsawddog Mar 14 '14 at 21:14
  • In the OP value must be a String since it is given as an argument to parseFloat. – Sean Owen Mar 14 '14 at 22:30
  • Storing it in BigDecimal solves the problem in my Java code. Problem is that the final destination of the value is an SQLite database. I guess I have to pass it as String - that's not to problematic I guess, DB type is still REAL so internally I should be able to perform comparisons and sums with it :) – Miguel Ribeiro Mar 15 '14 at 00:02
  • This is what type NUMERIC is for but sqlite does not have it. REAL is like a double. It does not give exact representation. – Sean Owen Mar 15 '14 at 12:30
0

I don't know if you can do something like this in android too, but I find this solution looking around:

NumberFormat df = DecimalFormat.getInstance();
df.setMinimumFractionDigits(2);
df.setMaximumFractionDigits(2);

float b = Float.parseFloat( df.format(a));

Or maybe you can try to round the number

float b = Math.round(a * 100)/100.0f;
Pievis
  • 1,954
  • 1
  • 22
  • 42
0

This happens because Float numbers cannot be represented exactly on a system.

A Floating Point number is represented on 32bit Architecture using this format. http://en.wikipedia.org/wiki/Single-precision_floating-point_format

While on a 64bit architechture using this format. http://en.wikipedia.org/wiki/Double-precision_floating-point_format

Representation of the number in the above two format results in loss of precision. Thus a number such as 0.15 can come up weirdly as 0.149999999998.

When you convert 0.15 to it's binary representation it gives you a recurring number which is 0.0010011001100....

10 however has a binary representation which is not recurring hence can be exactly represented on the above two systems.

0.15 however has some it's bits removed so as to fit in the above architecture where the precision loss happens.

Check the exact mathematics on how to convert a floating point number to a binary which will better help you understand where the precision loss is happening. http://sandbox.mc.edu/~bennet/cs110/flt/dtof.html

0

Hi i resolved your problem...You will use like this You can get your need...Definitely it will give only the 0.15 format only use this...I hope you will get your desired Answer..

    String decim= "0.15456564564564";
    String convert = String.format("%.2f", Double.valueOf(decim));
    float v = Float.parseFloat(convert);
    System.out.println(v);
Naveen Kumar Kuppan
  • 1,424
  • 1
  • 10
  • 12