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