Computing @ 40's

Trying to overcome the obsolescence

Java float and double primitive types are evil. Don’t use them.

Today I am writing a short article to show people why I decided some time ago to not to use Java float or double primitive types in any case.

When you start programming Java, one of the firsts lessons is to show java primitive types like int, short, long, byte, etc… All these are used to store integers in different range of values available. For floating point numbers, exists two types: float and double types.

But usually people is not aware that when using float or doubles they are entering to a twilight zone that can bring them a lot of headaches.

To see the reason let’s look a simple example. Take this code and run it on Eclipse or Netbeans. It’s a simple Java class with a main procedure.

package com.booreg.tests;

public class ILoveYouMySillyFloatMain
{
    public static void testWithFloats()
    {
        System.out.println("testWithFloats\n");
        float a = 0.1f;
        float b = 0.1f;
        float c = 0.1f;

        float d = a + b + c;
        float e = d * 3;
        float f = d * 100000;

        System.out.println("a + b + c = d = " + d);
        System.out.println("e = " + e);
        System.out.println("f = " + f);
        System.out.println("");
    }

    public static  void testWithDoubles()
    {
        System.out.println("testWithDoubles\n");

        double a = 0.1;
        double b = 0.1;
        double c = 0.1;

        double d = a + b + c;
        double e = d * 3;
        double f = d * 100000;

        System.out.println("a + b + c = d = " + d);
        System.out.println("e = " + e);
        System.out.println("f = " + f);
        System.out.println("");
    }

    public static void main(String[] args)
    {
        testWithFloats();
        testWithDoubles();
    }
}

As you see is a really simple program doing three simple operations based on three variables with the same value: 0.1.  When running this example you would expect an output like this:

testWithFloats

a + b + c = d = 0.3
e = 0.9
f = 30000

testWithDoubles

a + b + c = d = 0.3
e = 0.9
f = 30000

So, what you expect is to have the same results for float or double tests, and results of 0.3, 0.9 and 30000.

But, when you run it you see a result like this !!!

testWithFloats

a + b + c = d = 0.3
e = 0.90000004
f = 30000.002

testWithDoubles

a + b + c = d = 0.30000000000000004
e = 0.9000000000000001

f = 30000.000000000004

Incredible ! Amazing ! The two tests return different values and almost none of them are correct !!!!

What happened here ? It’s me or my eyes ? Should I visit the doctor ? No, the problem comes because of how Java stores floating point values on memory. They use binary representation of that values, and this means that implicitly that values are loosing precission. That is. In binary format is not always possible to store real number values with total precission.

I wonder how it’s possible for Java designers to introduce a java type that implicitly can generate errors. Imagine our bank using Java programs filled with floats and doubles everywhere. Not really serious in my opinion.

So, is there any solution ?

Yes, of course, and is relatively simple, although makes the code not so clean as I would like. The solution is to not to use float or double but to use java class BigDecimal instead. Use always BigDecimal. Always. Don’t use any second to think if you really need them or not. Use it and all the operations will have exact results.

So, take the example above and wtite the BigDecimal version, like this.

package com.booreg.tests;

import java.math.BigDecimal;

public class ILoveYouMySillyFloatMain
{
    public static void testWithFloats()
    {
        System.out.println("testWithFloats\n");

        float a = 0.1f;
        float b = 0.1f;
        float c = 0.1f;

        float d = a + b + c;
        float e = d * 3;
        float f = d * 100000;

        System.out.println("a + b + c = d = " + d);
        System.out.println("e = " + e);
        System.out.println("f = " + f);
        System.out.println("");
    }

    public static void testWithDoubles()
    {
        System.out.println("testWithDoubles\n");

        double a = 0.1;
        double b = 0.1;
        double c = 0.1;

        double d = a + b + c;
        double e = d * 3;
        double f = d * 100000;

        System.out.println("a + b + c = d = " + d);
        System.out.println("e = " + e);
        System.out.println("f = " + f);
        System.out.println("");
    }

    public static  void testWithBigDecimals()
    {
        System.out.println("testWithBigDecimals\n");

        BigDecimal a = new BigDecimal("0.1");
        BigDecimal b = new BigDecimal("0.1");
        BigDecimal c = new BigDecimal("0.1");

        BigDecimal d = a.add(b).add(c);
        BigDecimal e = d.multiply(new BigDecimal("3"));
        BigDecimal f = d.multiply(new BigDecimal("100000"));

        System.out.println("a + b + c = d = " + d);
        System.out.println("e = " + e);
        System.out.println("f = " + f);
        System.out.println(");
    }

    public static void main(String[] args)
    {
        testWithFloats();
        testWithDoubles();
        testWithBigDecimals();
    }
}

Notice that BigDecimal variables are declared using strings values instead of a literal value. If you initialize the BigDecimal variable a with

