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 java.io.BufferedReader;
  36 import java.io.InputStreamReader;
  37 import java.util.concurrent.atomic.AtomicBoolean;
  38 import java.util.concurrent.atomic.AtomicInteger;
  39 import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
  40 import jdk.nashorn.internal.runtime.doubleconv.DtoaBuffer;
  41 
  42 import org.testng.annotations.Test;
  43 
  44 import static org.testng.Assert.assertEquals;
  45 import static org.testng.Assert.assertTrue;
  46 
  47 /**
  48  * FastDtoa tests
  49  */
  50 @SuppressWarnings("javadoc")
  51 public 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 }