5
votes

Here's what I'm looking at:

float p=1.15f;
BigDecimal bdp=new BigDecimal(p);
float q=1.1499999f;
float r=1.14999999f;

System.out.println(p);   //1.15
System.out.println(bdp); //1.14999997615814208984375
System.out.println(q);   //1.1499999
System.out.println(r);   //1.15

So I understand that the decimal value of "p" 1.15 can't be represented exactly in binary.
And so the large big decimal "bdp" output makes perfect sense to me ... that's the actual value of the float.

Question 1
When the float "p" gets converted back to a string for output (as 1.15), how/where does that rounding occur (from the internal 1.149..375 value to 1.15)?

And where is it specified in the documentation? The toString javadoc doesn't really help (me at least).

I do see this in the language spec:

The elements of the types float and double are those values that can be represented using the IEEE 754 32-bit single-precision and 64-bit double-precision binary floating-point formats, respectively.

Wikipedia's IEEE 754 article gives this:

This gives from 6 to 9 significant decimal digits precision (if a decimal string with at most 6 significant decimal is converted to IEEE 754 single precision and then converted back to the same number of significant decimal, then the final string should match the original;

Question 2
So it seems that this is just how Java/IEEE 754 floats are supposed to work?

I get guaranteed accuracy of float/string conversion/representation up to a certain number of digits (like for "p" and "q"), and if that number of digits is exceeded Java will do some rounding for display (like for "r")?

Thanks for help.

3
Unless you are performing Science! stop using floats.DwB
Correct. We're using BigDecimals in our code. But we're getting doubles from another system and I need to understand these details so I can make sure I'm handling the data I'm getting as accurately as possible. Thanksmatt1616
If you are interested in this sort of question, consider using System.out.println(new BigDecimal(someFloat)); to get the exact decimal representation of someFloat.Patricia Shanahan

3 Answers

2
votes

From the JLS, 4.2.4. Floating-Point Operations:

The Java programming language requires that floating-point arithmetic behave as if every floating-point operator rounded its floating-point result to the result precision. Inexact results must be rounded to the representable value nearest to the infinitely precise result; if the two nearest representable values are equally near, the one with its least significant bit zero is chosen. This is the IEEE 754 standard's default rounding mode known as round to nearest.

The Java programming language uses round toward zero when converting a floating value to an integer (§5.1.3), which acts, in this case, as though the number were truncated, discarding the mantissa bits. Rounding toward zero chooses at its result the format's value closest to and no greater in magnitude than the infinitely precise result.

6
votes

I think this is the relevant part of the Javadoc that describes the behavior you're looking at (from the static String toString(float) method):

How many digits must be printed for the fractional part of m or a? 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.

To paraphrase: the toString methods for floating-point types will generally produce the shortest decimal representation that can unabmiguously identify the true value of the floating-point number.

Example program to illustrate:

import java.math.BigDecimal;

public class FloatTest {

  static void show(float f) {
    BigDecimal f_exact = new BigDecimal(f);
    System.out.println("---");
    System.out.println("String value: " + f);
    System.out.println("Exact value:  " + f_exact);
    System.out.println("Delta:        " + 
        new BigDecimal("1.15").subtract(f_exact));
  }

  public static void main(String[] args) {
    show(1.15f);
    show(Math.nextUp(1.15f));
  }
}

Output:

---
String value: 1.15
Exact value:  1.14999997615814208984375
Delta:        2.384185791015625E-8
---
String value: 1.1500001
Exact value:  1.150000095367431640625
Delta:        -9.5367431640625E-8
4
votes

Thanks mttdbrd ... that helps. From your first paragraph then I think that the answer to my question is: yes, Java does this rounding internally in accordance with the IEEE spec.

Here's the output of a program that let's me see a little bit of that rounding in action:

-------------------------------------------
string            1.49999
bigdec of float   1.499989986419677734375
float back to str 1.49999
-------------------------------------------
string            1.4999991
bigdec of float   1.49999904632568359375
float back to str 1.499999
-------------------------------------------
string            1.4999992
bigdec of float   1.49999916553497314453125
float back to str 1.4999992
-------------------------------------------
string            1.4999993
bigdec of float   1.4999992847442626953125
float back to str 1.4999993
-------------------------------------------
string            1.4999994
bigdec of float   1.49999940395355224609375
float back to str 1.4999994
-------------------------------------------
string            1.4999995
bigdec of float   1.499999523162841796875
float back to str 1.4999995
-------------------------------------------
string            1.4999996
bigdec of float   1.49999964237213134765625
float back to str 1.4999996
-------------------------------------------
string            1.4999997
bigdec of float   1.49999964237213134765625
float back to str 1.4999996
-------------------------------------------
string            1.4999998
bigdec of float   1.4999997615814208984375
float back to str 1.4999998
-------------------------------------------
string            1.4999999
bigdec of float   1.49999988079071044921875
float back to str 1.4999999
-------------------------------------------
string            1.15
bigdec of float   1.14999997615814208984375
float back to str 1.15
-------------------------------------------
string            1.49999964237213134765626
bigdec of float   1.49999964237213134765625
float back to str 1.4999996

And here's the program if anyone wants it:

public static void floatAccuracy3()
{
    printFloatAndBigDec("1.49999");
    for (int i = 1; i < 10; i++) {
        String s="1.499999";
        s+=i;
        printFloatAndBigDec(s);         
    }
    printFloatAndBigDec("1.15");
    printFloatAndBigDec("1.49999964237213134765626");
}

public static void printFloatAndBigDec(String s)
{
    Float f=new Float(s);
    BigDecimal bdf=new BigDecimal(f);
    System.out.println("-------------------------------------------");
    System.out.println("string            "+s);
    System.out.println("bigdec of float   "+bdf);
    System.out.println("float back to str "+f);
}

And some other links in case they're helpful to anyone else researching this stuff: