• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) 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 ASF 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  *     http://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 #define LOG_TAG "RealToString"
19 
20 #include <string.h>
21 #include <math.h>
22 #include <stdlib.h>
23 
24 #include "JNIHelp.h"
25 #include "JniConstants.h"
26 #include "ScopedLocalRef.h"
27 #include "ScopedPrimitiveArray.h"
28 #include "cbigint.h"
29 
30 #define INV_LOG_OF_TEN_BASE_2 (0.30102999566398114) /* Local */
31 
32 /*NB the Number converter methods are synchronized so it is possible to
33  *have global data for use by bigIntDigitGenerator */
34 #define RM_SIZE 21     /* Local. */
35 #define STemp_SIZE 22  /* Local. */
36 
37 /* The algorithm for this particular function can be found in:
38  *
39  *      Printing Floating-Point Numbers Quickly and Accurately, Robert
40  *      G. Burger, and R. Kent Dybvig, Programming Language Design and
41  *      Implementation (PLDI) 1996, pp.108-116.
42  *
43  * The previous implementation of this function combined m+ and m- into
44  * one single M which caused some inaccuracy of the last digit. The
45  * particular case below shows this inaccuracy:
46  *
47  *       System.out.println(new Double((1.234123412431233E107)).toString());
48  *       System.out.println(new Double((1.2341234124312331E107)).toString());
49  *       System.out.println(new Double((1.2341234124312332E107)).toString());
50  *
51  *       outputs the following:
52  *
53  *           1.234123412431233E107
54  *           1.234123412431233E107
55  *           1.234123412431233E107
56  *
57  *       instead of:
58  *
59  *           1.234123412431233E107
60  *           1.2341234124312331E107
61  *           1.2341234124312331E107
62  *
63  */
RealToString_bigIntDigitGenerator(JNIEnv * env,jobject obj,jlong f,jint e,jboolean isDenormalized,jint p)64 void RealToString_bigIntDigitGenerator(JNIEnv* env, jobject obj, jlong f, jint e,
65         jboolean isDenormalized, jint p) {
66   int RLength, SLength, TempLength, mplus_Length, mminus_Length;
67   int high, low, i;
68   jint k, firstK, U;
69 
70   uint64_t R[RM_SIZE], S[STemp_SIZE], mplus[RM_SIZE], mminus[RM_SIZE], Temp[STemp_SIZE];
71 
72   memset (R     , 0, RM_SIZE    * sizeof (uint64_t));
73   memset (S     , 0, STemp_SIZE * sizeof (uint64_t));
74   memset (mplus , 0, RM_SIZE    * sizeof (uint64_t));
75   memset (mminus, 0, RM_SIZE    * sizeof (uint64_t));
76   memset (Temp  , 0, STemp_SIZE * sizeof (uint64_t));
77 
78   if (e >= 0)
79     {
80       *R = f;
81       *mplus = *mminus = 1;
82       simpleShiftLeftHighPrecision (mminus, RM_SIZE, e);
83       if (f != (2 << (p - 1)))
84         {
85           simpleShiftLeftHighPrecision (R, RM_SIZE, e + 1);
86           *S = 2;
87           /*
88            * m+ = m+ << e results in 1.0e23 to be printed as
89            * 0.9999999999999999E23
90            * m+ = m+ << e+1 results in 1.0e23 to be printed as
91            * 1.0e23 (caused too much rounding)
92            *      470fffffffffffff = 2.0769187434139308E34
93            *      4710000000000000 = 2.076918743413931E34
94            */
95           simpleShiftLeftHighPrecision (mplus, RM_SIZE, e);
96         }
97       else
98         {
99           simpleShiftLeftHighPrecision (R, RM_SIZE, e + 2);
100           *S = 4;
101           simpleShiftLeftHighPrecision (mplus, RM_SIZE, e + 1);
102         }
103     }
104   else
105     {
106       if (isDenormalized || (f != (2 << (p - 1))))
107         {
108           *R = f << 1;
109           *S = 1;
110           simpleShiftLeftHighPrecision (S, STemp_SIZE, 1 - e);
111           *mplus = *mminus = 1;
112         }
113       else
114         {
115           *R = f << 2;
116           *S = 1;
117           simpleShiftLeftHighPrecision (S, STemp_SIZE, 2 - e);
118           *mplus = 2;
119           *mminus = 1;
120         }
121     }
122 
123   k = static_cast<int>(ceil ((e + p - 1) * INV_LOG_OF_TEN_BASE_2 - 1e-10));
124 
125   if (k > 0)
126     {
127       timesTenToTheEHighPrecision (S, STemp_SIZE, k);
128     }
129   else
130     {
131       timesTenToTheEHighPrecision (R     , RM_SIZE, -k);
132       timesTenToTheEHighPrecision (mplus , RM_SIZE, -k);
133       timesTenToTheEHighPrecision (mminus, RM_SIZE, -k);
134     }
135 
136   RLength = mplus_Length = mminus_Length = RM_SIZE;
137   SLength = TempLength = STemp_SIZE;
138 
139   memset (Temp + RM_SIZE, 0, (STemp_SIZE - RM_SIZE) * sizeof (uint64_t));
140   memcpy (Temp, R, RM_SIZE * sizeof (uint64_t));
141 
142   while (RLength > 1 && R[RLength - 1] == 0)
143     --RLength;
144   while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
145     --mplus_Length;
146   while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
147     --mminus_Length;
148   while (SLength > 1 && S[SLength - 1] == 0)
149     --SLength;
150   TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1;
151   addHighPrecision (Temp, TempLength, mplus, mplus_Length);
152 
153   if (compareHighPrecision (Temp, TempLength, S, SLength) >= 0)
154     {
155       firstK = k;
156     }
157   else
158     {
159       firstK = k - 1;
160       simpleAppendDecimalDigitHighPrecision (R     , ++RLength      , 0);
161       simpleAppendDecimalDigitHighPrecision (mplus , ++mplus_Length , 0);
162       simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0);
163       while (RLength > 1 && R[RLength - 1] == 0)
164         --RLength;
165       while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
166         --mplus_Length;
167       while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
168         --mminus_Length;
169     }
170 
171   static jfieldID digitsFid = env->GetFieldID(JniConstants::realToStringClass, "digits", "[I");
172   ScopedLocalRef<jintArray> javaDigits(env, reinterpret_cast<jintArray>(env->GetObjectField(obj, digitsFid)));
173   ScopedIntArrayRW digits(env, javaDigits.get());
174   if (digits.get() == NULL) {
175     return;
176   }
177 
178   jint digitCount = 0;
179   do
180     {
181       U = 0;
182       for (i = 3; i >= 0; --i)
183         {
184           TempLength = SLength + 1;
185           Temp[SLength] = 0;
186           memcpy (Temp, S, SLength * sizeof (uint64_t));
187           simpleShiftLeftHighPrecision (Temp, TempLength, i);
188           if (compareHighPrecision (R, RLength, Temp, TempLength) >= 0)
189             {
190               subtractHighPrecision (R, RLength, Temp, TempLength);
191               U += 1 << i;
192             }
193         }
194 
195       low = compareHighPrecision (R, RLength, mminus, mminus_Length) <= 0;
196 
197       memset (Temp + RLength, 0, (STemp_SIZE - RLength) * sizeof (uint64_t));
198       memcpy (Temp, R, RLength * sizeof (uint64_t));
199       TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1;
200       addHighPrecision (Temp, TempLength, mplus, mplus_Length);
201 
202       high = compareHighPrecision (Temp, TempLength, S, SLength) >= 0;
203 
204       if (low || high)
205         break;
206 
207       simpleAppendDecimalDigitHighPrecision (R     , ++RLength      , 0);
208       simpleAppendDecimalDigitHighPrecision (mplus , ++mplus_Length , 0);
209       simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0);
210       while (RLength > 1 && R[RLength - 1] == 0)
211         --RLength;
212       while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
213         --mplus_Length;
214       while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
215         --mminus_Length;
216       digits[digitCount++] = U;
217     }
218   while (1);
219 
220   simpleShiftLeftHighPrecision (R, ++RLength, 1);
221   if (low && !high)
222     digits[digitCount++] = U;
223   else if (high && !low)
224     digits[digitCount++] = U + 1;
225   else if (compareHighPrecision (R, RLength, S, SLength) < 0)
226     digits[digitCount++] = U;
227   else
228     digits[digitCount++] = U + 1;
229 
230   static jfieldID digitCountFid = env->GetFieldID(JniConstants::realToStringClass, "digitCount", "I");
231   env->SetIntField(obj, digitCountFid, digitCount);
232 
233   static jfieldID firstKFid = env->GetFieldID(JniConstants::realToStringClass, "firstK", "I");
234   env->SetIntField(obj, firstKFid, firstK);
235 }
236 
237 static JNINativeMethod gMethods[] = {
238     NATIVE_METHOD(RealToString, bigIntDigitGenerator, "(JIZI)V"),
239 };
register_java_lang_RealToString(JNIEnv * env)240 int register_java_lang_RealToString(JNIEnv* env) {
241     return jniRegisterNativeMethods(env, "java/lang/RealToString", gMethods, NELEM(gMethods));
242 }
243