1 /*
   2  * Copyright (c) 2015, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 // This file is available under and governed by the GNU General Public
  27 // License version 2 only, as published by the Free Software Foundation.
  28 // However, the following notice accompanied the original version of this
  29 // file:
  30 //
  31 // Copyright 2006-2008 the V8 project authors. All rights reserved.
  32 
  33 package jdk.nashorn.internal.runtime.doubleconv.test;
  34 
  35 import org.testng.annotations.Test;
  36 
  37 import java.lang.reflect.Constructor;
  38 import java.lang.reflect.Method;
  39 
  40 import static org.testng.Assert.assertEquals;
  41 import static org.testng.Assert.assertTrue;
  42 
  43 /**
  44  * IeeeDouble tests
  45  *
  46  * @test
  47  * @modules jdk.scripting.nashorn/jdk.nashorn.internal.runtime.doubleconv:open
  48  * @run testng jdk.nashorn.internal.runtime.doubleconv.test.IeeeDoubleTest
  49  */
  50 @SuppressWarnings({"unchecked", "javadoc"})
  51 public class IeeeDoubleTest {
  52 
  53     static final Method asDiyFp;
  54     static final Method asNormalizedDiyFp;
  55     static final Method doubleToLong;
  56     static final Method longToDouble;
  57     static final Method isDenormal;
  58     static final Method isSpecial;
  59     static final Method isInfinite;
  60     static final Method isNaN;
  61     static final Method value;
  62     static final Method sign;
  63     static final Method nextDouble;
  64     static final Method previousDouble;
  65     static final Method normalizedBoundaries;
  66     static final Method Infinity;
  67     static final Method NaN;
  68     static final Method f;
  69     static final Method e;
  70     static final Constructor<?> DiyFpCtor;
  71 
  72     static {
  73         try {
  74             final Class<?> IeeeDouble = Class.forName("jdk.nashorn.internal.runtime.doubleconv.IeeeDouble");
  75             final Class DiyFp = Class.forName("jdk.nashorn.internal.runtime.doubleconv.DiyFp");
  76             asDiyFp = method(IeeeDouble, "asDiyFp", long.class);
  77             asNormalizedDiyFp = method(IeeeDouble, "asNormalizedDiyFp", long.class);
  78             doubleToLong = method(IeeeDouble, "doubleToLong", double.class);
  79             longToDouble = method(IeeeDouble, "longToDouble", long.class);
  80             isDenormal = method(IeeeDouble, "isDenormal", long.class);
  81             isSpecial = method(IeeeDouble, "isSpecial", long.class);
  82             isInfinite = method(IeeeDouble, "isInfinite", long.class);
  83             isNaN = method(IeeeDouble, "isNaN", long.class);
  84             value = method(IeeeDouble, "value", long.class);
  85             sign = method(IeeeDouble, "sign", long.class);
  86             nextDouble = method(IeeeDouble, "nextDouble", long.class);
  87             previousDouble = method(IeeeDouble, "previousDouble", long.class);
  88             Infinity = method(IeeeDouble, "Infinity");
  89             NaN = method(IeeeDouble, "NaN");
  90             normalizedBoundaries = method(IeeeDouble, "normalizedBoundaries", long.class, DiyFp, DiyFp);
  91             DiyFpCtor = DiyFp.getDeclaredConstructor();
  92             DiyFpCtor.setAccessible(true);
  93             f = method(DiyFp, "f");
  94             e = method(DiyFp, "e");
  95         } catch (final Exception e) {
  96             throw new RuntimeException(e);
  97         }
  98     }
  99 
 100     private static Method method(final Class<?> clazz, final String name, final Class<?>... params) throws NoSuchMethodException {
 101         final Method m = clazz.getDeclaredMethod(name, params);
 102         m.setAccessible(true);
 103         return m;
 104     }
 105 
 106     @Test
 107     public void testUint64Conversions() throws Exception {
 108         // Start by checking the byte-order.
 109         final long ordered = 0x0123456789ABCDEFL;
 110         assertEquals(3512700564088504e-318, value.invoke(null, ordered));
 111 
 112         final long min_double64 = 0x0000000000000001L;
 113         assertEquals(5e-324, value.invoke(null, min_double64));
 114 
 115         final long max_double64 = 0x7fefffffffffffffL;
 116         assertEquals(1.7976931348623157e308, value.invoke(null, max_double64));
 117     }
 118 
 119 
 120     @Test
 121     public void testDoubleAsDiyFp() throws Exception {
 122         final long ordered = 0x0123456789ABCDEFL;
 123         Object diy_fp = asDiyFp.invoke(null, ordered);
 124         assertEquals(0x12 - 0x3FF - 52, e.invoke(diy_fp));
 125         // The 52 mantissa bits, plus the implicit 1 in bit 52 as a UINT64.
 126         assertTrue(0x0013456789ABCDEFL == (long) f.invoke(diy_fp));
 127 
 128         final long min_double64 = 0x0000000000000001L;
 129         diy_fp = asDiyFp.invoke(null, min_double64);
 130         assertEquals(-0x3FF - 52 + 1, e.invoke(diy_fp));
 131         // This is a denormal; so no hidden bit.
 132         assertTrue(1L == (long) f.invoke(diy_fp));
 133 
 134         final long max_double64 = 0x7fefffffffffffffL;
 135         diy_fp = asDiyFp.invoke(null, max_double64);
 136         assertEquals(0x7FE - 0x3FF - 52, e.invoke(diy_fp));
 137         assertTrue(0x001fffffffffffffL == (long) f.invoke(diy_fp));
 138     }
 139 
 140 
 141     @Test
 142     public void testAsNormalizedDiyFp() throws Exception {
 143         final long ordered = 0x0123456789ABCDEFL;
 144         Object diy_fp = asNormalizedDiyFp.invoke(null, ordered);
 145         assertEquals(0x12 - 0x3FF - 52 - 11, (int) e.invoke(diy_fp));
 146         assertTrue((0x0013456789ABCDEFL << 11) == (long) f.invoke(diy_fp));
 147 
 148         final long min_double64 = 0x0000000000000001L;
 149         diy_fp = asNormalizedDiyFp.invoke(null, min_double64);
 150         assertEquals(-0x3FF - 52 + 1 - 63, e.invoke(diy_fp));
 151         // This is a denormal; so no hidden bit.
 152         assertTrue(0x8000000000000000L == (long) f.invoke(diy_fp));
 153 
 154         final long max_double64 = 0x7fefffffffffffffL;
 155         diy_fp = asNormalizedDiyFp.invoke(null, max_double64);
 156         assertEquals(0x7FE - 0x3FF - 52 - 11, e.invoke(diy_fp));
 157         assertTrue((0x001fffffffffffffL << 11) == (long) f.invoke(diy_fp));
 158     }
 159 
 160 
 161     @Test
 162     public void testIsDenormal() throws Exception {
 163         final long min_double64 = 0x0000000000000001L;
 164         assertTrue((boolean) isDenormal.invoke(null, min_double64));
 165         long bits = 0x000FFFFFFFFFFFFFL;
 166         assertTrue((boolean) isDenormal.invoke(null, bits));
 167         bits = 0x0010000000000000L;
 168         assertTrue(!(boolean) isDenormal.invoke(null, bits));
 169     }
 170 
 171     @Test
 172     public void testIsSpecial() throws Exception {
 173         assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 174         assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 175         assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
 176         final long bits = 0xFFF1234500000000L;
 177         assertTrue((boolean) isSpecial.invoke(null, bits));
 178         // Denormals are not special:
 179         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 5e-324)));
 180         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -5e-324)));
 181         // And some random numbers:
 182         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 0.0)));
 183         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -0.0)));
 184         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1.0)));
 185         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1.0)));
 186         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1000000.0)));
 187         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1000000.0)));
 188         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1e23)));
 189         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1e23)));
 190         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1.7976931348623157e308)));
 191         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1.7976931348623157e308)));
 192     }
 193 
 194         @Test
 195     public void testIsInfinite() throws Exception {
 196         assertTrue((boolean) isInfinite.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 197         assertTrue((boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 198         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
 199         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, 0.0)));
 200         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -0.0)));
 201         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, 1.0)));
 202         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -1.0)));
 203         final long min_double64 = 0x0000000000000001L;
 204         assertTrue(!(boolean) isInfinite.invoke(null, min_double64));
 205     }
 206 
 207         @Test
 208     public void testIsNan() throws Exception {
 209         assertTrue((boolean) isNaN.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
 210         final long other_nan = 0xFFFFFFFF00000001L;
 211         assertTrue((boolean) isNaN.invoke(null, other_nan));
 212         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 213         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 214         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, 0.0)));
 215         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -0.0)));
 216         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, 1.0)));
 217         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -1.0)));
 218         final long min_double64 = 0x0000000000000001L;
 219         assertTrue(!(boolean) isNaN.invoke(null, min_double64));
 220     }
 221 
 222     @Test
 223     public void testSign() throws Exception {
 224         assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, 1.0)));
 225         assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 226         assertEquals(-1, (int) sign.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 227         assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, 0.0)));
 228         assertEquals(-1, (int) sign.invoke(null, doubleToLong.invoke(null, -0.0)));
 229         final long min_double64 = 0x0000000000000001L;
 230         assertEquals(1, (int) sign.invoke(null, min_double64));
 231     }
 232 
 233     @Test
 234     public void testNormalizedBoundaries() throws Exception {
 235         final Object boundary_plus = DiyFpCtor.newInstance();
 236         final Object boundary_minus = DiyFpCtor.newInstance();
 237         Object diy_fp = asNormalizedDiyFp.invoke(null, doubleToLong.invoke(null, 1.5));
 238         normalizedBoundaries.invoke(null, doubleToLong.invoke(null, 1.5), boundary_minus, boundary_plus);
 239         assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
 240         assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
 241         // 1.5 does not have a significand of the form 2^p (for some p).
 242         // Therefore its boundaries are at the same distance.
 243         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 244         assertTrue((1 << 10) == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 245 
 246         diy_fp =asNormalizedDiyFp.invoke(null, doubleToLong.invoke(null, 1.0));
 247         normalizedBoundaries.invoke(null, doubleToLong.invoke(null, 1.0), boundary_minus, boundary_plus);
 248         assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
 249         assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
 250         // 1.0 does have a significand of the form 2^p (for some p).
 251         // Therefore its lower boundary is twice as close as the upper boundary.
 252         assertTrue((long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp) > (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 253         assertTrue((1L << 9) == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 254         assertTrue((1L << 10) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 255 
 256         final long min_double64 = 0x0000000000000001L;
 257         diy_fp = asNormalizedDiyFp.invoke(null, min_double64);
 258         normalizedBoundaries.invoke(null, min_double64, boundary_minus, boundary_plus);
 259         assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
 260         assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
 261         // min-value does not have a significand of the form 2^p (for some p).
 262         // Therefore its boundaries are at the same distance.
 263         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 264         // Denormals have their boundaries much closer.
 265         assertTrue(1L << 62 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 266 
 267         final long smallest_normal64 = 0x0010000000000000L;
 268         diy_fp = asNormalizedDiyFp.invoke(null, smallest_normal64);
 269         normalizedBoundaries.invoke(null, smallest_normal64, boundary_minus, boundary_plus);
 270         assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
 271         assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
 272         // Even though the significand is of the form 2^p (for some p), its boundaries
 273         // are at the same distance. (This is the only exception).
 274         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 275         assertTrue(1L << 10 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 276 
 277         final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
 278         diy_fp = asNormalizedDiyFp.invoke(null, largest_denormal64);
 279         normalizedBoundaries.invoke(null, largest_denormal64, boundary_minus, boundary_plus);
 280         assertEquals(e.invoke(diy_fp),  e.invoke(boundary_minus));
 281         assertEquals(e.invoke(diy_fp),  e.invoke(boundary_plus));
 282         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 283         assertTrue(1L << 11 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 284 
 285         final long max_double64 = 0x7fefffffffffffffL;
 286         diy_fp = asNormalizedDiyFp.invoke(null, max_double64);
 287         normalizedBoundaries.invoke(null, max_double64, boundary_minus, boundary_plus);
 288         assertEquals(e.invoke(diy_fp),  e.invoke(boundary_minus));
 289         assertEquals(e.invoke(diy_fp),  e.invoke(boundary_plus));
 290         // max-value does not have a significand of the form 2^p (for some p).
 291         // Therefore its boundaries are at the same distance.
 292         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 293         assertTrue(1L << 10 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 294     }
 295 
 296     @Test
 297     public void testNextDouble() throws Exception {
 298         assertEquals(4e-324, (double) nextDouble.invoke(null, doubleToLong.invoke(null, 0.0)));
 299         assertEquals(0.0, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -0.0)));
 300         assertEquals(-0.0, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -4e-324)));
 301         assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, nextDouble.invoke(null, doubleToLong.invoke(null, -0.0)))) > 0);
 302         assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, nextDouble.invoke(null, doubleToLong.invoke(null, -4e-324)))) < 0);
 303         final long d0 = (long) doubleToLong.invoke(null, -4e-324);
 304         final long d1 = (long) doubleToLong.invoke(null, nextDouble.invoke(null, d0));
 305         final long d2 = (long) doubleToLong.invoke(null, nextDouble.invoke(null, d1));
 306         assertEquals(-0.0, value.invoke(null, d1));
 307         assertTrue((int) sign.invoke(null, d1) < 0);
 308         assertEquals(0.0, value.invoke(null, d2));
 309         assertTrue((int) sign.invoke(null, d2) > 0);
 310         assertEquals(4e-324, (double) nextDouble.invoke(null, d2));
 311         assertEquals(-1.7976931348623157e308, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 312         assertEquals(Infinity.invoke(null), (double) nextDouble.invoke(null, 0x7fefffffffffffffL));
 313     }
 314 
 315     @Test
 316     public void testPreviousDouble() throws Exception {
 317         assertEquals(0.0, (double) previousDouble.invoke(null, doubleToLong.invoke(null, 4e-324)));
 318         assertEquals(-0.0, (double) previousDouble.invoke(null, doubleToLong.invoke(null, 0.0)));
 319         assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, previousDouble.invoke(null, doubleToLong.invoke(null, 0.0)))) < 0);
 320         assertEquals(-4e-324, previousDouble.invoke(null, doubleToLong.invoke(null, -0.0)));
 321         final long d0 = (long) doubleToLong.invoke(null, 4e-324);
 322         final long d1 = (long) doubleToLong.invoke(null, previousDouble.invoke(null, d0));
 323         final long d2 = (long) doubleToLong.invoke(null, previousDouble.invoke(null, d1));
 324         assertEquals(0.0, value.invoke(null, d1));
 325         assertTrue((int) sign.invoke(null, d1) > 0);
 326         assertEquals(-0.0, value.invoke(null, d2));
 327         assertTrue((int) sign.invoke(null, d2) < 0);
 328         assertEquals(-4e-324, (double) previousDouble.invoke(null, d2));
 329         assertEquals(1.7976931348623157e308, (double) previousDouble.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 330         assertEquals(-(double) Infinity.invoke(null), (double) previousDouble.invoke(null, 0xffefffffffffffffL));
 331     }
 332 
 333 }