2

I have a Java class that deals with a lot of trigonometric functions using java.lang.Math. The following code determines the average direction of 0 degrees and 180 degrees. The result should be NaN, since 0 degrees and 180 degrees are in opposite directions, canceling each other out. However, when I do:

double[] angles = {0, 180};
double sines = 0;
double cosines = 0;
double avg;
for (int i = 0; i < angles.length; i++) {
    double rad = Math.toRadians(angles[i]);
    sines += Math.sin(rad);
    cosines += Math.cos(rad);
}
double sin = sines / angles.length;
double cos = cosines / angles.length;
System.out.println("Avg sin: " + sin + "\nAvg cos: " + cos); // sin != 0 but it should
double avgRad = Math.atan2(sin, cos);
avg = Math.toDegrees(avgRad);
System.out.println("Average: " + avg);

avg equals 90.0 instead of NaN. This is because the average sine of 180 degrees and 0 degrees results in a very small but positive number (due to the way floating-point precision works). If you run the above code you will see what I mean. How can I avoid this lack of precision? I know that I could round the average sines and cosines as well as the final result, but that seems a little inelegant to me.

null
  • 2,060
  • 3
  • 23
  • 42
  • Floating point math is susceptible to rounding errors. Live with it. – Mick Sep 13 '16 at 02:13
  • I know that it is. I'm asking how to circumvent it. – null Sep 13 '16 at 02:17
  • What exactly are you trying to do? – Mick Sep 13 '16 at 02:18
  • I am determining the average direction of an array of directions (in terms of degrees). The above code returns an incorrect result when the array is [0, 180] because of how floating-point precision works. I am trying to circumvent this issue. (I mention that I could round some of the values, but that seems inelegant, and I could lose precision with all that rounding.) – null Sep 13 '16 at 02:22
  • 1
    Presumably, you have some reason for summing the sines and cosines. You're asking for trouble by using atan(). Use atan2() instead. – Mick Sep 13 '16 at 02:34
  • What do you take average of 0,180 as? Also 0,360 as? Don't you want the average of 0,180 as 90? Don't you want the average of 0,360 as 0? – blackpen Sep 13 '16 at 03:26

1 Answers1

0

I take it granted that you must have considered averaging angles directly (by using mod 360) before you went onto use sin/cos/tan. That being said, I think, you are in right direction in getting what you intended in your code (except possibly the negative sign flip in the last example).

~> java Main 0 180
Avg sin: 6.123233995736766E-17
Avg cos: 0.0
Average: 90.0

~> java Main 0 179
Avg sin: 0.00872620321864172
Avg cos: 7.615242180436521E-5
Average: 89.50000000000011

~> java Main 1 179
Avg sin: 0.017452406437283477
Avg cos: 0.0
Average: 90.0

~> java Main 1 180
Avg sin: 0.008726203218641817
Avg cos: -7.615242180436521E-5
Average: 90.49999999999991

~> java Main 1 181
Avg sin: 1.5959455978986625E-16
Avg cos: 0.0
Average: 90.0

~> java Main 1 182
Avg sin: -0.008723545132608694
Avg cos: 2.2843406864775373E-4
Average: -88.50000000000001
blackpen
  • 2,339
  • 13
  • 15