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