37

I need to do some floating point arithmetic in Java as shown in the code below:

public class TestMain {
    private static Map<Integer, Double> ccc = new HashMap<Integer, Double>() {
      { put(1, 0.01); put(2, 0.02); put(3, 0.05); put(4, 0.1); put(6, 0.2);
        put(10, 0.5); put(20, 1.0); put(30, 2.0); put(50, 5.0); put(100, 10.0);
      }
    };

    Double increment(Double i, boolean up) {
        Double inc = null;

        while (inc == null) {
            inc = ccc.get(i.intValue());

            if (up)
                --i;
            else
                ++i;
        }
        return inc;
    }

    public static void main(String[] args) {
        TestMain tt = new TestMain();

        for (double i = 1; i < 1000; i += tt.increment(i, true)) {
            System.out.print(i + ",");
        }
    }
}

This is to simulate the range of values given as output by the Betfair spinner widget.

Floating point arithmetic in Java seems to introduce some unexpected errors. For example, I get 2.180000000000001 instead of 2.18. What use are floating point numbers is you can't trust the results of arithmetic performed on them? How can I get around this issue?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 45
    Welcome to computer science. :) – BobbyShaftoe Nov 02 '09 at 13:25
  • 1
    See this question, which while phrased differently, come around to the same answer. http://stackoverflow.com/questions/1088216/whats-wrong-with-using-to-compare-floats-in-java – Yishai Nov 02 '09 at 14:09
  • The question can be reformulated as: *Inexact arithmetic not producing exact values.* You bet! – Ingo Feb 02 '13 at 12:12
  • not to be unfair, but programmers should read a bit about type representation, as in every serious Computer Science Course. in My university the first exam is about manually compute IEEE numbers... :) – ingconti Aug 16 '17 at 10:10

7 Answers7

38

If you need exact decimal values, you should use java.math.BigDecimal. Then read "What Every Computer Scientist Should Know About Floating-Point Arithmetic" for the background of why you're getting those results.

