1

This is not duplicate of this, and this I am developing a Calculator application for Android and I have been searching web for past 20-30 days but did not find any reasonable answer. I have also studied many papers on Floating Point Computation. I have also tried both Math and StrictMath library.

The following values I have tried

Math.cos(Math.PI/4) result in 0.7071067811865476 which is correct answer

Math.cos(Math.PI/2) result in 6.123233995736766E-17 correct answer is 0

Math.cos(Math.PI) result in -1.0 which is correct

Math.cos((3*Math.PI)/2) result in -1.8369701987210297E-16 correct answer is 0

Math.cos(Math.PI*2) result in 1.0 which is correct



Math.sin(Math.PI/4) result in 0.7071067811865476 which is correct answer

Math.sin(Math.PI/2) result in 1 which is correct

Math.sin(Math.PI) result in 1.2246467991473532E-16 correct answer is 0

Math.sin((3*Math.PI)/2) result in -1 which is correct

Math.sin(Math.PI*2) result in -2.4492935982947064E-16 correct answer is 0



Math.tan(Math.PI/4) result in 0.999999999999999 correct answer is 1

Math.tan(Math.PI/2) result in 1.633123935319537E16 correct answer is NaN

Math.tan(Math.PI) result in -1.2246467991473532E-16 correct answer is 0

Math.tan((3*Math.PI)/2) result in 5.443746451065123E15 correct answer is NaN

Math.tan(Math.PI*2) result in -2.4492935982947064E-16 correct answer is 0

When I tried all these calculations on Google's Official calculator which is included in Stock Lollipop yielded all correct answers except for tan((3*PI)/2) and tan(PI/2)

When I tried all these calculation on my Casio fx-991 PLUS all answers were correct.

Now my question is "How Google's calculator and Casio's calculator managed to get correct answer using limited floating precision of CPU?" and "How can I achieve same output?"

Community
  • 1
  • 1
Kamil Mahmood
  • 527
  • 9
  • 22
  • Have you read the API for Math? There are some details about the precision of the calculations. – MadConan Apr 08 '15 at 14:02
  • Yes I have read API for Math and they say that result can be in 1-2 ulp but it is not pet rule for all floating number – Kamil Mahmood Apr 08 '15 at 14:26
  • possible duplicate of [Java Math.cos() Method Does Not Return 0 When Expected](http://stackoverflow.com/questions/2235689/java-math-cos-method-does-not-return-0-when-expected) – andand Apr 08 '15 at 14:35
  • @andand if you read first line I clearly mentioned that it is not duplicate. – Kamil Mahmood Apr 08 '15 at 14:55
  • If it's important to get exact values, my advice is to use a symbolic math library. – Robert Dodier Apr 08 '15 at 15:55
  • 1
    @KamilMahmood: I read the first line, and though you claim they're not duplicates, I disagree. You're dealing with the same underlying problem, and once the problem is understood correctly (and those link you cite both described the problem and provided references which should have been useful) a solution should have been clear. Clearly understanding the problem is the first and most critical step in solving any problem. – andand Apr 08 '15 at 17:24
  • @andand All answers tell "Why this problem occur?". Any answer does not tell "How to solve?" that is why it is not duplicate. – Kamil Mahmood Apr 08 '15 at 17:35

3 Answers3

4

I am skeptical of many of the "correct answer" values you give. sin(pi) is 0, but Math.PI is not pi, it is an approximation. Sine of something that is only close to PI shouldn't give you 0. How is the user entering the values? If the user enters a decimal input, with 16 decimal places, he/she should expect to have some results that are off in the 16th decimal place. If a user asks for sin(10^-15) and you change the input to 0 and return a result of 0, then you make it so the user can't compute a numerical derivative for sin x at 0 by computing (sin(10^-15)-sin(0))/(10^-15-0). The same is true if the user enters an approximation like Math.PI and you change the input to pi.

As Bryan Reilly answered, you can round results before presenting them to the user, and this will avoid showing a value like 5*10^-15 instead of 0.

You can shift the inputs to ranges near 0. For example, for values of x greater than pi or less than -pi, you can subtract off a multiple of 2pi to get a value in [-pi,pi]. Then you can use trig identities to reduce the domain you need further to [0,pi/2]. For example, if x is in [-pi,pi/2], then use sin(x) = -sin(x+pi).

If any roundoff errors at all are unacceptable, then perhaps you should make a symbolic calculator instead of a floating point calculator.

Douglas Zare
  • 3,296
  • 1
  • 14
  • 21
  • rounding is acceptable but to some extent. I wonder how Windows default calculator uses the precision up to 31 places and support number up to 3248! = 1.9736342530860425312047080034031e+9997 and calculate everything perfect. What data type they use. – Kamil Mahmood Apr 08 '15 at 16:01
  • The Windows calculator uses much higher precision than is ordinarily used. It probably uses argument reduction. One way to see that it is not perfect is to compute something like sind(361)-sind(1). That's sine in degrees. On my computer, version 6.1 Build 7601, it says the difference is not 0, but 2.78633...E-41. This is probably because it calculates sind(1) with more precision than sind(361), since it calculates the same difference for sind(721)-sind(1) and sind(1081)-sin(1), but a different value for sind(3601)-sind(1). – Douglas Zare Apr 08 '15 at 16:53
  • If you use BigDecimals, and argument reduction, you can calculate your own values for sin and cosine using Taylor series by reducing the argument to be close to 0, and estimating how many terms you need for the accuracy you want. This is not efficient, but for this purpose there may be no real difference between 1 microsecond and 1 millisecond. – Douglas Zare Apr 08 '15 at 16:56
  • Now I am using (Math.floor(Math.ulp(value))) and it is yielding desired result – Kamil Mahmood Apr 08 '15 at 17:20
  • I am getting all the answer 0 using your answer @KamilMahmood – Marfin. F Sep 23 '20 at 00:15
2

What is most likely is that Google and Casios calculators simply round down if the result is something smaller than say 1.0E-14.

Something similar can be done if the number is too large.

Floating point inaccuracies are hard to deal with but rounding is the most common way to fix them.

And although it seems like you know what is happening under the hood, this may help you:

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Bryan Reilly
  • 114
  • 4
0

Maybe they use something like the Big Decimal Class

Sorry I'm not yet allowed to provide a simple comment.

For example yesterday I created a simple swing application which converted from decimal to unsigned int binary and vice versa. The problem is, what the user enters in a text box are String values to convert, and they can easily exceed even Long.MAX_VALUE.

What I wanted was for the user to enter as many digits as desired in both cases, so I used the Big Integer Class, which is similar to Big Decimal but is suited for Integer values. The result of using it allows users to enter strings of incredible length, and for my program to output the conversion in as much or even more digits, far, far exceeding Long.MAX_VALUE.

However, since the Math functions use doubles, we are still limited here with the 'Big' classes, which initialize with doubles and strings. If you have an application where you can represent numbers with strings (which can have a max character length of Integer.MAX_VALUE), then the 'Big' classes are great. However if you want to initialize with a double, you are obviously limited by the constraints of double.

APengue
  • 138
  • 6