Home | History | Annotate | Download | only in native
      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