 BigDecimal a = new BigDecimal(0.1); 

you will reproduce the floating-point problem again, because the compiler makes an implicit conversion of 0.1 value to a double value, loosing the precission that we want to keep.

Is a little bit harder to work with BigDecimal. For example you can initialize a zero value using this sentence: BigDecimal a = BigDecimal.ZERO; It’s not so polite that when using float or double, but you will not have surprises later.

Additional information

There are multiple Internet entries talking about this. Making a little search in Google, it’s easy to find more information. Probably you will find them before this little and modest blog entry. For example take a look at http://stackoverflow.com/tags/floating-point/info or at http://floating-point-gui.de/

They explain better some things about binary representations of floating point numbers.

But everything can be sumarized with only one idea: never, never, never use float or double java primitive types. Use BigDecimal instead.

Cardedeu, September 30th, 2013

7 thoughts on “Java float and double primitive types are evil. Don’t use them.

  1. Hmm as the Big Decimal is an Object, all operations will be slower on them. There are application areas such as a signal processing for example where many-thousands-items-arrays are repeatedly processed in real time. The performance impact there could be fatal. So in cases where the performance rules I would suggest using the primitives. In cases where the accuracy rules, use the BigDecimal.

    • Well, this is the main reasoning of people defending the use of double and float types. You are right that the performance is significantly slower with BigDecimal than with float or double types. But in case that your application doesn’t need it, I wouldn’t have any doubt of using BigDecimal. The title of this article was intentionally provocative to make people thinking about using that primitives types without worrying the lost of precision.

      Many thanks for your comment. I appreciate all of them.

  2. The Java floating point types double and float store their values in the scientific IEEE format. Exactly the same format common CPUs (Intel, AMD, etc.) use internally. It is optimized for performance in scientific calculations.
    However, most Java programmers write business applications. In the business area precision usually is most important, especially when it comes to price information etc.. So for business applications use the slow BigDecimal, for performance critical scientific calculations, use double and float. The difference in performance is big (just a guess: about factor ~10 up to 100), however in a business application this is not noticable as the calculations are not complex in that area.
    So you are right: in a business application, use BigDecimal. If performance is not an issue, use BigDecimal. Use double and float only if performance is critical (e.g. compression algorithms, 3D graphics etc.).

    • Hi Jens.

      I wanted to answer you before, sorry for this long delay but some circumstances have made me answer late.

      I agree with you. When I wrote this article I was thinking specially in bussiness applications, so that’s why the title was intentionally provocative, just to emphasize the devil side of float and double types in that kind of applications. Even with that, having in mind the power of current CPUs, I think that the question of performance should be very well explained to justify using that primitive types.

      Thanks for your comments!

  3. Pingback: Double or nothing » Espresso Programmer

  4. This post is unfortunately very wrong.

    The problem with floating point numbers with fixed precision (float, double) is that they do not represent real numbers, but only a very small subset of rational numbers. BigDecimal is different only in that it has arbitrary precision (arbitrary, but still finite), and thus it cannot represent all real numbers. BigDecimals is unable to currently count 1/3 + 1/3 + 1/3:

    @Test
    public void testBigDecimal() {
    final int div = 3;
    final int precision = 100;

    BigDecimal part = BigDecimal.ONE.divide(new BigDecimal(3), precision, BigDecimal.ROUND_HALF_DOWN);
    BigDecimal sum = BigDecimal.ZERO;
    for (int i = 0; i < div; ++i) {
    sum = sum.add(part);
    }

    assertEquals(sum, BigDecimal.ONE);
    }

    The result is:

    java.lang.AssertionError: expected [1] but found [0.9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999]
    Expected :1
    Actual :0.9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

    No matter what value of precision you pick, you will never have the correct value.

    My recommendation is: for mutliplication, addition and substraction, BigDecimal is precise (unlike float & double), but when it comes to division or other functions (roots, goniometric functions …), absolute precision is not achievable and you will have to do some rounding.

    • Hi Michal.

      Thanks for your comment. All of them are welcome, even if people don’t agree with what I was saying.

      As you see in this article I was doing simple operations, like addition, substraction and multiplication, and they gave me unexpected results. Of course both BigDecimal and primitive types will have problems when working with some divisions. But this post, and it’s title, was intentionally provocative, to make people think that many times they will find situations where the primitive types will not suite the expected functionality. Imagine if a bank makes 0.1 + 0.1 + 0.1 operations frequently. They will fall in bankrupt in a few months, or they will loose a lot of customers if they don’t care about it.

      Even making simple operations like converting a String to a decimal point number can cause a lot of problems. In general we think that operations with numbers are exactly precise for simple operations (although we know that this is not true for operations like the examples you gave), so, we expect exact result from exact operations. If we fall down in the trap of thinking that java float and double types are good for everything we will have a lot of problems in our programs.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s