View Javadoc
1   /* Copyright 2018 Ulf Adams
2    * Licensed to the Hipparchus project under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The Hipparchus project licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by Ulf Adams in project
20   * https://github.com/ulfjack/ryu
21   * It has been modified by the Hipparchus project.
22   */
23  package org.hipparchus.util;
24  
25  import static org.junit.Assert.assertEquals;
26  
27  import org.hipparchus.random.RandomGenerator;
28  import org.hipparchus.random.Well19937a;
29  import org.junit.Assert;
30  import org.junit.Test;
31  
32  public class RyuDoubleTest {
33  
34      private void assertD2sEquals(String expected, double f) {
35          assertEquals(expected, RyuDouble.doubleToString(f));
36      }
37  
38      @Test
39      public void simpleCases() {
40          assertD2sEquals("0.0", 0);
41          assertD2sEquals("-0.0", Double.longBitsToDouble(0x8000000000000000L));
42          assertD2sEquals("1.0", 1.0d);
43          assertD2sEquals("-1.0", -1.0d);
44          assertD2sEquals("NaN", Double.NaN);
45          assertD2sEquals("Infinity", Double.POSITIVE_INFINITY);
46          assertD2sEquals("-Infinity", Double.NEGATIVE_INFINITY);
47      }
48  
49      @Test
50      public void switchToSubnormal() {
51          assertD2sEquals("2.2250738585072014E-308", Double.longBitsToDouble(0x0010000000000000L));
52      }
53  
54      /**
55       * Floating point values in the range 1.0E-3 <= x < 1.0E7 have to be printed
56       * without exponent. This test checks the values at those boundaries.
57       */
58      @Test
59      public void boundaryConditions() {
60          // x = 1.0E7
61          assertD2sEquals("1.0E7", 1.0E7d);
62          // x < 1.0E7
63          assertD2sEquals("9999999.999999998", 9999999.999999998d);
64          // x = 1.0E-3
65          assertD2sEquals("0.001", 0.001d);
66          // x < 1.0E-3
67          assertD2sEquals("9.999999999999998E-4", 0.0009999999999999998d);
68      }
69  
70      @Test
71      public void test10Power() {
72          for (int e = -20; e < -3; ++e) {
73              assertD2sEquals("1.0E" + e, FastMath.pow(10.0, e));
74          }
75          assertD2sEquals("0.001",     FastMath.pow(10.0, -3));
76          assertD2sEquals("0.01",      FastMath.pow(10.0, -2));
77          assertD2sEquals("0.1",       FastMath.pow(10.0, -1));
78          assertD2sEquals("1.0",       FastMath.pow(10.0,  0));
79          assertD2sEquals("10.0",      FastMath.pow(10.0,  1));
80          assertD2sEquals("100.0",     FastMath.pow(10.0,  2));
81          assertD2sEquals("1000.0",    FastMath.pow(10.0,  3));
82          assertD2sEquals("10000.0",   FastMath.pow(10.0,  4));
83          assertD2sEquals("100000.0",  FastMath.pow(10.0,  5));
84          assertD2sEquals("1000000.0", FastMath.pow(10.0,  6));
85          for (int e = 7; e < 20; ++e) {
86              assertD2sEquals("1.0E" + e, FastMath.pow(10.0, e));
87          }
88      }
89  
90      @Test
91      public void minAndMax() {
92          assertD2sEquals("1.7976931348623157E308", Double.longBitsToDouble(0x7fefffffffffffffL));
93          assertD2sEquals("4.9E-324", Double.longBitsToDouble(1));
94      }
95  
96      @Test
97      public void roundingModeEven() {
98          assertD2sEquals("-2.109808898695963E16", -2.109808898695963E16);
99      }
100 
101     @Test
102     public void regressionTest() {
103         assertD2sEquals("4.940656E-318", 4.940656E-318d);
104         assertD2sEquals("1.18575755E-316", 1.18575755E-316d);
105         assertD2sEquals("2.989102097996E-312", 2.989102097996E-312d);
106         assertD2sEquals("9.0608011534336E15", 9.0608011534336E15d);
107         assertD2sEquals("4.708356024711512E18", 4.708356024711512E18);
108         assertD2sEquals("9.409340012568248E18", 9.409340012568248E18);
109         // This number naively requires 65 bit for the intermediate results if we reduce the lookup
110         // table by half. This checks that we don't loose any information in that case.
111         assertD2sEquals("1.8531501765868567E21", 1.8531501765868567E21);
112         assertD2sEquals("-3.347727380279489E33", -3.347727380279489E33);
113         // Discovered by Andriy Plokhotnyuk, see #29.
114         assertD2sEquals("1.9430376160308388E16", 1.9430376160308388E16);
115         assertD2sEquals("-6.9741824662760956E19", -6.9741824662760956E19);
116         assertD2sEquals("4.3816050601147837E18", 4.3816050601147837E18);
117     }
118 
119     @Test
120     public void testMantissaSize() {
121         assertEquals("1.0",                    RyuDouble.doubleToString(1.0,                -20, 20));
122         assertEquals("21.0",                   RyuDouble.doubleToString(21.0,               -20, 20));
123         assertEquals("321.0",                  RyuDouble.doubleToString(321.0,              -20, 20));
124         assertEquals("4321.0",                 RyuDouble.doubleToString(4321.0,             -20, 20));
125         assertEquals("54321.0",                RyuDouble.doubleToString(54321.0,            -20, 20));
126         assertEquals("654321.0",               RyuDouble.doubleToString(654321.0,           -20, 20));
127         assertEquals("7654321.0",              RyuDouble.doubleToString(7654321.0,          -20, 20));
128         assertEquals("87654321.0",             RyuDouble.doubleToString(87654321.0,         -20, 20));
129         assertEquals("987654321.0",            RyuDouble.doubleToString(987654321.0,        -20, 20));
130         assertEquals("1987654321.0",           RyuDouble.doubleToString(1987654321.0,       -20, 20));
131         assertEquals("21987654321.0",          RyuDouble.doubleToString(21987654321.0,      -20, 20));
132         assertEquals("321987654321.0",         RyuDouble.doubleToString(321987654321.0,     -20, 20));
133         assertEquals("4321987654321.0",        RyuDouble.doubleToString(4321987654321.0,    -20, 20));
134         assertEquals("54321987654321.0",       RyuDouble.doubleToString(54321987654321.0,   -20, 20));
135         assertEquals("654321987654321.0",      RyuDouble.doubleToString(654321987654321.0,  -20, 20));
136         assertEquals("7654321987654321.0",     RyuDouble.doubleToString(7654321987654321.0, -20, 20));
137     }
138 
139     @Test
140     public void testStandard() {
141         RandomGenerator random = new Well19937a(0xca939d6d82eff2d6l);
142         for (int i = 0; i < 1000000; ++i) {
143             final long   l = random.nextLong();
144             final double d = Double.longBitsToDouble(l);
145             final String s1 = Double.toString(d);
146             final String s2 = RyuDouble.doubleToString(d);
147             if (!s1.equals(s2)) {
148                 // in about 3% cases, standard Double.toString and Ryū give different results
149                 // in theses cases, Ryū finds either an equal or shorter representation
150                 // and both representations are information preserving (i.e. they parse to the same number)
151                 Assert.assertTrue(s2.length() <= s1.length());
152                 Assert.assertEquals(l, Double.doubleToRawLongBits(Double.parseDouble(s1)));
153                 Assert.assertEquals(l, Double.doubleToRawLongBits(Double.parseDouble(s2)));
154             }
155         }
156     }
157 
158 }