Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8051808

Math.toRadians and Math.toDegrees could be more accurate (and faster)

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P4
    • Resolution: Duplicate
    • Affects Version/s: 8u11
    • Fix Version/s: None
    • Component/s: core-libs
    • Labels:
    • Subcomponent:
    • CPU:
      x86
    • OS:
      windows_7

      Description

      A DESCRIPTION OF THE REQUEST :
      In java.lang.Math, the methods toRadians and toDegrees are not as accurate as they could be. This is the current implementation:

          public static double toRadians(double angdeg) {
              return angdeg / 180.0 * PI;
          }
          
          public static double toDegrees(double angrad) {
              return angrad * 180.0 / PI;
          }

      The following simple change would produce a result that is generally more accurate:

          public static double toRadians(double angdeg) {
              return angdeg * DEG_TO_RAD;
          }
          
          public static double toDegrees(double angrad) {
              return angrad / DEG_TO_RAD;
          }
          
          /** Constant used by toRadians and toDegrees. */
          private static final double DEG_TO_RAD = 0.017453292519943295;

      The attached test case shows this in detail. It tests three techniques for converting radians to degrees and degrees to radians.

      (1) Using the standard Math methods ("standard").
      (2) Using a single double constant DEG_TO_RAD in the way shown above ("better").
      (3) Using BigDecimal for computation, then converting back to double ("best").

      It tests a large number of random values and compares how often the "better" technique beats the standard technique at producing a result that is as close as possible to the one computed by BigDecimal. It shows that:

      - The better technique is more accurate than the standard technique about 24% of the time.
      - The better technique is less accurate than the standard technique about 3% of the time.

      (The rest of the time, it makes no difference.)

      Not only would this change make the Math methods slightly more accurate, it would be faster, because it only does a single arithmetic operation instead of two. An equivalent change could benefit StrictMath.

      JUSTIFICATION :
      It's better, it's faster, and it's an easy change to implement.


      ---------- BEGIN SOURCE ----------
      import java.math.BigDecimal;
      import java.math.RoundingMode;
      import java.util.Random;

      class MathRadiansTest {
          static final BigDecimal PI = new BigDecimal(
              "3.14159265358979323846264338327950288419716939937510" +
              "5820974944592307816406286208998628034825342117067982");
          
          static final double DEG_TO_RAD = PI.divide(new BigDecimal(180), 100, RoundingMode.HALF_UP).doubleValue();
          static final double RAD_TO_DEG = new BigDecimal(180).divide(PI, 100, RoundingMode.HALF_UP).doubleValue();
          
          
          public static void main(String[] args) {
              Random random = new Random(0);
              
              int toRadBetterTechniqueWins = 0;
              int toRadBetterTechniqueLoses = 0;
              int toDegBetterTechniqueWins = 0;
              int toDegBetterTechniqueLoses = 0;
              
              for (int i = 0; i < 10000; i++) {
                  double degrees = random.nextInt(360) + random.nextDouble();
                  
                  double standard = Math.toRadians(degrees);
                  double better = degrees * DEG_TO_RAD;
                  double best = new BigDecimal(degrees)
                      .divide(new BigDecimal(180), 100, RoundingMode.HALF_UP)
                      .multiply(PI)
                      .doubleValue();
                  
                  double standardError = Math.abs(best - standard);
                  double betterError = Math.abs(best - better);
                  
                  if (betterError < standardError) {
                      toRadBetterTechniqueWins++;
                  } else if (betterError > standardError) {
                      toRadBetterTechniqueLoses++;
                  }
                  
                  if (!(standard == better && better == best)) {
                      System.out.println(
                          degrees + " => " +
                          "Standard: " + standard + "; " +
                          "Better: " + better + "; " +
                          "Best: " + best);
                  }
              }
              
              for (int i = 0; i < 10000; i++) {
                  double radians = random.nextDouble() * (2 * Math.PI);
                  
                  double standard = Math.toDegrees(radians);
                  
                  // seemingly more accurate to divide than to multiply by the reciprocal:
                  double better = radians / DEG_TO_RAD;
                  //double better = radians * RAD_TO_DEG;
                  
                  double best = new BigDecimal(radians)
                      .multiply(new BigDecimal(180))
                      .divide(PI, 100, RoundingMode.HALF_UP)
                      .doubleValue();
                  
                  double standardError = Math.abs(best - standard);
                  double betterError = Math.abs(best - better);
                  
                  if (betterError < standardError) {
                      toDegBetterTechniqueWins++;
                  } else if (betterError > standardError) {
                      toDegBetterTechniqueLoses++;
                  }
                  
                  if (!(standard == better && better == best)) {
                      System.out.println(
                          radians + " => " +
                          "Standard: " + standard + "; " +
                          "Better: " + better + "; " +
                          "Best: " + best);
                  }
              }
              
              System.out.println("When converting from degrees to radians:");
              System.out.println("Better technique wins: " + toRadBetterTechniqueWins);
              System.out.println("Better technique loses: " + toRadBetterTechniqueLoses);
              
              System.out.println("When converting from radians to degrees:");
              System.out.println("Better technique wins: " + toDegBetterTechniqueWins);
              System.out.println("Better technique loses: " + toDegBetterTechniqueLoses);
              
              //System.out.println("DEG_TO_RAD = " + DEG_TO_RAD);
              //System.out.println("RAD_TO_DEG = " + RAD_TO_DEG);
          }
      }
      ---------- END SOURCE ----------

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                bpb Brian Burkhalter
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                0 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: