blob: f6d50b23c6bec8e859c0011fc53b80dd9b2d9a5f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "RealToString"
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "JNIHelp.h"
#include "JniConstants.h"
#include "ScopedPrimitiveArray.h"
#include "cbigint.h"
#define INV_LOG_OF_TEN_BASE_2 (0.30102999566398114) /* Local */
/*NB the Number converter methods are synchronized so it is possible to
*have global data for use by bigIntDigitGenerator */
#define RM_SIZE 21 /* Local. */
#define STemp_SIZE 22 /* Local. */
/* The algorithm for this particular function can be found in:
*
* Printing Floating-Point Numbers Quickly and Accurately, Robert
* G. Burger, and R. Kent Dybvig, Programming Language Design and
* Implementation (PLDI) 1996, pp.108-116.
*
* The previous implementation of this function combined m+ and m- into
* one single M which caused some inaccuracy of the last digit. The
* particular case below shows this inaccuracy:
*
* System.out.println(new Double((1.234123412431233E107)).toString());
* System.out.println(new Double((1.2341234124312331E107)).toString());
* System.out.println(new Double((1.2341234124312332E107)).toString());
*
* outputs the following:
*
* 1.234123412431233E107
* 1.234123412431233E107
* 1.234123412431233E107
*
* instead of:
*
* 1.234123412431233E107
* 1.2341234124312331E107
* 1.2341234124312331E107
*
*/
void RealToString_bigIntDigitGenerator(JNIEnv* env, jobject obj, jlong f, jint e,
jboolean isDenormalized, jint p) {
int RLength, SLength, TempLength, mplus_Length, mminus_Length;
int high, low, i;
jint k, firstK, U;
uint64_t R[RM_SIZE], S[STemp_SIZE], mplus[RM_SIZE], mminus[RM_SIZE], Temp[STemp_SIZE];
memset (R , 0, RM_SIZE * sizeof (uint64_t));
memset (S , 0, STemp_SIZE * sizeof (uint64_t));
memset (mplus , 0, RM_SIZE * sizeof (uint64_t));
memset (mminus, 0, RM_SIZE * sizeof (uint64_t));
memset (Temp , 0, STemp_SIZE * sizeof (uint64_t));
if (e >= 0)
{
*R = f;
*mplus = *mminus = 1;
simpleShiftLeftHighPrecision (mminus, RM_SIZE, e);
if (f != (2 << (p - 1)))
{
simpleShiftLeftHighPrecision (R, RM_SIZE, e + 1);
*S = 2;
/*
* m+ = m+ << e results in 1.0e23 to be printed as
* 0.9999999999999999E23
* m+ = m+ << e+1 results in 1.0e23 to be printed as
* 1.0e23 (caused too much rounding)
* 470fffffffffffff = 2.0769187434139308E34
* 4710000000000000 = 2.076918743413931E34
*/
simpleShiftLeftHighPrecision (mplus, RM_SIZE, e);
}
else
{
simpleShiftLeftHighPrecision (R, RM_SIZE, e + 2);
*S = 4;
simpleShiftLeftHighPrecision (mplus, RM_SIZE, e + 1);
}
}
else
{
if (isDenormalized || (f != (2 << (p - 1))))
{
*R = f << 1;
*S = 1;
simpleShiftLeftHighPrecision (S, STemp_SIZE, 1 - e);
*mplus = *mminus = 1;
}
else
{
*R = f << 2;
*S = 1;
simpleShiftLeftHighPrecision (S, STemp_SIZE, 2 - e);
*mplus = 2;
*mminus = 1;
}
}
k = static_cast<int>(ceil ((e + p - 1) * INV_LOG_OF_TEN_BASE_2 - 1e-10));
if (k > 0)
{
timesTenToTheEHighPrecision (S, STemp_SIZE, k);
}
else
{
timesTenToTheEHighPrecision (R , RM_SIZE, -k);
timesTenToTheEHighPrecision (mplus , RM_SIZE, -k);
timesTenToTheEHighPrecision (mminus, RM_SIZE, -k);
}
RLength = mplus_Length = mminus_Length = RM_SIZE;
SLength = TempLength = STemp_SIZE;
memset (Temp + RM_SIZE, 0, (STemp_SIZE - RM_SIZE) * sizeof (uint64_t));
memcpy (Temp, R, RM_SIZE * sizeof (uint64_t));
while (RLength > 1 && R[RLength - 1] == 0)
--RLength;
while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
--mplus_Length;
while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
--mminus_Length;
while (SLength > 1 && S[SLength - 1] == 0)
--SLength;
TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1;
addHighPrecision (Temp, TempLength, mplus, mplus_Length);
if (compareHighPrecision (Temp, TempLength, S, SLength) >= 0)
{
firstK = k;
}
else
{
firstK = k - 1;
simpleAppendDecimalDigitHighPrecision (R , ++RLength , 0);
simpleAppendDecimalDigitHighPrecision (mplus , ++mplus_Length , 0);
simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0);
while (RLength > 1 && R[RLength - 1] == 0)
--RLength;
while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
--mplus_Length;
while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
--mminus_Length;
}
static jfieldID digitsFid = env->GetFieldID(JniConstants::realToStringClass, "digits", "[I");
jintArray javaDigits = reinterpret_cast<jintArray>(env->GetObjectField(obj, digitsFid));
ScopedIntArrayRW digits(env, javaDigits);
if (digits.get() == NULL) {
return;
}
jint digitCount = 0;
do
{
U = 0;
for (i = 3; i >= 0; --i)
{
TempLength = SLength + 1;
Temp[SLength] = 0;
memcpy (Temp, S, SLength * sizeof (uint64_t));
simpleShiftLeftHighPrecision (Temp, TempLength, i);
if (compareHighPrecision (R, RLength, Temp, TempLength) >= 0)
{
subtractHighPrecision (R, RLength, Temp, TempLength);
U += 1 << i;
}
}
low = compareHighPrecision (R, RLength, mminus, mminus_Length) <= 0;
memset (Temp + RLength, 0, (STemp_SIZE - RLength) * sizeof (uint64_t));
memcpy (Temp, R, RLength * sizeof (uint64_t));
TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1;
addHighPrecision (Temp, TempLength, mplus, mplus_Length);
high = compareHighPrecision (Temp, TempLength, S, SLength) >= 0;
if (low || high)
break;
simpleAppendDecimalDigitHighPrecision (R , ++RLength , 0);
simpleAppendDecimalDigitHighPrecision (mplus , ++mplus_Length , 0);
simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0);
while (RLength > 1 && R[RLength - 1] == 0)
--RLength;
while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
--mplus_Length;
while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
--mminus_Length;
digits[digitCount++] = U;
}
while (1);
simpleShiftLeftHighPrecision (R, ++RLength, 1);
if (low && !high)
digits[digitCount++] = U;
else if (high && !low)
digits[digitCount++] = U + 1;
else if (compareHighPrecision (R, RLength, S, SLength) < 0)
digits[digitCount++] = U;
else
digits[digitCount++] = U + 1;
static jfieldID digitCountFid = env->GetFieldID(JniConstants::realToStringClass, "digitCount", "I");
env->SetIntField(obj, digitCountFid, digitCount);
static jfieldID firstKFid = env->GetFieldID(JniConstants::realToStringClass, "firstK", "I");
env->SetIntField(obj, firstKFid, firstK);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(RealToString, bigIntDigitGenerator, "(JIZI)V"),
};
int register_java_lang_RealToString(JNIEnv* env) {
return jniRegisterNativeMethods(env, "java/lang/RealToString", gMethods, NELEM(gMethods));
}