1 /*
   2  * Copyright (c) 2003, 2012, 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  * @test
  26  * @bug 4851638 4900189 4939441
  27  * @summary Tests for {Math, StrictMath}.expm1
  28  * @author Joseph D. Darcy
  29  */
  30 
  31 /*
  32  * The Taylor expansion of expxm1(x) = exp(x) -1 is
  33  *
  34  * 1 + x/1! + x^2/2! + x^3/3| + ... -1 =
  35  *
  36  * x + x^2/2! + x^3/3 + ...
  37  *
  38  * Therefore, for small values of x, expxm1 ~= x.
  39  *
  40  * For large values of x, expxm1(x) ~= exp(x)
  41  *
  42  * For large negative x, expxm1(x) ~= -1.
  43  */
  44 
  45 public class Expm1Tests {
  46 
  47     private Expm1Tests(){}
  48 
  49     static final double infinityD = Double.POSITIVE_INFINITY;
  50     static final double NaNd = Double.NaN;
  51 
  52     static int testExpm1() {
  53         int failures = 0;
  54 
  55         double [][] testCases = {
  56             {Double.NaN,                NaNd},
  57             {Double.longBitsToDouble(0x7FF0000000000001L),      NaNd},
  58             {Double.longBitsToDouble(0xFFF0000000000001L),      NaNd},
  59             {Double.longBitsToDouble(0x7FF8555555555555L),      NaNd},
  60             {Double.longBitsToDouble(0xFFF8555555555555L),      NaNd},
  61             {Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL),      NaNd},
  62             {Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL),      NaNd},
  63             {Double.longBitsToDouble(0x7FFDeadBeef00000L),      NaNd},
  64             {Double.longBitsToDouble(0xFFFDeadBeef00000L),      NaNd},
  65             {Double.longBitsToDouble(0x7FFCafeBabe00000L),      NaNd},
  66             {Double.longBitsToDouble(0xFFFCafeBabe00000L),      NaNd},
  67             {infinityD,                 infinityD},
  68             {-infinityD,                -1.0},
  69             {-0.0,                      -0.0},
  70             {+0.0,                      +0.0},
  71         };
  72 
  73         // Test special cases
  74         for(int i = 0; i < testCases.length; i++) {
  75             failures += testExpm1CaseWithUlpDiff(testCases[i][0],
  76                                                  testCases[i][1], 0, null);
  77         }
  78 
  79 
  80         // For |x| < 2^-54 expm1(x) ~= x
  81         for(int i = DoubleConsts.MIN_SUB_EXPONENT; i <= -54; i++) {
  82             double d = Math.scalb(2, i);
  83             failures += testExpm1Case(d, d);
  84             failures += testExpm1Case(-d, -d);
  85         }
  86 
  87 
  88         // For values of y where exp(y) > 2^54, expm1(x) ~= exp(x).
  89         // The least such y is ln(2^54) ~= 37.42994775023705; exp(x)
  90         // overflows for x > ~= 709.8
  91 
  92         // Use a 2-ulp error threshold to account for errors in the
  93         // exp implementation; the increments of d in the loop will be
  94         // exact.
  95         for(double d = 37.5; d <= 709.5; d += 1.0) {
  96             failures += testExpm1CaseWithUlpDiff(d, StrictMath.exp(d), 2, null);
  97         }
  98 
  99         // For x > 710, expm1(x) should be infinity
 100         for(int i = 10; i <= Double.MAX_EXPONENT; i++) {
 101             double d = Math.scalb(2, i);
 102             failures += testExpm1Case(d, infinityD);
 103         }
 104 
 105         // By monotonicity, once the limit is reached, the
 106         // implemenation should return the limit for all smaller
 107         // values.
 108         boolean reachedLimit [] = {false, false};
 109 
 110         // Once exp(y) < 0.5 * ulp(1), expm1(y) ~= -1.0;
 111         // The greatest such y is ln(2^-53) ~= -36.7368005696771.
 112         for(double d = -36.75; d >= -127.75; d -= 1.0) {
 113             failures += testExpm1CaseWithUlpDiff(d, -1.0, 1,
 114                                                  reachedLimit);
 115         }
 116 
 117         for(int i = 7; i <= Double.MAX_EXPONENT; i++) {
 118             double d = -Math.scalb(2, i);
 119             failures += testExpm1CaseWithUlpDiff(d, -1.0, 1, reachedLimit);
 120         }
 121 
 122         // Test for monotonicity failures near multiples of log(2).
 123         // Test two numbers before and two numbers after each chosen
 124         // value; i.e.
 125         //
 126         // pcNeighbors[] =
 127         // {nextDown(nextDown(pc)),
 128         // nextDown(pc),
 129         // pc,
 130         // nextUp(pc),
 131         // nextUp(nextUp(pc))}
 132         //
 133         // and we test that expm1(pcNeighbors[i]) <= expm1(pcNeighbors[i+1])
 134         {
 135             double pcNeighbors[] = new double[5];
 136             double pcNeighborsExpm1[] = new double[5];
 137             double pcNeighborsStrictExpm1[] = new double[5];
 138 
 139             for(int i = -50; i <= 50; i++) {
 140                 double pc = StrictMath.log(2)*i;
 141 
 142                 pcNeighbors[2] = pc;
 143                 pcNeighbors[1] = Math.nextDown(pc);
 144                 pcNeighbors[0] = Math.nextDown(pcNeighbors[1]);
 145                 pcNeighbors[3] = Math.nextUp(pc);
 146                 pcNeighbors[4] = Math.nextUp(pcNeighbors[3]);
 147 
 148                 for(int j = 0; j < pcNeighbors.length; j++) {
 149                     pcNeighborsExpm1[j]       =       Math.expm1(pcNeighbors[j]);
 150                     pcNeighborsStrictExpm1[j] = StrictMath.expm1(pcNeighbors[j]);
 151                 }
 152 
 153                 for(int j = 0; j < pcNeighborsExpm1.length-1; j++) {
 154                     if(pcNeighborsExpm1[j] >  pcNeighborsExpm1[j+1] ) {
 155                         failures++;
 156                         System.err.println("Monotonicity failure for Math.expm1 on " +
 157                                           pcNeighbors[j] + " and "  +
 158                                           pcNeighbors[j+1] + "\n\treturned " +
 159                                           pcNeighborsExpm1[j] + " and " +
 160                                           pcNeighborsExpm1[j+1] );
 161                     }
 162 
 163                     if(pcNeighborsStrictExpm1[j] >  pcNeighborsStrictExpm1[j+1] ) {
 164                         failures++;
 165                         System.err.println("Monotonicity failure for StrictMath.expm1 on " +
 166                                           pcNeighbors[j] + " and "  +
 167                                           pcNeighbors[j+1] + "\n\treturned " +
 168                                           pcNeighborsStrictExpm1[j] + " and " +
 169                                           pcNeighborsStrictExpm1[j+1] );
 170                     }
 171 
 172 
 173                 }
 174 
 175             }
 176         }
 177 
 178         return failures;
 179     }
 180 
 181     public static int testExpm1Case(double input,
 182                                     double expected) {
 183         return testExpm1CaseWithUlpDiff(input, expected, 1, null);
 184     }
 185 
 186     public static int testExpm1CaseWithUlpDiff(double input,
 187                                                double expected,
 188                                                double ulps,
 189                                                boolean [] reachedLimit) {
 190         int failures = 0;
 191         double mathUlps = ulps, strictUlps = ulps;
 192         double mathOutput;
 193         double strictOutput;
 194 
 195         if (reachedLimit != null) {
 196             if (reachedLimit[0])
 197                 mathUlps = 0;
 198 
 199             if (reachedLimit[1])
 200                 strictUlps = 0;
 201         }
 202 
 203         failures += Tests.testUlpDiffWithLowerBound("Math.expm1(double)",
 204                                                     input, mathOutput=Math.expm1(input),
 205                                                     expected, mathUlps, -1.0);
 206         failures += Tests.testUlpDiffWithLowerBound("StrictMath.expm1(double)",
 207                                                     input, strictOutput=StrictMath.expm1(input),
 208                                                     expected, strictUlps, -1.0);
 209         if (reachedLimit != null) {
 210             reachedLimit[0] |= (mathOutput   == -1.0);
 211             reachedLimit[1] |= (strictOutput == -1.0);
 212         }
 213 
 214         return failures;
 215     }
 216 
 217     public static void main(String argv[]) {
 218         int failures = 0;
 219 
 220         failures += testExpm1();
 221 
 222         if (failures > 0) {
 223             System.err.println("Testing expm1 incurred "
 224                                + failures + " failures.");
 225             throw new RuntimeException();
 226         }
 227     }
 228 }