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 java.io.BufferedReader;
36import java.io.InputStreamReader;
37import java.util.concurrent.atomic.AtomicBoolean;
38import java.util.concurrent.atomic.AtomicInteger;
39import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
40import jdk.nashorn.internal.runtime.doubleconv.DtoaBuffer;
41
42import org.testng.annotations.Test;
43
44import static org.testng.Assert.assertEquals;
45import static org.testng.Assert.assertTrue;
46
47/**
48 * FastDtoa tests
49 */
50@SuppressWarnings("javadoc")
51public class FastDtoaTest {
52
53    final static private int kBufferSize = 100;
54
55    // Removes trailing '0' digits.
56    // Can return the empty string if all digits are 0.
57    private static String trimRepresentation(final String representation) {
58        final int len = representation.length();
59        int i;
60        for (i = len - 1; i >= 0; --i) {
61            if (representation.charAt(i) != '0') break;
62        }
63        return representation.substring(0, i + 1);
64    }
65
66    @Test
67    public void testFastShortestVarious() {
68        final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
69        boolean status;
70
71        final double min_double = 5e-324;
72        status = DoubleConversion.fastDtoaShortest(min_double, buffer);
73        assertTrue(status);
74        assertEquals("5", buffer.getRawDigits());
75        assertEquals(-323, buffer.getDecimalPoint());
76        buffer.reset();
77
78        final double max_double = 1.7976931348623157e308;
79        status = DoubleConversion.fastDtoaShortest(max_double, buffer);
80        assertTrue(status);
81        assertEquals("17976931348623157", buffer.getRawDigits());
82        assertEquals(309, buffer.getDecimalPoint());
83        buffer.reset();
84
85
86        status = DoubleConversion.fastDtoaShortest(4294967272.0, buffer);
87        assertTrue(status);
88        assertEquals("4294967272", buffer.getRawDigits());
89        assertEquals(10, buffer.getDecimalPoint());
90        buffer.reset();
91
92
93        status = DoubleConversion.fastDtoaShortest(4.1855804968213567e298, buffer);
94        assertTrue(status);
95        assertEquals("4185580496821357", buffer.getRawDigits());
96        assertEquals(299, buffer.getDecimalPoint());
97        buffer.reset();
98
99        status = DoubleConversion.fastDtoaShortest(5.5626846462680035e-309, buffer);
100        assertTrue(status);
101        assertEquals("5562684646268003", buffer.getRawDigits());
102        assertEquals(-308, buffer.getDecimalPoint());
103        buffer.reset();
104
105        status = DoubleConversion.fastDtoaShortest(2147483648.0, buffer);
106        assertTrue(status);
107        assertEquals("2147483648", buffer.getRawDigits());
108        assertEquals(10, buffer.getDecimalPoint());
109        buffer.reset();
110
111        status = DoubleConversion.fastDtoaShortest(3.5844466002796428e+298, buffer);
112        if (status) {  // Not all FastDtoa variants manage to compute this number.
113            assertEquals("35844466002796428", buffer.getRawDigits());
114            assertEquals(299, buffer.getDecimalPoint());
115        }
116        buffer.reset();
117
118        final long smallest_normal64 = 0x0010000000000000L;
119        double v = Double.longBitsToDouble(smallest_normal64);
120        status = DoubleConversion.fastDtoaShortest(v, buffer);
121        if (status) {
122            assertEquals("22250738585072014", buffer.getRawDigits());
123            assertEquals(-307, buffer.getDecimalPoint());
124        }
125        buffer.reset();
126
127        final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
128        v = Double.longBitsToDouble(largest_denormal64);
129        status = DoubleConversion.fastDtoaShortest(v, buffer);
130        if (status) {
131            assertEquals("2225073858507201", buffer.getRawDigits());
132            assertEquals(-307, buffer.getDecimalPoint());
133        }
134        buffer.reset();
135    }
136
137    @Test
138    public void testFastPrecisionVarious() {
139        final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
140        boolean status;
141
142        status = DoubleConversion.fastDtoaCounted(1.0, 3, buffer);
143        assertTrue(status);
144        assertTrue(3 >= buffer.getLength());
145        assertEquals("1", trimRepresentation(buffer.getRawDigits()));
146        assertEquals(1, buffer.getDecimalPoint());
147        buffer.reset();
148
149        status = DoubleConversion.fastDtoaCounted(1.5, 10, buffer);
150        if (status) {
151            assertTrue(10 >= buffer.getLength());
152            assertEquals("15", trimRepresentation(buffer.getRawDigits()));
153            assertEquals(1, buffer.getDecimalPoint());
154        }
155        buffer.reset();
156
157        final double min_double = 5e-324;
158        status = DoubleConversion.fastDtoaCounted(min_double, 5, buffer);
159        assertTrue(status);
160        assertEquals("49407", buffer.getRawDigits());
161        assertEquals(-323, buffer.getDecimalPoint());
162        buffer.reset();
163
164        final double max_double = 1.7976931348623157e308;
165        status = DoubleConversion.fastDtoaCounted(max_double, 7, buffer);
166        assertTrue(status);
167        assertEquals("1797693", buffer.getRawDigits());
168        assertEquals(309, buffer.getDecimalPoint());
169        buffer.reset();
170
171        status = DoubleConversion.fastDtoaCounted(4294967272.0, 14, buffer);
172        if (status) {
173            assertTrue(14 >= buffer.getLength());
174            assertEquals("4294967272", trimRepresentation(buffer.getRawDigits()));
175            assertEquals(10, buffer.getDecimalPoint());
176        }
177        buffer.reset();
178
179        status = DoubleConversion.fastDtoaCounted(4.1855804968213567e298, 17, buffer);
180        assertTrue(status);
181        assertEquals("41855804968213567", buffer.getRawDigits());
182        assertEquals(299, buffer.getDecimalPoint());
183        buffer.reset();
184
185        status = DoubleConversion.fastDtoaCounted(5.5626846462680035e-309, 1, buffer);
186        assertTrue(status);
187        assertEquals("6", buffer.getRawDigits());
188        assertEquals(-308, buffer.getDecimalPoint());
189        buffer.reset();
190
191        status = DoubleConversion.fastDtoaCounted(2147483648.0, 5, buffer);
192        assertTrue(status);
193        assertEquals("21475", buffer.getRawDigits());
194        assertEquals(10, buffer.getDecimalPoint());
195        buffer.reset();
196
197        status = DoubleConversion.fastDtoaCounted(3.5844466002796428e+298, 10, buffer);
198        assertTrue(status);
199        assertTrue(10 >= buffer.getLength());
200        assertEquals("35844466", trimRepresentation(buffer.getRawDigits()));
201        assertEquals(299, buffer.getDecimalPoint());
202        buffer.reset();
203
204        final long smallest_normal64 = 0x0010000000000000L;
205        double v = Double.longBitsToDouble(smallest_normal64);
206        status = DoubleConversion.fastDtoaCounted(v, 17, buffer);
207        assertTrue(status);
208        assertEquals("22250738585072014", buffer.getRawDigits());
209        assertEquals(-307, buffer.getDecimalPoint());
210        buffer.reset();
211
212        final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
213        v = Double.longBitsToDouble(largest_denormal64);
214        status = DoubleConversion.fastDtoaCounted(v, 17, buffer);
215        assertTrue(status);
216        assertTrue(20 >= buffer.getLength());
217        assertEquals("22250738585072009", trimRepresentation(buffer.getRawDigits()));
218        assertEquals(-307, buffer.getDecimalPoint());
219        buffer.reset();
220
221        v = 3.3161339052167390562200598e-237;
222        status = DoubleConversion.fastDtoaCounted(v, 18, buffer);
223        assertTrue(status);
224        assertEquals("331613390521673906", buffer.getRawDigits());
225        assertEquals(-236, buffer.getDecimalPoint());
226        buffer.reset();
227
228        v = 7.9885183916008099497815232e+191;
229        status = DoubleConversion.fastDtoaCounted(v, 4, buffer);
230        assertTrue(status);
231        assertEquals("7989", buffer.getRawDigits());
232        assertEquals(192, buffer.getDecimalPoint());
233        buffer.reset();
234    }
235
236
237    @Test
238    public void testFastShortest() {
239        final AtomicInteger total = new AtomicInteger();
240        final AtomicInteger succeeded = new AtomicInteger();
241        final AtomicBoolean neededMaxLength = new AtomicBoolean();
242
243        new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-shortest.txt")))
244                .lines()
245                .forEach(line -> {
246                    if (line.isEmpty() || line.startsWith("//")) {
247                        return; // comment or empty line
248                    }
249                    final String[] tokens = line.split(",\\s+");
250                    assertEquals(tokens.length, 3, "*" + line + "*");
251                    final double v = Double.parseDouble(tokens[0]);
252                    final String str = tokens[1].replace('"', ' ').trim();;
253                    final int point = Integer.parseInt(tokens[2]);
254                    final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
255                    total.getAndIncrement();
256
257                    if (DoubleConversion.fastDtoaShortest(v, buffer)) {
258                        assertEquals(str, buffer.getRawDigits());
259                        assertEquals(point, buffer.getDecimalPoint());
260                        succeeded.getAndIncrement();
261                        if (buffer.getLength() == DtoaBuffer.kFastDtoaMaximalLength) {
262                            neededMaxLength.set(true);
263                        }
264                    }
265                });
266
267        assertTrue(succeeded.get() * 1.0 / total.get() > 0.99);
268        assertTrue(neededMaxLength.get());
269        // Additional constraints: Make sure these numbers are exactly the same as in C++ version
270        assertEquals(succeeded.get(), 99440);
271        assertEquals(total.get(), 100000);
272    }
273
274    @Test
275    public void testFastPrecision() {
276        final AtomicInteger total = new AtomicInteger();
277        final AtomicInteger succeeded = new AtomicInteger();
278        // Count separately for entries with less than 15 requested digits.
279        final AtomicInteger  succeeded_15  = new AtomicInteger();
280        final AtomicInteger  total_15 = new AtomicInteger();
281
282        new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-precision.txt")))
283                .lines()
284                .forEach(line -> {
285                    if (line.isEmpty() || line.startsWith("//")) {
286                        return; // comment or empty line
287                    }
288                    final String[] tokens = line.split(",\\s+");
289                    assertEquals(tokens.length, 4);
290                    final double v = Double.parseDouble(tokens[0]);
291                    final int digits = Integer.parseInt(tokens[1]);
292                    final String str = tokens[2].replace('"', ' ').trim();
293                    final int point = Integer.parseInt(tokens[3]);
294                    final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
295                    total.getAndIncrement();
296                    if (digits <= 15) {
297                        total_15.getAndIncrement();
298                    }
299
300                    if (DoubleConversion.fastDtoaCounted(v, digits, buffer)) {
301                        assertEquals(str, trimRepresentation(buffer.getRawDigits()));
302                        assertEquals(point, buffer.getDecimalPoint());
303                        succeeded.getAndIncrement();
304                        if (digits <= 15) {
305                            succeeded_15.getAndIncrement();
306                        }
307                    }
308                });
309
310        // The precomputed numbers contain many entries with many requested
311        // digits. These have a high failure rate and we therefore expect a lower
312        // success rate than for the shortest representation.
313        assertTrue(succeeded.get() * 1.0 / total.get() > 0.85);
314        // However with less than 15 digits almost the algorithm should almost always
315        // succeed.
316        assertTrue(succeeded_15.get() * 1.0 / total_15.get() > 0.9999);
317        // Additional constraints: Make sure these numbers are exactly the same as in C++ version
318        assertEquals(succeeded.get(), 86866);
319        assertEquals(total.get(), 100000);
320        assertEquals(succeeded_15.get(), 71328);
321        assertEquals(total_15.get(), 71330);
322    }
323
324}
325