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