1 /*
   2  * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.jtt.lang;
  26 
  27 import java.util.ArrayList;
  28 import java.util.Collection;
  29 import java.util.List;
  30 import java.util.Random;
  31 
  32 import org.junit.Before;
  33 import org.junit.Test;
  34 import org.junit.runner.RunWith;
  35 import org.junit.runners.Parameterized;
  36 import org.junit.runners.Parameterized.Parameter;
  37 import org.junit.runners.Parameterized.Parameters;
  38 
  39 import org.graalvm.compiler.jtt.JTTTest;
  40 
  41 import jdk.vm.ci.meta.ResolvedJavaMethod;
  42 
  43 /**
  44  * This has been converted to JUnit from the jtreg test test/java/lang/Math/Log10Tests.java in JDK8.
  45  */
  46 @RunWith(Parameterized.class)
  47 public final class Math_log10 extends JTTTest {
  48 
  49     static final double LN_10 = StrictMath.log(10.0);
  50 
  51     @Parameter(value = 0) public double input;
  52     @Parameter(value = 1) public Number input2;
  53     @Parameter(value = 2) public Number result;
  54     @Parameter(value = 3) public Condition condition;
  55     public Double computedResult;
  56 
  57     enum Condition {
  58         EQUALS,
  59         THREE_ULPS,
  60         MONOTONICITY
  61     }
  62 
  63     public static double log10(double v) {
  64         return Math.log10(v);
  65     }
  66 
  67     public static boolean log10Monotonicity(double v, double v2) {
  68         return Math.log10(v) < Math.log(v2);
  69     }
  70 
  71     @Test
  72     public void testLog10() {
  73         if (condition == Condition.MONOTONICITY) {
  74             runTest("log10Monotonicity", input, input2.doubleValue());
  75         } else {
  76             runTest("log10", input);
  77         }
  78     }
  79 
  80     public static double strictLog10(double v) {
  81         return StrictMath.log10(v);
  82     }
  83 
  84     public static boolean strictLog10Monotonicity(double v, double v2) {
  85         return StrictMath.log10(v) < StrictMath.log(v2);
  86     }
  87 
  88     @Test
  89     public void testStrictLog10() {
  90         if (condition == Condition.MONOTONICITY) {
  91             runTest("strictLog10Monotonicity", input, input2.doubleValue());
  92         } else {
  93             runTest("strictLog10", input);
  94         }
  95     }
  96 
  97     @Before
  98     public void before() {
  99         computedResult = null;
 100     }
 101 
 102     private static boolean checkFor3ulps(double expected, double result) {
 103         return Math.abs(result - expected) / Math.ulp(expected) <= 3;
 104     }
 105 
 106     @Override
 107     protected void assertDeepEquals(Object expected, Object actual) {
 108         if (this.condition == Condition.THREE_ULPS) {
 109             double actualValue = ((Number) actual).doubleValue();
 110             assertTrue("differs by more than 3 ulps: " + result.doubleValue() + "," + actualValue, checkFor3ulps(result.doubleValue(), actualValue));
 111             if (computedResult != null && actualValue != computedResult) {
 112                 /*
 113                  * This test detects difference in the actual result between the built in
 114                  * implementation and what Graal does. If it reaches this test then the value was
 115                  * within 3 ulps but differs in the exact amount.
 116                  *
 117                  * System.err.println("value for " + input + " is within 3 ulps but differs from
 118                  * computed value: " + computedResult + " " + actualValue);
 119                  */
 120             }
 121         } else {
 122             super.assertDeepEquals(expected, actual);
 123         }
 124     }
 125 
 126     @Override
 127     protected Result executeExpected(ResolvedJavaMethod method, Object receiver, Object... args) {
 128         Result actual = super.executeExpected(method, receiver, args);
 129         if (actual.returnValue instanceof Number) {
 130             computedResult = ((Number) actual.returnValue).doubleValue();
 131             assertDeepEquals(computedResult, actual.returnValue);
 132         }
 133         return actual;
 134     }
 135 
 136     static void addEqualityTest(List<Object[]> tests, double input, double expected) {
 137         tests.add(new Object[]{input, null, expected, Condition.EQUALS});
 138     }
 139 
 140     static void add3UlpTest(List<Object[]> tests, double input, double expected) {
 141         tests.add(new Object[]{input, null, expected, Condition.THREE_ULPS});
 142     }
 143 
 144     static void addMonotonicityTest(List<Object[]> tests, double input, double input2) {
 145         tests.add(new Object[]{input, input2, null, Condition.MONOTONICITY});
 146     }
 147 
 148     @Parameters(name = "{index}")
 149     public static Collection<Object[]> data() {
 150         List<Object[]> tests = new ArrayList<>();
 151 
 152         addEqualityTest(tests, Double.NaN, Double.NaN);
 153         addEqualityTest(tests, Double.longBitsToDouble(0x7FF0000000000001L), Double.NaN);
 154         addEqualityTest(tests, Double.longBitsToDouble(0xFFF0000000000001L), Double.NaN);
 155         addEqualityTest(tests, Double.longBitsToDouble(0x7FF8555555555555L), Double.NaN);
 156         addEqualityTest(tests, Double.longBitsToDouble(0xFFF8555555555555L), Double.NaN);
 157         addEqualityTest(tests, Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL), Double.NaN);
 158         addEqualityTest(tests, Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), Double.NaN);
 159         addEqualityTest(tests, Double.longBitsToDouble(0x7FFDeadBeef00000L), Double.NaN);
 160         addEqualityTest(tests, Double.longBitsToDouble(0xFFFDeadBeef00000L), Double.NaN);
 161         addEqualityTest(tests, Double.longBitsToDouble(0x7FFCafeBabe00000L), Double.NaN);
 162         addEqualityTest(tests, Double.longBitsToDouble(0xFFFCafeBabe00000L), Double.NaN);
 163         addEqualityTest(tests, Double.NEGATIVE_INFINITY, Double.NaN);
 164         addEqualityTest(tests, -8.0, Double.NaN);
 165         addEqualityTest(tests, -1.0, Double.NaN);
 166         addEqualityTest(tests, -Double.MIN_NORMAL, Double.NaN);
 167         addEqualityTest(tests, -Double.MIN_VALUE, Double.NaN);
 168         addEqualityTest(tests, -0.0, -Double.POSITIVE_INFINITY);
 169         addEqualityTest(tests, +0.0, -Double.POSITIVE_INFINITY);
 170         addEqualityTest(tests, +1.0, 0.0);
 171         addEqualityTest(tests, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
 172 
 173         // Test log10(10^n) == n for integer n; 10^n, n < 0 is not
 174         // exactly representable as a floating-point value -- up to
 175         // 10^22 can be represented exactly
 176         double testCase = 1.0;
 177         for (int i = 0; i < 23; i++) {
 178             addEqualityTest(tests, testCase, i);
 179             testCase *= 10.0;
 180         }
 181 
 182         // Test for gross inaccuracy by comparing to log; should be
 183         // within a few ulps of log(x)/log(10)
 184         Random rand = new java.util.Random(0L);
 185         for (int i = 0; i < 500; i++) {
 186             double input = Double.longBitsToDouble(rand.nextLong());
 187             if (!Double.isFinite(input)) {
 188                 continue; // avoid testing NaN and infinite values
 189             } else {
 190                 input = Math.abs(input);
 191 
 192                 double expected = StrictMath.log(input) / LN_10;
 193                 if (!Double.isFinite(expected)) {
 194                     continue; // if log(input) overflowed, try again
 195                 } else {
 196                     add3UlpTest(tests, input, expected);
 197                 }
 198             }
 199         }
 200 
 201         double z = Double.NaN;
 202         // Test inputs greater than 1.0.
 203         double[] input = new double[40];
 204         int half = input.length / 2;
 205         // Initialize input to the 40 consecutive double values
 206         // "centered" at 1.0.
 207         double up = Double.NaN;
 208         double down = Double.NaN;
 209         for (int i = 0; i < half; i++) {
 210             if (i == 0) {
 211                 input[half] = 1.0;
 212                 up = Math.nextUp(1.0);
 213                 down = Math.nextDown(1.0);
 214             } else {
 215                 input[half + i] = up;
 216                 input[half - i] = down;
 217                 up = Math.nextUp(up);
 218                 down = Math.nextDown(down);
 219             }
 220         }
 221         input[0] = Math.nextDown(input[1]);
 222         for (int i = 0; i < input.length; i++) {
 223             // Test accuracy.
 224             z = input[i] - 1.0;
 225             double expected = (z - (z * z) * 0.5) / LN_10;
 226             add3UlpTest(tests, input[i], expected);
 227 
 228             // Test monotonicity
 229             if (i > 0) {
 230                 addMonotonicityTest(tests, input[i - 1], input[i]);
 231             }
 232         }
 233 
 234         return tests;
 235     }
 236 
 237 }