0

If I do something like

final float third = 1f / 3f;
System.out.println((third + third + third) == 1.0f);

I get true. Does that mean float can exactly represent 1/3?

Steve11235
  • 2,849
  • 1
  • 17
  • 18
  • A `float` can represent 1/2 exactly, as `0.5f`. – rgettman Dec 10 '13 at 18:24
  • 1
    SOME fractions can be represented exactly. I think it's maybe limited to fractions where the divisor is a power of two, but IEEE float has a way of surprising you, so there may be others. – Hot Licks Dec 10 '13 at 18:25
  • 1
    You should read [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). – ajb Dec 10 '13 at 18:33
  • 1
    @HotLicks No, there aren't any others. If A/B is a fraction in reduced form, then it can be represented exactly if B is a power of 2 up through 2^126, and if abs(A) < 2^24. Thus, (2^24-1)/(2^43) can be represented exactly, but (2^24+1)/(2^43) cannot. (I'm ignoring integers, i.e. B=1. There are other fractions where B is a power of 2 >= 2^127, but it still has to be a power of 2.) – ajb Dec 10 '13 at 18:49

3 Answers3

0

I lost a bet a long time ago when I said "No!" Not only that, but the other party claimed "There is no CS reason that float cannot represent fractional values."

Here is a sample program that explores the issue a bit:

package test;
public class FloatTest
{
public static void main(String[] args)
{
    new FloatTest().run();
}
public void run()
{
    final float third = 1f / 3f;
    final float small = third * .01f;
    final float realSmall = third * .0001f;

    System.out.println("third: " + third);
    System.out.println("small: " + small);
    System.out.println("real small: " + realSmall);
    System.out.println("Three thirds: " + (third + third + third));
    System.out.println("Three small: " + (small + small + small));
    System.out.println("Three real small: " + (realSmall + realSmall + realSmall));
}
}

The output is

third: 0.33333334
small: 0.0033333334
real small: 3.3333334E-5
Three thirds: 1.0
Three small: 0.01
Three real small: 1.00000005E-4

The strange results have to do with a couple of things. First, float cannot exactly represent 1/3, any more than 1/3 can be written out with a finite number of decimal digits. In the old days, the result would be .99999999, due to rounding.

In modern times, IEEE 754 has specified how float should be represented and arithmetic handled. In particular, when doing arithmetic, an extra three bits are kept and rounding is performed. That's why the first two results come out exactly right and I lost my bet. However, these extra bits don't guarantee accuracy, and the third result shows.

Here's a decent description of float in general, and the section toward the bottom, "on Rounding" (sic), describes the extra bits: http://pages.cs.wisc.edu/~markhill/cs354/Fall2008/notes/flpt.apprec.html

Bottom line, if you want exact fractional values and to control rounding, use BigDecimal.

Steve11235
  • 2,849
  • 1
  • 17
  • 18
  • Why is this answer downvoted? Sounds like a nice one. – Joao Guilherme Dec 10 '13 at 18:24
  • Of course, `BigDecimal` won't represent 1/3 exactly any more than a `float` would, although you could get more precision if you wanted to. If you really want to represent rational numbers exactly, see http://stackoverflow.com/questions/5442640/is-there-a-commonly-used-rational-numbers-library-in-java. – ajb Dec 10 '13 at 23:30
0

A float cannot exactly represent every fraction because the float data type is a single-precision 32-bit IEEE 754 floating point and is subject to IEEE rounding rules. Therefore any thing requiring greater then 32 bits of precision is not representable by a float. There's also the double which is a double-precision 64-bit IEEE 754 floating point number. Finally, Java has the arbitrary precision BigDecimal class. However it cannot represent every fraction perfectly either, consider the Golden ratio; BigDecimal will throw an exception if you try to calculate

Golden ratio

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • Are you suggesting that `BigDecimal` *could* represent 1/3 perfectly? I kind of don't think so. Also, can you show us some code that demonstrates `BigDecimal` throwing an exception when you try to calculate the golden ratio? – ajb Dec 10 '13 at 23:36
  • No. It can't. That code isn't where I thought it was; but I was recall I was using Newton's method. – Elliott Frisch Dec 11 '13 at 00:34
0

float cannot represent exactly 1/3. If A/B is a fraction in reduced notation, with A and B both integers, A > 0, and B > 1, the only fractions float can represent are those where B is a power of 2 up to 2126, and A is less than 224; or other cases where B is a power of 2 greater than 2126, but I won't go into exactly what those are. (I'm not considering B=1; it would take extra work to describe what integers float is capable of representing, and it's not relevant here.)

Thus, float cannot represent 1/3. If you compute f=1/3, the fraction represented by the float is 11184811/33554432 (33554432 = 225). If you add f+f, the result is a float that represents the fraction 11184811/16777216 (16777216 = 224). If you then add this to f, the exact resulting fraction would be 33554433/33554432. But this fraction cannot be represented exactly in a float, since it breaks the rule in the first paragraph (33554433 > 224). Thus, the result must be rounded to something that can be represented in a float, and that something will be 1.

Try this:

final float third = 1f / 3f;
System.out.println(((double)third + (double)third + (double)third) == 1.0);

If third represented exactly 1/3, then surely casting it to a double would also represent exactly 1/3, since a double has more precision than a float, right? But this displays false. In fact, the double on the left side is 1.0000000298023224 if you display it. That is, 33554433/33554432.

ajb
  • 31,309
  • 3
  • 58
  • 84