Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
541 views
in Technique[技术] by (71.8m points)

floating accuracy - How to overcome inaccuracy in Java

I came to know about the accuracy issues when I executed the following following program:

public static void main(String args[])
{
    double table[][] = new double[5][4];
    int i, j;
    for(i = 0, j = 0; i <= 90; i+= 15)
    {
        if(i == 15 || i == 75)
            continue;
        table[j][0] = i;
        double theta = StrictMath.toRadians((double)i);
        table[j][1] = StrictMath.sin(theta);
        table[j][2] = StrictMath.cos(theta);
        table[j++][3] = StrictMath.tan(theta);
    }
    System.out.println("angle#sin#cos#tan");
    for(i = 0; i < table.length; i++){
        for(j = 0; j < table[i].length; j++)
            System.out.print(table[i][j] + "");
        System.out.println();
    }
}

And the output is:

angle#sin#cos#tan
0.0 0.0 1.0 0.0 
30.0    0.49999999999999994 0.8660254037844387  0.5773502691896257  
45.0    0.7071067811865475  0.7071067811865476  0.9999999999999999  
60.0    0.8660254037844386  0.5000000000000001  1.7320508075688767  
90.0    1.0 6.123233995736766E-17   1.633123935319537E16    

(Please forgive the unorganised output). I've noted several things:

  • sin 30 i.e. 0.5 is stored as 0.49999999999999994.
  • tan 45 i.e. 1.0 is stored as 0.9999999999999999.
  • tan 90 i.e. infinity or undefined is stored as 1.633123935319537E16 (which is a very big number).

Naturally, I was quite confused to see the output (even after deciphering the output).

So I've read this post, and the best answer tells me:

These accuracy problems are due to the internal representation of floating > point numbers and there's not much you can do to avoid it.

By the way, printing these values at run-time often still leads to the correct results, at >least using modern C++ compilers. For most operations, this isn't much of an issue.

answered Oct 7 '08 at 7:42

Konrad Rudolph

So, my question is:

Is there any way to prevent such inaccurate results (in Java)?

Should I round-off the results? In that case, how would I store infinity i.e. Double.POSITIVE_INFINITY?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You have to take a bit of a zen* approach to floating-point numbers: rather than eliminating the error, learn to live with it.

In practice this usually means doing things like:

  • when displaying the number, use String.format to specify the amount of precision to display (it'll do the appropriate rounding for you)
  • when comparing against an expected value, don't look for equality (==). Instead, look for a small-enough delta: Math.abs(myValue - expectedValue) <= someSmallError

EDIT: For infinity, the same principle applies, but with a tweak: you have to pick some number to be "large enough" to treat as infinity. This is again because you have to learn to live with, rather than solve, imprecise values. In the case of something like tan(90 degrees), a double can't store π/2 with infinite precision, so your input is something very close to, but not exactly, 90 degrees -- and thus, the result is something very big, but not quite infinity. You may ask "why don't they just return Double.POSITIVE_INFINITY when you pass in the closest double to π/2," but that could lead to ambiguity: what if you really wanted the tan of that number, and not 90 degrees? Or, what if (due to previous floating-point error) you had something that was slightly farther from π/2 than the closest possible value, but for your needs it's still π/2? Rather than make arbitrary decisions for you, the JDK treats your close-to-but-not-exactly π/2 number at face value, and thus gives you a big-but-not-infinity result.

For some operations, especially those relating to money, you can use BigDecimal to eliminate floating-point errors: you can really represent values like 0.1 (instead of a value really really close to 0.1, which is the best a float or double can do). But this is much slower, and doesn't help you for things like sin/cos (at least with the built-in libraries).

* this probably isn't actually zen, but in the colloquial sense


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...