(I have a .NET-centric article which you may find easier to read - and certainly shorter. The differences between Java and .NET are mostly irrelevant for the purposes of understanding this issue.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 6
    Keep in mind, `BigDecimal` is going to be a lot slower than plain floating-point arithmetic. – Anthony Mills Nov 02 '09 at 13:27
  • 24
    @Anthony: True, but if you actually *need* accurate decimal values, then slow and right is better than fast and wrong. – Jon Skeet Nov 02 '09 at 13:47
  • 4
    You can also use integers for cents in this case. You'll have to remember that 218 means 2.18 and you need to do some extra work for printing. – starblue Nov 02 '09 at 14:35
  • 4
    I love math, and even I can't finish that article. I wish people would stop posting it - it is not a good reference, unless you are looking to read a small book just to understand how floating-point numbers work. – BlueRaja - Danny Pflughoeft Apr 08 '11 at 19:52
  • One grain of salt, though: BigDecimal is no more exact in the general case than anything else. For example, try 1/3. The point is, that you can't represent infinite many fractions in finite storage, no matter what the base of your positional system might be. Note that number systems to the base 60 can represent many fractions, like 1/3, 1/4, 1/5, 1/10, 1/20. If you also need n/7 fractions, use base 420. – Ingo Feb 02 '13 at 12:18
  • @Ingo: That's why I specified "exact decimal values" :) – Jon Skeet Feb 02 '13 at 12:24
  • If the goal is to be able to represent any possible number exactly, than this cannot be done with any finite amount of memory. But if the goal is to be able to represent any decimal number that the user can type in exactly, and be able to perform calculations on decimal numbers and give the user the exact same answer that he would get if he did the calculation on paper (and made no mistakes), then BigDecimal solves the problem. – Jay Mar 07 '13 at 22:23
  • 1
    http://floating-point-gui.de/ has a somewhat simpler and more practical approach to explaining the problem(s). – Joachim Sauer Mar 20 '13 at 13:21
  • updated link: [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – Tintin May 07 '22 at 00:28
11

Floating point numbers use binary fractions and not decimal fractions. That is, you're used to decimal fractions made up of a tenths digit, a hundredths digit, a thousandths digit, etc. d1/10 + d2/100 + d3/1000 ... But floating point numbers are in binary, so they have a half digit, a quarter digit, an eighth digit, etc. d1/2 + d2/4 + d3/8 ...

Many decimal fractions cannot be expressed exactly in any finite number of binary digits. For example, 1/2 is no problem: in decimal it's .5, in binary it's .1. 3/4 is decimal .75, binary .11. But 1/10 is a clean .1 in decimal, but in binary it's .0001100110011... with the "0011" repeating forever. As the computer can store only a finite number of digits, at some point this has to get chopped off, so the answer is not precise. When we convert back to decimal on output, we get a strange-looking number.

As Jon Skeet says, if you need exact decimal fractions, use BigDecimal. If performance is an issue, you could roll your own decimal fractions. Like, if you know you always want exactly 3 decimal places and that the numbers will not be more than a million or so, you could simply use int's with an assumed 3 decimal places, making adjustments as necessary when you do arithmetic and writing an output format function to insert the decimal point in the right place. But 99% of the time performance isn't a big enough issue to be worth the trouble.

Jay
  • 26,876
  • 10
  • 61
  • 112
3

Floating-point numbers are imprecise, especially since they work in binary fractions (1/2, 1/4, 1/8, 1/16, 1/32, ...) instead of decimal fractions (1/10, 1/100, 1/1000, ...). Just define what you feel is "close enough" and use something like Math.abs(a-b) < 0.000001.

Anthony Mills
  • 8,676
  • 4
  • 32
  • 51
3

On a philosophical note, I wonder: Most computer CPUs today have built-in support for integer arithmetic and floating-point arithmetic, but no support for decimal arithmetic. Why not? I haven't written an application in years where floats were useable because of this rounding problem. You certainly can't use them for money amounts: No one wants to print a price on a sales receipt of "$42.3200003". No accountant is going to accept "we might be off by a penny here and there because we're using binary fractions and had rounding errors".

Floats are fine for measurements, like distance or temperature, where there's no such thing as an "exact answer" and you have to round off to the precision of your instruments at some point anyway. I suppose for people who are programming the computer in the chemistry lab, floats are used routinely. But for those of us in the business world, they're pretty much useless.

Back in those ancient days when I programmed on mainframes, the IBM 360 family of CPUs had built-in support for packed decimal arithmetic. They stored strings where each byte held two decimal digits, i.e. the first four bits had values from 0 to 9 and ditto the second four bits, and the CPU had arithmetic functions to manipulate them. Why can't Intel do something like that? Then Java could add a "decimal" data type and we wouldn't need all the extra junk.

I'm not saying to abolish floats, of course. Just add decimals.

Oh well, as great social movements go, I don't suppose this is one that is going to generate a lot of popular excitement or rioting in the streets.

Jay
  • 26,876
  • 10
  • 61
  • 112
  • Short answer, that would be a waste of silicon. A chip only has room for so-many millions of transistors. The trick is to get the most utility out of each one of them. If you dedicate some fraction of the chip to making decimal fractions go faster, only certain, specialized applications will benefit. If you dedicate the same fraction of the chip to more cache, or more pipelining, or more cores, or more of whatever, then _all_ applications will benefit---even the ones that do decimal fractions in software. – Solomon Slow May 06 '14 at 17:51
  • @jameslarge There's truth in that. But "certain specialized applications" would be any application that deals with fractions where rounding errors are unacceptable, such as apps that deal with money, which I think is a pretty substantial percentage of all the apps written. I suspect more apps could use a decimal data type than can use a float, and yet CPUs have built-in support for floats. Hey, they've got room in the instruction set for FLDLN2 -- put the natural log of 2 on the top of the stack. How often do you need that? ... – Jay May 06 '14 at 19:05
  • ... In any case, I wasn't so much thinking of speed as of clean coding. I'd be happy if the JVM would include a decimal data type like VB and C# do, however close to or far away from the bare metal that is done. – Jay May 06 '14 at 19:06
  • 1
    FWIW, modern decimal support wouldn't use BCD, for speed reasons. Now that 64-bit and even 128-bit registers are common, it would probably be done with Decimal Floating Point (DFP) in fixed-width types, where the whole value fits in a register. IEEE-754 includes DFP now, there's an Intel Decimal Floating Point Math library, and IBM's POWER6 has hardware support for it. (And fixed-point decimal can be implemented efficiently on top of existing ints.) Since AMD and Intel are mass-market, they probably don't see this as a marketable feature compared to the stuff james listed. – Andrew Janke Aug 01 '14 at 06:27
  • @AndrewJanke I didn't mean to say that BCD strings were the best or only way to implement it, just to note that this was *A* way to do it that existed on older CPUs. It may be that implementing decimal in software on top of integers and then using space on the chip for improved caching or whatever would give net better results than using chip space to implement decimal arithmetic. I don't claim to know enough about CPU design to discuss that intelligently. I'd just like to be able to write programs that use decimals as simply and naturally as they use ints and floats, however you get me there! – Jay Aug 01 '14 at 14:07
  • Not trying to start an argument; just thought you'd be interested in what the current decimal support looks like, and to note there's been some progress in that direction, so your dream isn't entirely dead. I wish Java had `decimal` primitives, too (or at least operator overloading and non-reference object types so you could build your own); I'd use them all the time, and hardware DFP support would be a big win for me. (In C++ it matters less: you can roll your own decimal type on top of the Intel DFP library right now and it'll look about as natural as a `float` from the user's perspective.) – Andrew Janke Aug 01 '14 at 18:16
  • @AndrewJanke And I was so looking forward to having an argument. But yeah, in C++ with operator overloading you could make your own. Does C# have operator overloading? I've only played with that briefly. Similar things could be said about other languages. – Jay Aug 01 '14 at 20:24
  • @Jay: Yup, C# has operator overloading. But for Java, while op overloading would be great, I think another big barrier is that you can't define new non-reference types in Java, so there's no way to make efficient large arrays of non-primitive types. It's always following a pointer or two to objects on the heap, which sucks for big numeric problems. In C++ you can define objects with non-pointer fields and make arrays of the objects, not just references to them. – Andrew Janke Aug 01 '14 at 23:17
  • @Jay Intel *has* done something like that. The FPU supports a reversed BCD encoding in 80 bits. But it doesn't show through in any languages I am aware of. I used these instructions in a financial application in 1995-7. – user207421 Jan 27 '15 at 03:36
  • 1
    @Jay Just a side note, C# has this functionality built in in the form of the Decimal type. You get the usual problems with Double and Float, but Decimal does not have rounding errors. Why does every language not have something like Decimal? That I couldnt say. – kingfrito_5005 Jan 16 '17 at 20:45
  • Because you can just count in pennies, and display them with a point in the right place? – Will Crawford Mar 25 '20 at 13:16
1

You can make the output of your program look more like you expect by using formatted output.

http://java.sun.com/javase/6/docs/api/java/util/Formatter.html

Obviously the underlying floating point arithmetic still works the same, but at least the output will be more readable.

For example, to round your results to two decimal places:

System.out.print(String.format(".2f", i) + ","); 
hallidave
  • 9,579
  • 6
  • 31
  • 27
0

You can write some code to compute Epsilon on your machine. I believe std:: C++ defines it, its defined in other ways depending on what you are using.

private static float calcEpsilonFloat() {
    float epsi = 1.0f;


    while ((float) (1.0 + (epsi / 2.0)) != 1.0)
    {
       epsi /= 2.0f;
    }

    return epsi;
}

The only time I worried about Epsilon was when I was comparing signals for thresholding. Even then I'm not sure I really needed to worry about it, in my experience if you are concerned about Epsilon you may have some other consideration to deal with first.

don_q
  • 415
  • 3
  • 11
-1

By the way, you can try to use this function to be sure (for not too much number of decimal digits) that your number will be reformatted to keep only the decimals you need.

http://pastebin.com/CACER0xK

n is you number with lots of decimals (e.g. Math.PI), numberOfDecimals is the maximum number of decimals you need (e.g. 2 for 3.14 or 3 for 3.151).

By theory, putting a negative value to numberOfDecmals, it will cut off the lower integer digits of the number too. E.g putting n=1588.22 and numberOfDecimals=-2, the function will return 1500.0.

Let me know if it is something wrong.

cr0
  • 617
  • 5
  • 17
acauan
  • 1
  • [Doesn't work](http://stackoverflow.com/questions/153724/how-to-round-a-number-to-n-decimal-places-in-java/12684082#12684082), and the code should have been posted here. – user207421 Jan 27 '15 at 03:38