9

Why does tan 45(0.7853981633974483 in radian) give me 0.9999? What's wrong with the following code?

System.out.println(Math.tan(Math.toRadians(45.0)) );

I don't think there's any typo in here.

So what's the solution here?

starblue
  • 55,348
  • 14
  • 97
  • 151
siaooo
  • 1,827
  • 3
  • 23
  • 25

4 Answers4

19

Floating point calculations will often lead to such inaccuracies. The problem is that numbers cannot be accurately represented within a fixed number of bits.

To give you another example (in decimal), we all agree that 3 * (1/3) = 1. However, if your calculator only has 4 decimal places, 1/3 would be represented as 0.3333. When that's multiplied with 3, you would get 0.9999 not 1.

As further information, floating points on most systems are usually represented using the IEEE754 standard. You could search for it, or refer the Wikipedia page for more details. IEEE floating point

Masked Man
  • 1
  • 7
  • 40
  • 80
  • 1
    @siaooo Round to the relevant number of decimal places you need, for `tan(45)` it will round up to 1. – Scott Chamberlain Dec 08 '12 at 07:48
  • 1
    I agree, rounding off is the best you can do. If you are inclined to go to a little more technical detail, (cutting a long story short) conversion of analog data to digital will lead to information loss (due to a process called `quantization`). That loss cannot be recovered. – Masked Man Dec 08 '12 at 07:52
  • 3
    @ScottChamberlain: That's not a good idea if you do any calculations with the number afterward. Rounding introduces error. You don't want to introduce any more error than you have to. – tmyklebu Dec 08 '12 at 15:45
10

The closest double to pi/4 is exactly 0x1.921fb54442d18p-1. The tangent of this double, to more bits than you need, is 0x1.fffffffffffff72cece67p-1. Rounding to the nearest double gives you exactly 0x1.fffffffffffffp-1 because 0x1.fffffffffffff72cece67p-1 is less than 0x1.fffffffffffff8p-1.

tmyklebu
  • 13,915
  • 3
  • 28
  • 57
  • Yes! A sound answer! Does Java specify correctly rounded transcendental functions? – Pascal Cuoq Dec 08 '12 at 21:54
  • 1
    @PascalCuoq: Java doesn't ask for correct rounding. It specifies (looking at the API documentation, which I think is canonical) that answers must be within 1 ulp of the correct result and that, unless a pole of tan is berween `x` and `y`, `Math.tan(x) - Math.tan(y)` does not have the opposite sign as tan(x)-tan(y). – tmyklebu Dec 08 '12 at 22:13
  • A quick Google search took me back to the classic http://www.cs.berkeley.edu/~wkahan/LOG10HAF.TXT . Worth a re-read. – Pascal Cuoq Dec 08 '12 at 22:21
2

Use this

double radians = Math.toRadians(45.0);

System.out.format("The tangent of 45.0 degrees is %.4f%n", Math.tan(radians));
Mudassir Hasan
  • 28,083
  • 20
  • 99
  • 133
  • 1
    -1 This answer fails to understand that floating point arithmetic is the issue. Converting radians to degrees is not. –  Dec 08 '12 at 10:09
  • 2
    @woodchips I do not think this answer is claiming that converting radians to degrees is the issue. However, I do think that **converting radians to degrees is the issue** to some extent: if we had standardized on correctly rounded transcendental functions (we haven't), then if we had a tangent function that accepts degrees (we haven't), then the result of this function applied to 45 would be 1. However correctly rounded the radian tangent function we have, we are not applying it to π/4, but to the nearest `double`, and therefore we can hardly expect to get 1 in return. – Pascal Cuoq Dec 08 '12 at 21:48
  • @woodchips Besides, this answer is truncating decimals **at the time of converting to decimal for output**. That still puts it one head above the “truncate binary floating-point numbers to a fixed number of decimals” crowd that you can see some much of in questions, and answers, on StackOverflow. – Pascal Cuoq Dec 08 '12 at 21:51
  • @PascalCuoq - Anyway, the question HAS employed Math.toRadians(45), even though it also shows the explicit (truncated) value 0.7853981633974483, and it has done so since before your comments or mine. So your comment is not relevant, nor is this answer. –  Dec 09 '12 at 11:50
  • @woodchips Well, this answer offers a relevant palliative (use the `%.4f` format) and avoids a common mistake (trying to round a `double` to 4 significant decimal digits and store the rounded result as a double). Overall quite relevant, I would say. I am not the right person to discuss the relevance of my comment, of course. – Pascal Cuoq Dec 09 '12 at 12:34
  • 1
    @PascalCuoq - Changing the output format merely hides the fact that the number is not exactly 1. Hiding ones head in the sand is not the same as understanding why the number is not exactly 1, and then appreciating that it should not be relevant as long as you understand floating point numbers and work with a proper tolerance. –  Dec 09 '12 at 13:52
  • woodchips and @Pascal Cuoq . thnks for the inputs...learnt significant stuff from your conversations... – Mudassir Hasan Dec 09 '12 at 14:05
0

That's probably because tan(45) is 1, and the rest is a rounding error. Floating point calculations are highly unlikely to give you accurate results, due to how floating point calculations work.

Isaac
  • 16,458
  • 5
  • 57
  • 81
  • 3
    It's because you're taking the tangent of the *closest double* to pi/4. `tan` in the math library is correctly rounded. The point of floating-point calculations is that they go fast and they *do* give you accurate results if you pay attention to what you're doing. – tmyklebu Dec 08 '12 at 15:44