blob: 15e53f31874ba313ab5bfa45a2cbdc29dab7e579 [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.
*/
package org.apache.harmony.luni.util;
public final class NumberConverter {
private int setCount; // number of times u and k have been gotten
private int getCount; // number of times u and k have been set
private int[] uArray = new int[64];
private int firstK;
private final static double invLogOfTenBaseTwo = Math.log(2.0)
/ Math.log(10.0);
private final static long[] TEN_TO_THE = new long[20];
static {
TEN_TO_THE[0] = 1L;
for (int i = 1; i < TEN_TO_THE.length; ++i) {
long previous = TEN_TO_THE[i - 1];
TEN_TO_THE[i] = (previous << 1) + (previous << 3);
}
}
private static NumberConverter getConverter() {
return new NumberConverter();
}
public static String convert(double input) {
return getConverter().convertD(input);
}
public static String convert(float input) {
return getConverter().convertF(input);
}
public String convertD(double inputNumber) {
int p = 1023 + 52; // the power offset (precision)
long signMask = 0x8000000000000000L; // the mask to get the sign of
// the number
long eMask = 0x7FF0000000000000L; // the mask to get the power bits
long fMask = 0x000FFFFFFFFFFFFFL; // the mask to get the significand
// bits
long inputNumberBits = Double.doubleToLongBits(inputNumber);
// the value of the sign... 0 is positive, ~0 is negative
String signString = (inputNumberBits & signMask) == 0 ? "" : "-";
// the value of the 'power bits' of the inputNumber
int e = (int) ((inputNumberBits & eMask) >> 52);
// the value of the 'significand bits' of the inputNumber
long f = inputNumberBits & fMask;
boolean mantissaIsZero = f == 0;
int pow = 0, numBits = 52;
if (e == 2047)
return mantissaIsZero ? signString + "Infinity" : "NaN";
if (e == 0) {
if (mantissaIsZero)
return signString + "0.0";
if (f == 1)
// special case to increase precision even though 2 *
// Double.MIN_VALUE is 1.0e-323
return signString + "4.9E-324";
pow = 1 - p; // a denormalized number
long ff = f;
while ((ff & 0x0010000000000000L) == 0) {
ff = ff << 1;
numBits--;
}
} else {
// 0 < e < 2047
// a "normalized" number
f = f | 0x0010000000000000L;
pow = e - p;
}
if (-59 < pow && pow < 6 || (pow == -59 && !mantissaIsZero))
longDigitGenerator(f, pow, e == 0, mantissaIsZero, numBits);
else
bigIntDigitGeneratorInstImpl(f, pow, e == 0, mantissaIsZero,
numBits);
if (inputNumber >= 1e7D || inputNumber <= -1e7D
|| (inputNumber > -1e-3D && inputNumber < 1e-3D))
return signString + freeFormatExponential();
return signString + freeFormat();
}
public String convertF(float inputNumber) {
int p = 127 + 23; // the power offset (precision)
int signMask = 0x80000000; // the mask to get the sign of the number
int eMask = 0x7F800000; // the mask to get the power bits
int fMask = 0x007FFFFF; // the mask to get the significand bits
int inputNumberBits = Float.floatToIntBits(inputNumber);
// the value of the sign... 0 is positive, ~0 is negative
String signString = (inputNumberBits & signMask) == 0 ? "" : "-";
// the value of the 'power bits' of the inputNumber
int e = (inputNumberBits & eMask) >> 23;
// the value of the 'significand bits' of the inputNumber
int f = inputNumberBits & fMask;
boolean mantissaIsZero = f == 0;
int pow = 0, numBits = 23;
if (e == 255)
return mantissaIsZero ? signString + "Infinity" : "NaN";
if (e == 0) {
if (mantissaIsZero)
return signString + "0.0";
pow = 1 - p; // a denormalized number
if (f < 8) { // want more precision with smallest values
f = f << 2;
pow -= 2;
}
int ff = f;
while ((ff & 0x00800000) == 0) {
ff = ff << 1;
numBits--;
}
} else {
// 0 < e < 255
// a "normalized" number
f = f | 0x00800000;
pow = e - p;
}
if (-59 < pow && pow < 35 || (pow == -59 && !mantissaIsZero))
longDigitGenerator(f, pow, e == 0, mantissaIsZero, numBits);
else
bigIntDigitGeneratorInstImpl(f, pow, e == 0, mantissaIsZero,
numBits);
if (inputNumber >= 1e7f || inputNumber <= -1e7f
|| (inputNumber > -1e-3f && inputNumber < 1e-3f))
return signString + freeFormatExponential();
return signString + freeFormat();
}
private String freeFormatExponential() {
// corresponds to process "Free-Format Exponential"
char[] formattedDecimal = new char[25];
formattedDecimal[0] = (char) ('0' + uArray[getCount++]);
formattedDecimal[1] = '.';
// the position the next character is to be inserted into
// formattedDecimal
int charPos = 2;
int k = firstK;
int expt = k;
while (true) {
k--;
if (getCount >= setCount)
break;
formattedDecimal[charPos++] = (char) ('0' + uArray[getCount++]);
}
if (k == expt - 1)
formattedDecimal[charPos++] = '0';
formattedDecimal[charPos++] = 'E';
return new String(formattedDecimal, 0, charPos)
+ Integer.toString(expt);
}
private String freeFormat() {
// corresponds to process "Free-Format"
char[] formattedDecimal = new char[25];
// the position the next character is to be inserted into
// formattedDecimal
int charPos = 0;
int k = firstK;
if (k < 0) {
formattedDecimal[0] = '0';
formattedDecimal[1] = '.';
charPos += 2;
for (int i = k + 1; i < 0; i++)
formattedDecimal[charPos++] = '0';
}
int U = uArray[getCount++];
do {
if (U != -1)
formattedDecimal[charPos++] = (char) ('0' + U);
else if (k >= -1)
formattedDecimal[charPos++] = '0';
if (k == 0)
formattedDecimal[charPos++] = '.';
k--;
U = getCount < setCount ? uArray[getCount++] : -1;
} while (U != -1 || k >= -1);
return new String(formattedDecimal, 0, charPos);
}
private native void bigIntDigitGeneratorInstImpl(long f, int e,
boolean isDenormalized, boolean mantissaIsZero, int p);
private void longDigitGenerator(long f, int e, boolean isDenormalized,
boolean mantissaIsZero, int p) {
long R, S, M;
if (e >= 0) {
M = 1l << e;
if (!mantissaIsZero) {
R = f << (e + 1);
S = 2;
} else {
R = f << (e + 2);
S = 4;
}
} else {
M = 1;
if (isDenormalized || !mantissaIsZero) {
R = f << 1;
S = 1l << (1 - e);
} else {
R = f << 2;
S = 1l << (2 - e);
}
}
int k = (int) Math.ceil((e + p - 1) * invLogOfTenBaseTwo - 1e-10);
if (k > 0) {
S = S * TEN_TO_THE[k];
} else if (k < 0) {
long scale = TEN_TO_THE[-k];
R = R * scale;
M = M == 1 ? scale : M * scale;
}
if (R + M > S) { // was M_plus
firstK = k;
} else {
firstK = k - 1;
R = R * 10;
M = M * 10;
}
getCount = setCount = 0; // reset indices
boolean low, high;
int U;
long[] Si = new long[] { S, S << 1, S << 2, S << 3 };
while (true) {
// set U to be floor (R / S) and R to be the remainder
// using a kind of "binary search" to find the answer.
// It's a lot quicker than actually dividing since we know
// the answer will be between 0 and 10
U = 0;
long remainder;
for (int i = 3; i >= 0; i--) {
remainder = R - Si[i];
if (remainder >= 0) {
R = remainder;
U += 1 << i;
}
}
low = R < M; // was M_minus
high = R + M > S; // was M_plus
if (low || high)
break;
R = R * 10;
M = M * 10;
uArray[setCount++] = U;
}
if (low && !high)
uArray[setCount++] = U;
else if (high && !low)
uArray[setCount++] = U + 1;
else if ((R << 1) < S)
uArray[setCount++] = U;
else
uArray[setCount++] = U + 1;
}
}