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