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  * Ieee class tests
  45  *
  46  * @test
  47  * @run testng jdk.nashorn.internal.runtime.doubleconv.test.IeeeTest
  48  */
  49 @SuppressWarnings({"unchecked", "javadoc"})
  50 public class IeeeDoubleTest {
  51 
  52     static final Method asDiyFp;
  53     static final Method asNormalizedDiyFp;
  54     static final Method doubleToLong;
  55     static final Method longToDouble;
  56     static final Method isDenormal;
  57     static final Method isSpecial;
  58     static final Method isInfinite;
  59     static final Method isNaN;
  60     static final Method value;
  61     static final Method sign;
  62     static final Method nextDouble;
  63     static final Method previousDouble;
  64     static final Method normalizedBoundaries;
  65     static final Method Infinity;
  66     static final Method NaN;
  67     static final Method f;
  68     static final Method e;
  69     static final Constructor<?> DiyFpCtor;
  70 
  71     static {
  72         try {
  73             final Class<?> IeeeDouble = Class.forName("jdk.nashorn.internal.runtime.doubleconv.IeeeDouble");
  74             final Class DiyFp = Class.forName("jdk.nashorn.internal.runtime.doubleconv.DiyFp");
  75             asDiyFp = method(IeeeDouble, "asDiyFp", long.class);
  76             asNormalizedDiyFp = method(IeeeDouble, "asNormalizedDiyFp", long.class);
  77             doubleToLong = method(IeeeDouble, "doubleToLong", double.class);
  78             longToDouble = method(IeeeDouble, "longToDouble", long.class);
  79             isDenormal = method(IeeeDouble, "isDenormal", long.class);
  80             isSpecial = method(IeeeDouble, "isSpecial", long.class);
  81             isInfinite = method(IeeeDouble, "isInfinite", long.class);
  82             isNaN = method(IeeeDouble, "isNaN", long.class);
  83             value = method(IeeeDouble, "value", long.class);
  84             sign = method(IeeeDouble, "sign", long.class);
  85             nextDouble = method(IeeeDouble, "nextDouble", long.class);
  86             previousDouble = method(IeeeDouble, "previousDouble", long.class);
  87             Infinity = method(IeeeDouble, "Infinity");
  88             NaN = method(IeeeDouble, "NaN");
  89             normalizedBoundaries = method(IeeeDouble, "normalizedBoundaries", long.class, DiyFp, DiyFp);
  90             DiyFpCtor = DiyFp.getDeclaredConstructor();
  91             DiyFpCtor.setAccessible(true);
  92             f = method(DiyFp, "f");
  93             e = method(DiyFp, "e");
  94         } catch (final Exception e) {
  95             throw new RuntimeException(e);
  96         }
  97     }
  98 
  99     private static Method method(final Class<?> clazz, final String name, final Class<?>... params) throws NoSuchMethodException {
 100         final Method m = clazz.getDeclaredMethod(name, params);
 101         m.setAccessible(true);
 102         return m;
 103     }
 104 
 105     @Test
 106     public void testUint64Conversions() throws Exception {
 107         // Start by checking the byte-order.
 108         final long ordered = 0x0123456789ABCDEFL;
 109         assertEquals(3512700564088504e-318, value.invoke(null, ordered));
 110 
 111         final long min_double64 = 0x0000000000000001L;
 112         assertEquals(5e-324, value.invoke(null, min_double64));
 113 
 114         final long max_double64 = 0x7fefffffffffffffL;
 115         assertEquals(1.7976931348623157e308, value.invoke(null, max_double64));
 116     }
 117 
 118 
 119     @Test
 120     public void testDoubleAsDiyFp() throws Exception {
 121         final long ordered = 0x0123456789ABCDEFL;
 122         Object diy_fp = asDiyFp.invoke(null, ordered);
 123         assertEquals(0x12 - 0x3FF - 52, e.invoke(diy_fp));
 124         // The 52 mantissa bits, plus the implicit 1 in bit 52 as a UINT64.
 125         assertTrue(0x0013456789ABCDEFL == (long) f.invoke(diy_fp));
 126 
 127         final long min_double64 = 0x0000000000000001L;
 128         diy_fp = asDiyFp.invoke(null, min_double64);
 129         assertEquals(-0x3FF - 52 + 1, e.invoke(diy_fp));
 130         // This is a denormal; so no hidden bit.
 131         assertTrue(1L == (long) f.invoke(diy_fp));
 132 
 133         final long max_double64 = 0x7fefffffffffffffL;
 134         diy_fp = asDiyFp.invoke(null, max_double64);
 135         assertEquals(0x7FE - 0x3FF - 52, e.invoke(diy_fp));
 136         assertTrue(0x001fffffffffffffL == (long) f.invoke(diy_fp));
 137     }
 138 
 139 
 140     @Test
 141     public void testAsNormalizedDiyFp() throws Exception {
 142         final long ordered = 0x0123456789ABCDEFL;
 143         Object diy_fp = asNormalizedDiyFp.invoke(null, ordered);
 144         assertEquals(0x12 - 0x3FF - 52 - 11, (int) e.invoke(diy_fp));
 145         assertTrue((0x0013456789ABCDEFL << 11) == (long) f.invoke(diy_fp));
 146 
 147         final long min_double64 = 0x0000000000000001L;
 148         diy_fp = asNormalizedDiyFp.invoke(null, min_double64);
 149         assertEquals(-0x3FF - 52 + 1 - 63, e.invoke(diy_fp));
 150         // This is a denormal; so no hidden bit.
 151         assertTrue(0x8000000000000000L == (long) f.invoke(diy_fp));
 152 
 153         final long max_double64 = 0x7fefffffffffffffL;
 154         diy_fp = asNormalizedDiyFp.invoke(null, max_double64);
 155         assertEquals(0x7FE - 0x3FF - 52 - 11, e.invoke(diy_fp));
 156         assertTrue((0x001fffffffffffffL << 11) == (long) f.invoke(diy_fp));
 157     }
 158 
 159 
 160     @Test
 161     public void testIsDenormal() throws Exception {
 162         final long min_double64 = 0x0000000000000001L;
 163         assertTrue((boolean) isDenormal.invoke(null, min_double64));
 164         long bits = 0x000FFFFFFFFFFFFFL;
 165         assertTrue((boolean) isDenormal.invoke(null, bits));
 166         bits = 0x0010000000000000L;
 167         assertTrue(!(boolean) isDenormal.invoke(null, bits));
 168     }
 169 
 170     @Test
 171     public void testIsSpecial() throws Exception {
 172         assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 173         assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 174         assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
 175         final long bits = 0xFFF1234500000000L;
 176         assertTrue((boolean) isSpecial.invoke(null, bits));
 177         // Denormals are not special:
 178         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 5e-324)));
 179         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -5e-324)));
 180         // And some random numbers:
 181         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 0.0)));
 182         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -0.0)));
 183         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1.0)));
 184         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1.0)));
 185         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1000000.0)));
 186         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1000000.0)));
 187         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1e23)));
 188         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1e23)));
 189         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1.7976931348623157e308)));
 190         assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1.7976931348623157e308)));
 191     }
 192 
 193         @Test
 194     public void testIsInfinite() throws Exception {
 195         assertTrue((boolean) isInfinite.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 196         assertTrue((boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 197         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
 198         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, 0.0)));
 199         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -0.0)));
 200         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, 1.0)));
 201         assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -1.0)));
 202         final long min_double64 = 0x0000000000000001L;
 203         assertTrue(!(boolean) isInfinite.invoke(null, min_double64));
 204     }
 205 
 206         @Test
 207     public void testIsNan() throws Exception {
 208         assertTrue((boolean) isNaN.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
 209         final long other_nan = 0xFFFFFFFF00000001L;
 210         assertTrue((boolean) isNaN.invoke(null, other_nan));
 211         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 212         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 213         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, 0.0)));
 214         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -0.0)));
 215         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, 1.0)));
 216         assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -1.0)));
 217         final long min_double64 = 0x0000000000000001L;
 218         assertTrue(!(boolean) isNaN.invoke(null, min_double64));
 219     }
 220 
 221     @Test
 222     public void testSign() throws Exception {
 223         assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, 1.0)));
 224         assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 225         assertEquals(-1, (int) sign.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 226         assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, 0.0)));
 227         assertEquals(-1, (int) sign.invoke(null, doubleToLong.invoke(null, -0.0)));
 228         final long min_double64 = 0x0000000000000001L;
 229         assertEquals(1, (int) sign.invoke(null, min_double64));
 230     }
 231 
 232     @Test
 233     public void testNormalizedBoundaries() throws Exception {
 234         Object boundary_plus = DiyFpCtor.newInstance();
 235         Object boundary_minus = DiyFpCtor.newInstance();
 236         Object diy_fp = asNormalizedDiyFp.invoke(null, doubleToLong.invoke(null, 1.5));
 237         normalizedBoundaries.invoke(null, doubleToLong.invoke(null, 1.5), boundary_minus, boundary_plus);
 238         assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
 239         assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
 240         // 1.5 does not have a significand of the form 2^p (for some p).
 241         // Therefore its boundaries are at the same distance.
 242         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 243         assertTrue((1 << 10) == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 244 
 245         diy_fp =asNormalizedDiyFp.invoke(null, doubleToLong.invoke(null, 1.0));
 246         normalizedBoundaries.invoke(null, doubleToLong.invoke(null, 1.0), boundary_minus, boundary_plus);
 247         assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
 248         assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
 249         // 1.0 does have a significand of the form 2^p (for some p).
 250         // Therefore its lower boundary is twice as close as the upper boundary.
 251         assertTrue((long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp) > (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 252         assertTrue((1L << 9) == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 253         assertTrue((1L << 10) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 254 
 255         final long min_double64 = 0x0000000000000001L;
 256         diy_fp = asNormalizedDiyFp.invoke(null, min_double64);
 257         normalizedBoundaries.invoke(null, min_double64, boundary_minus, boundary_plus);
 258         assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
 259         assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
 260         // min-value does not have a significand of the form 2^p (for some p).
 261         // Therefore its boundaries are at the same distance.
 262         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 263         // Denormals have their boundaries much closer.
 264         assertTrue(1L << 62 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 265 
 266         final long smallest_normal64 = 0x0010000000000000L;
 267         diy_fp = asNormalizedDiyFp.invoke(null, smallest_normal64);
 268         normalizedBoundaries.invoke(null, smallest_normal64, boundary_minus, boundary_plus);
 269         assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
 270         assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
 271         // Even though the significand is of the form 2^p (for some p), its boundaries
 272         // are at the same distance. (This is the only exception).
 273         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 274         assertTrue(1L << 10 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 275 
 276         final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
 277         diy_fp = asNormalizedDiyFp.invoke(null, largest_denormal64);
 278         normalizedBoundaries.invoke(null, largest_denormal64, boundary_minus, boundary_plus);
 279         assertEquals(e.invoke(diy_fp),  e.invoke(boundary_minus));
 280         assertEquals(e.invoke(diy_fp),  e.invoke(boundary_plus));
 281         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 282         assertTrue(1L << 11 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 283 
 284         final long max_double64 = 0x7fefffffffffffffL;
 285         diy_fp = asNormalizedDiyFp.invoke(null, max_double64);
 286         normalizedBoundaries.invoke(null, max_double64, boundary_minus, boundary_plus);
 287         assertEquals(e.invoke(diy_fp),  e.invoke(boundary_minus));
 288         assertEquals(e.invoke(diy_fp),  e.invoke(boundary_plus));
 289         // max-value does not have a significand of the form 2^p (for some p).
 290         // Therefore its boundaries are at the same distance.
 291         assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
 292         assertTrue(1L << 10 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
 293     }
 294 
 295     @Test
 296     public void testNextDouble() throws Exception {
 297         assertEquals(4e-324, (double) nextDouble.invoke(null, doubleToLong.invoke(null, 0.0)));
 298         assertEquals(0.0, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -0.0)));
 299         assertEquals(-0.0, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -4e-324)));
 300         assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, nextDouble.invoke(null, doubleToLong.invoke(null, -0.0)))) > 0);
 301         assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, nextDouble.invoke(null, doubleToLong.invoke(null, -4e-324)))) < 0);
 302         final long d0 = (long) doubleToLong.invoke(null, -4e-324);
 303         final long d1 = (long) doubleToLong.invoke(null, nextDouble.invoke(null, d0));
 304         final long d2 = (long) doubleToLong.invoke(null, nextDouble.invoke(null, d1));
 305         assertEquals(-0.0, value.invoke(null, d1));
 306         assertTrue((int) sign.invoke(null, d1) < 0);
 307         assertEquals(0.0, value.invoke(null, d2));
 308         assertTrue((int) sign.invoke(null, d2) > 0);
 309         assertEquals(4e-324, (double) nextDouble.invoke(null, d2));
 310         assertEquals(-1.7976931348623157e308, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
 311         assertEquals(Infinity.invoke(null), (double) nextDouble.invoke(null, 0x7fefffffffffffffL));
 312     }
 313 
 314     @Test
 315     public void testPreviousDouble() throws Exception {
 316         assertEquals(0.0, (double) previousDouble.invoke(null, doubleToLong.invoke(null, 4e-324)));
 317         assertEquals(-0.0, (double) previousDouble.invoke(null, doubleToLong.invoke(null, 0.0)));
 318         assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, previousDouble.invoke(null, doubleToLong.invoke(null, 0.0)))) < 0);
 319         assertEquals(-4e-324, previousDouble.invoke(null, doubleToLong.invoke(null, -0.0)));
 320         final long d0 = (long) doubleToLong.invoke(null, 4e-324);
 321         final long d1 = (long) doubleToLong.invoke(null, previousDouble.invoke(null, d0));
 322         final long d2 = (long) doubleToLong.invoke(null, previousDouble.invoke(null, d1));
 323         assertEquals(0.0, value.invoke(null, d1));
 324         assertTrue((int) sign.invoke(null, d1) > 0);
 325         assertEquals(-0.0, value.invoke(null, d2));
 326         assertTrue((int) sign.invoke(null, d2) < 0);
 327         assertEquals(-4e-324, (double) previousDouble.invoke(null, d2));
 328         assertEquals(1.7976931348623157e308, (double) previousDouble.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
 329         assertEquals(-(double) Infinity.invoke(null), (double) previousDouble.invoke(null, 0xffefffffffffffffL));
 330     }
 331 
 332 }