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