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 */ 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 }; 240 void register_java_lang_RealToString(JNIEnv* env) { 241 jniRegisterNativeMethods(env, "java/lang/RealToString", gMethods, NELEM(gMethods)); 242 } 243