NumberUtils.java
package com.alibaba.fastjson2.util;
import java.lang.invoke.*;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import static com.alibaba.fastjson2.util.IOUtils.*;
import static com.alibaba.fastjson2.util.JDKUtils.ANDROID;
/**
* Author: wangy
*/
public final class NumberUtils {
@FunctionalInterface
interface LongBiFunction {
long multiplyHigh(long x, long y);
}
static long multiplyHigh(long x, long y) {
long x1 = x >> 32;
long x2 = x & 0xFFFFFFFFL;
long y1 = y >> 32;
long y2 = y & 0xFFFFFFFFL;
long z2 = x2 * y2;
long t = x1 * y2 + (z2 >>> 32);
long z1 = t & 0xFFFFFFFFL;
long z0 = t >> 32;
z1 += x2 * y1;
return x1 * y1 + z0 + (z1 >> 32);
}
static final LongBiFunction MULTIPLY_HIGH;
static {
LongBiFunction function = null;
if (JDKUtils.JVM_VERSION > 8 && !ANDROID) {
try {
MethodHandles.Lookup lookup = JDKUtils.trustedLookup(NumberUtils.class);
MethodType methodType = MethodType.methodType(long.class, long.class, long.class);
MethodHandle methodHandle = lookup.findStatic(Math.class, "multiplyHigh", methodType);
CallSite callSite = LambdaMetafactory.metafactory(
lookup,
"multiplyHigh",
MethodType.methodType(LongBiFunction.class),
methodType,
methodHandle,
methodType
);
function = (LongBiFunction) callSite.getTarget().invokeExact();
} catch (Throwable ignored) {
// ignored
}
}
if (function == null) {
function = NumberUtils::multiplyHigh;
}
MULTIPLY_HIGH = function;
}
private NumberUtils() {
}
static final long INFI;
static final long NITY;
static final long INFINITY;
static {
String str = "Infinity";
INFINITY = IOUtils.getLongUnaligned(str.getBytes(StandardCharsets.ISO_8859_1), 0);
char[] chars = str.toCharArray();
INFI = IOUtils.getLongUnaligned(chars, 0);
NITY = IOUtils.getLongUnaligned(chars, 4);
}
static final double[] POSITIVE_DECIMAL_POWER = new double[325];
static final double[] NEGATIVE_DECIMAL_POWER = new double[325];
static final long[] POW10_LONG_VALUES = new long[]{
10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L, 1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L, 9223372036854775807L
};
static final long[] POW5_LONG_VALUES = new long[27];
static final BigInteger[] POW5_BI_VALUES = new BigInteger[343];
static {
// e0 ~ e360(e306)
for (int i = 0, len = POSITIVE_DECIMAL_POWER.length; i < len; ++i) {
POSITIVE_DECIMAL_POWER[i] = Double.valueOf("1.0E" + i);
NEGATIVE_DECIMAL_POWER[i] = Double.valueOf("1.0E-" + i);
}
// 4.9e-324
NEGATIVE_DECIMAL_POWER[NEGATIVE_DECIMAL_POWER.length - 1] = Double.MIN_VALUE;
long val = 1;
for (int i = 0; i < POW5_LONG_VALUES.length; ++i) {
POW5_LONG_VALUES[i] = val;
val *= 5;
}
BigInteger five = BigInteger.valueOf(5);
POW5_BI_VALUES[0] = BigInteger.ONE;
for (int i = 1; i < POW5_BI_VALUES.length; ++i) {
BigInteger pow5Value = five.pow(i);
POW5_BI_VALUES[i] = pow5Value;
}
}
static final int MOD_DOUBLE_EXP = (1 << 11) - 1;
static final long MOD_DOUBLE_MANTISSA = (1L << 52) - 1;
/**
* multiplyOutput
*
* @param x > 0
* @param y > 0
* @param shift > 0
* @return
*/
static long multiplyHighAndShift(long x, long y, int shift) {
long H = MULTIPLY_HIGH.multiplyHigh(x, y);
if (shift >= 64) {
int sr = shift - 64;
return H >>> sr;
}
long L = x * y;
return H << (64 - shift) | (L >>> shift);
}
/**
* Unsigned algorithm: (H * 2^64 * 2^n + L * 2^n + H1 * 2^64 + L1) / 2^(n+s) = (H * 2^64 + L + (H1 * 2^64 + L1) / 2^n) / 2^s
* <p>
* use BigInteger: BigInteger.valueOf(pd.y).shiftLeft(n).add(BigInteger.valueOf(f & MASK_32_BITS)).multiply(BigInteger.valueOf(x)).shiftRight(s + n).longValue()
*
* @param x 63bits
* @param y 63bits
* @param y32 unsigned int32 f(32bits)
* @param s s > 0
* @return
*/
static long multiplyHighAndShift(long x, long y, long y32, int s) {
int sr = s - 64;
long H = MULTIPLY_HIGH.multiplyHigh(x, y);
long L = x * y;
// cal x * f -> H1, L1
long H1 = MULTIPLY_HIGH.multiplyHigh(x, y32);
long L1 = x * y32;
// carry = (H1 * 2^64 + L1) / 2^n
long carry = (H1 << 32) + (L1 >>> 32);
long L2 = L + carry;
if ((L | carry) < 0 && ((L & carry) < 0 || L2 >= 0)) {
++H;
}
L = L2;
if (sr >= 0) {
return H >>> sr;
}
return H << -sr | (L >>> s);
}
/**
* Conversion of ieee floating point numbers to Scientific notation
*
* <p> Using the difference estimation method </p>
* <p> The output may not be the shortest, but the general result is correct </p>
*
* @param doubleValue > 0
*/
public static Scientific doubleToScientific(double doubleValue) {
if (doubleValue == Double.MIN_VALUE) {
// Double.MIN_VALUE The minimum double value converted by JDK is 4.9e-324. This method converts it to 5.0e-324.
// Due to the special value, it is specially processed to be consistent with JDK conversion.
return Scientific.DOUBLE_MIN;
}
long bits = Double.doubleToRawLongBits(doubleValue);
int e2 = (int) (bits >> 52) & MOD_DOUBLE_EXP;
long mantissa0 = bits & MOD_DOUBLE_MANTISSA;
// boolean flagForUp = mantissa0 < MOD_DOUBLE_MANTISSA;
boolean flagForDown = mantissa0 > 0;
int e52;
long output;
long rawOutput, /*d2, */d3, d4;
int e10, adl;
if (e2 > 0) {
// Double.NaN/Double.POSITIVE_INFINITY/Double.NEGATIVE_INFINITY -> NULL
if (e2 == 2047) {
return Scientific.SCIENTIFIC_NULL;
}
mantissa0 = 1L << 52 | mantissa0;
e52 = e2 - 1075;
} else {
if (mantissa0 == 0) {
// Dealing with error issues with doubleValue=0.0(-0.0) Or make sure doubleValue is greater than 0 before calling
return bits == 0 ? Scientific.ZERO : Scientific.NEGATIVE_ZERO;
}
int lz52 = Long.numberOfLeadingZeros(mantissa0) - 11;
mantissa0 <<= lz52;
e52 = -1074 - lz52;
}
boolean /*tflag = true,*/ accurate = false;
if (e52 >= 0) {
ED d = ED.E2_D_A[e52];
e10 = d.e10; // e10 > 15
adl = d.adl;
// d2 = d.d2;
d3 = d.d3;
d4 = d.d4;
if (d.b && mantissa0 >= d.bv) {
if (mantissa0 > d.bv) {
++e10;
++adl;
} else {
if (doubleValue == POSITIVE_DECIMAL_POWER[e10 + 1]) {
return new Scientific(e10 + 1, true);
}
}
}
int o5 = d.o5; // adl + 2 - e10
int sb = e52 + o5;
if (o5 < 0) {
// mantissa0 * 2^(e52 + o5) * 5^o5 -> mantissa0 * 2^sb / 5^(-o5)
ED5 d5 = ED5.ED5_A[-o5];
int rb = sb - 10 - d5.ob;
// rawOutput = BigInteger.valueOf(mantissa0).shiftLeft(sb).divide(POW5_BI_VALUES[-o5]).longValue();
rawOutput = multiplyHighAndShift(mantissa0 << 10, d5.oy, d5.of, 32 - rb);
accurate = o5 == -1 && sb < 11;
} else {
// o5 > 0 -> sb > 0
// accurate
rawOutput = (mantissa0 * POW5_LONG_VALUES[o5]) << sb;
accurate = true;
}
} else {
// e52 >= -1074 -> p5 <= 1074
int e5 = -e52;
ED d = ED.E5_D_A[e5];
e10 = d.e10;
adl = d.adl;
// d2 = d.d2;
d3 = d.d3;
d4 = d.d4;
if (d.b && mantissa0 >= d.bv) {
if (mantissa0 > d.bv) {
++e10;
++adl;
} else {
if (e10 >= -1 && doubleValue == POSITIVE_DECIMAL_POWER[e10 + 1]) {
return new Scientific(e10 + 1, true);
}
if (e10 < -1 && doubleValue == NEGATIVE_DECIMAL_POWER[-e10 - 1]) {
return new Scientific(e10 + 1, true);
}
}
}
int o5 = d.o5; // adl + 2 - e10; // o5 > 0
int sb = o5 + e52;
if (sb < 0) {
if (o5 < POW5_LONG_VALUES.length) {
rawOutput = multiplyHighAndShift(mantissa0, POW5_LONG_VALUES[o5], -sb);
} else if (o5 < POW5_LONG_VALUES.length + 4) {
rawOutput = multiplyHighAndShift(mantissa0 * POW5_LONG_VALUES[o5 - POW5_LONG_VALUES.length + 1], POW5_LONG_VALUES[POW5_LONG_VALUES.length - 1], -sb);
} else {
ED5 ed5 = ED5.ED5_A[o5];
rawOutput = multiplyHighAndShift(mantissa0 << 10, ed5.y, ed5.f, -(ed5.dfb + sb) + 10);
}
} else {
rawOutput = POW5_LONG_VALUES[o5] * mantissa0 << sb;
}
}
if (accurate) {
rawOutput = rawOutput / 10;
if (adl == 16) {
--adl;
rawOutput = (rawOutput + 5) / 10; // rawOutput = rawOutput / 10 + ((rawOutput % 10) >= 5 ? 1 : 0);
}
return new Scientific(rawOutput, adl + 2, e10);
}
// rem <= Actual Rem Value
long div = rawOutput / 1000, rem = rawOutput - div * 1000;
long remUp = (10001 - rem * 10) << 1;
boolean up;
if ((up = (remUp <= d4)) || ((rem + 1) << (flagForDown ? 1 : 2)) <= d3) {
output = div + (up ? 1 : 0);
--adl;
} else {
if (flagForDown) {
output = (rawOutput + 50) / 100; // rawOutput / 100 + ((rawOutput % 100) >= 50 ? 1 : 0)
} else {
output = (rawOutput + 5) / 10; // rawOutput / 10 + ((rawOutput % 10) >= 5 ? 1 : 0)
++adl;
}
}
return new Scientific(output, adl + 1, e10);
}
static final char[][] POSITIVE_DECIMAL_POWER_CHARS = new char[325][];
static final char[][] NEGATIVE_DECIMAL_POWER_CHARS = new char[325][];
static {
for (int i = 0, len = POSITIVE_DECIMAL_POWER_CHARS.length; i < len; ++i) {
String positive = "1.0E" + i;
String negative = "1.0E-" + i;
POSITIVE_DECIMAL_POWER_CHARS[i] = positive.toCharArray();
NEGATIVE_DECIMAL_POWER_CHARS[i] = negative.toCharArray();
}
NEGATIVE_DECIMAL_POWER_CHARS[NEGATIVE_DECIMAL_POWER_CHARS.length - 1] = "4.9E-324".toCharArray();
}
public static int writeDouble(byte[] buf, int off, double doubleValue, boolean json) {
long bits;
if (doubleValue == 0) {
bits = Double.doubleToLongBits(doubleValue);
if (bits == 0x8000000000000000L) {
buf[off++] = '-';
}
buf[off] = '0';
IOUtils.putShortUnaligned(buf, off + 1, DOT_ZERO_16);
return off + 3;
}
boolean sign = doubleValue < 0;
if (sign) {
if (!json || doubleValue != Double.NEGATIVE_INFINITY) {
buf[off++] = '-';
}
doubleValue = -doubleValue;
}
if (doubleValue == (long) doubleValue) {
long output = (long) doubleValue;
int numLength = stringSize(output);
return writeDecimal(output, numLength, numLength - 1, buf, off);
}
Scientific scientific = NumberUtils.doubleToScientific(doubleValue);
int e10 = scientific.e10;
if (!scientific.b) {
return writeDecimal(scientific.output, scientific.count, scientific.e10, buf, off);
}
if (scientific == Scientific.SCIENTIFIC_NULL) {
if (json) {
IOUtils.putIntUnaligned(buf, off, IOUtils.NULL_32);
return off + 4;
} else {
if (doubleValue == Double.POSITIVE_INFINITY) {
IOUtils.putLongUnaligned(buf, off, INFINITY);
return off + 8;
} else {
buf[off] = 'N';
buf[off + 1] = 'a';
buf[off + 2] = 'N';
return off + 3;
}
}
}
if (e10 >= 0) {
char[] chars = POSITIVE_DECIMAL_POWER_CHARS[e10];
for (char c : chars) {
buf[off++] = (byte) c;
}
return off;
} else {
char[] chars = NEGATIVE_DECIMAL_POWER_CHARS[-e10];
for (char c : chars) {
buf[off++] = (byte) c;
}
return off;
}
}
public static int writeDouble(char[] buf, int off, double doubleValue, boolean json) {
long bits;
if (doubleValue == 0) {
bits = Double.doubleToLongBits(doubleValue);
if (bits == 0x8000000000000000L) {
buf[off++] = '-';
}
buf[off] = '0';
IOUtils.putIntUnaligned(buf, off + 1, DOT_ZERO_32);
return off + 3;
}
boolean sign = doubleValue < 0;
if (sign) {
if (!json || doubleValue != Double.NEGATIVE_INFINITY) {
buf[off++] = '-';
}
doubleValue = -doubleValue;
}
if (doubleValue == (long) doubleValue) {
long output = (long) doubleValue;
int numLength = stringSize(output);
return writeDecimal(output, numLength, numLength - 1, buf, off);
}
Scientific scientific = NumberUtils.doubleToScientific(doubleValue);
int e10 = scientific.e10;
if (!scientific.b) {
return writeDecimal(scientific.output, scientific.count, e10, buf, off);
}
if (scientific == Scientific.SCIENTIFIC_NULL) {
if (json) {
IOUtils.putLongUnaligned(buf, off, NULL_64);
return off + 4;
} else {
if (doubleValue == Double.POSITIVE_INFINITY) {
IOUtils.putLongUnaligned(buf, off, INFI);
IOUtils.putLongUnaligned(buf, off + 4, NITY);
return off + 8;
} else {
buf[off] = 'N';
buf[off + 1] = 'a';
buf[off + 2] = 'N';
return off + 3;
}
}
}
if (e10 >= 0) {
char[] chars = POSITIVE_DECIMAL_POWER_CHARS[e10];
System.arraycopy(chars, 0, buf, off, chars.length);
return off + chars.length;
} else {
char[] chars = NEGATIVE_DECIMAL_POWER_CHARS[-e10];
System.arraycopy(chars, 0, buf, off, chars.length);
return off + chars.length;
}
}
public static int writeFloat(byte[] buf, int off, float floatValue, boolean json) {
if (Float.isNaN(floatValue) || floatValue == Float.POSITIVE_INFINITY || floatValue == Float.NEGATIVE_INFINITY) {
return writeSpecial(buf, off, floatValue, json);
}
int bits;
if (floatValue == 0) {
bits = Float.floatToIntBits(floatValue);
if (bits == 0x80000000) {
buf[off++] = '-';
}
buf[off] = '0';
IOUtils.putShortUnaligned(buf, off + 1, DOT_ZERO_16);
return off + 3;
}
boolean sign = floatValue < 0;
if (sign) {
buf[off++] = '-';
floatValue = -floatValue;
}
Scientific scientific = floatToScientific(floatValue);
return writeDecimal(scientific.output, scientific.count, scientific.e10, buf, off);
}
public static int writeFloat(char[] buf, int off, float floatValue, boolean json) {
if (Float.isNaN(floatValue) || floatValue == Float.POSITIVE_INFINITY || floatValue == Float.NEGATIVE_INFINITY) {
return writeSpecial(buf, off, floatValue, json);
}
int bits;
if (floatValue == 0) {
bits = Float.floatToIntBits(floatValue);
if (bits == 0x80000000) {
buf[off++] = '-';
}
buf[off] = '0';
IOUtils.putIntUnaligned(buf, off + 1, DOT_ZERO_32); // .0
return off + 3;
}
boolean sign = floatValue < 0;
if (sign) {
buf[off++] = '-';
floatValue = -floatValue;
}
Scientific scientific = NumberUtils.floatToScientific(floatValue);
return writeDecimal(scientific.output, scientific.count, scientific.e10, buf, off);
}
private static int writeSpecial(byte[] buf, int off, float floatValue, boolean json) {
if (json) {
IOUtils.putIntUnaligned(buf, off, NULL_32);
return off + 4;
}
if (Float.isNaN(floatValue)) {
buf[off] = 'N';
buf[off + 1] = 'a';
buf[off + 2] = 'N';
return off + 3;
}
if (floatValue == Float.NEGATIVE_INFINITY) {
buf[off++] = '-';
}
IOUtils.putLongUnaligned(buf, off, INFINITY);
return off + 8;
}
private static int writeSpecial(char[] buf, int off, float floatValue, boolean json) {
if (json) {
IOUtils.putLongUnaligned(buf, off, NULL_64);
return off + 4;
}
if (Float.isNaN(floatValue)) {
buf[off] = 'N';
buf[off + 1] = 'a';
buf[off + 2] = 'N';
return off + 3;
}
if (floatValue == Float.NEGATIVE_INFINITY) {
buf[off++] = '-';
}
IOUtils.putLongUnaligned(buf, off, INFI);
IOUtils.putLongUnaligned(buf, off + 4, NITY);
return off + 8;
}
static final int MOD_FLOAT_EXP = (1 << 8) - 1;
static final int MOD_FLOAT_MANTISSA = (1 << 23) - 1;
public static Scientific floatToScientific(float floatValue) {
final int bits = Float.floatToRawIntBits(floatValue);
int e2 = (bits >> 23) & MOD_FLOAT_EXP;
int mantissa0 = bits & MOD_FLOAT_MANTISSA;
boolean nonZeroFlag = mantissa0 > 0;
int e23;
long output, rawOutput;
long d4;
int e10, adl;
boolean accurate = false;
if (e2 > 0) {
if (e2 == MOD_FLOAT_EXP) {
return Scientific.SCIENTIFIC_NULL;
}
mantissa0 = 1 << 23 | mantissa0;
e23 = e2 - 150; // 1023 - 52
} else {
// e2 == 0
if (mantissa0 == 0) {
return bits == 0 ? Scientific.ZERO : Scientific.NEGATIVE_ZERO;
}
int l = Integer.numberOfLeadingZeros(mantissa0) - 8;
mantissa0 <<= l;
e23 = -149 - l;
}
if (e23 >= 0) {
ED d = EF.E2_F_A[e23];
e10 = d.e10;
adl = d.adl;
d4 = d.d4;
if (d.b && mantissa0 > d.bv) {
++e10;
++adl;
}
int o5 = d.o5 + 6; // Compared to double (adl + 2 - e10), adding 6 more numbers increases the probability of hitting
int sb = e23 + o5;
if (o5 < 0) {
// mantissa0 * 2^(e23 + o5) * 5^o5 -> mantissa0 * 2^sb / 5^(-o5)
// rawOutput = BigInteger.valueOf(mantissa0).shiftLeft(sb).divide(POW5_BI_VALUES[-o5]).longValue();
if (sb < 40) {
rawOutput = ((long) mantissa0 << sb) / POW5_LONG_VALUES[-o5];
} else {
ED5 d5 = ED5.ED5_A[-o5];
rawOutput = multiplyHighAndShift((long) mantissa0 << 39, d5.oy, d5.of, 71 + d5.ob - sb);
}
} else {
// o5 > 0 -> sb > 0
// accurate
rawOutput = mantissa0 * POW5_LONG_VALUES[o5] << sb;
accurate = true;
}
} else {
// e52 >= -149 -> p5 <= 149
int e5 = -e23;
ED d = EF.E5_F_A[e5];
e10 = d.e10;
adl = d.adl;
d4 = d.d4;
if (d.b && mantissa0 > d.bv) {
++e10;
++adl;
}
int o5 = d.o5 + 6; // Compared to double (adl + 2 - e10), adding 6 more numbers increases the probability of hitting
int sb = o5 + e23;
if (sb < 0) {
// todo To be optimized
if (o5 < 17) {
rawOutput = mantissa0 * POW5_LONG_VALUES[o5] >> -sb;
} else if (o5 < POW5_LONG_VALUES.length) {
rawOutput = multiplyHighAndShift(mantissa0, POW5_LONG_VALUES[o5], -sb);
} else if (o5 < POW5_LONG_VALUES.length + 4) {
rawOutput = multiplyHighAndShift(mantissa0 * POW5_LONG_VALUES[o5 - POW5_LONG_VALUES.length + 1], POW5_LONG_VALUES[POW5_LONG_VALUES.length - 1], -sb);
} else {
ED5 ed5 = ED5.ED5_A[o5];
rawOutput = multiplyHighAndShift((long) mantissa0 << 39, ed5.y, ed5.f, -(ed5.dfb + sb) + 39); // 39 = 63 - 24
}
} else {
rawOutput = POW5_LONG_VALUES[o5] * mantissa0 << sb;
accurate = true;
}
}
if (accurate) {
// If we pursue performance, we can return it here, but it may return a non-shortest sequence of numbers (the result is correct)
// rawOutput = MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x6b5fca6af2bd215fL) >> 22; // rawOutput / 10000000;
// if (adl == 7) {
// --adl;
// rawOutput = (rawOutput + 5) / 10; // rawOutput = rawOutput / 10 + ((rawOutput % 10) >= 5 ? 1 : 0);
// }
// return new Scientific(rawOutput, adl + 2, e10);
}
if (rawOutput < 1000000000) {
return new Scientific(MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x6b5fca6af2bd215fL) >> 22, 2, e10); // rawOutput / 10000000
}
long div = MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x44b82fa09b5a52ccL) >> 28; // rawOutput / 1000000000;
long rem = rawOutput - div * 1000000000;
long remUp = (1000000001 - rem) << 1;
boolean up = remUp <= d4;
if (up || ((rem + 1) << (nonZeroFlag ? 1 : 2)) <= d4) {
output = div + (up ? 1 : 0);
--adl;
if (up) {
if (POW10_LONG_VALUES[adl] == output) {
++e10;
output = 1;
adl = 0;
}
}
} else {
if (nonZeroFlag) {
long div0 = MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x55e63b88c230e77fL) >> 25; // rawOutput / 100000000
output = div0 + (rem % 100000000 >= 50000000 ? 1 : 0);
} else {
long div0 = MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x6b5fca6af2bd215fL) >> 22; // rawOutput / 10000000
output = div0 + (rem % 10000000 >= 5000000 ? 1 : 0);
++adl;
}
}
return new Scientific(output, adl + 1, e10);
}
static final int[] TWO_DIGITS_32_BITS = new int[100];
static final short[] TWO_DIGITS_16_BITS = new short[100];
static {
for (long d1 = 0; d1 < 10; ++d1) {
for (long d2 = 0; d2 < 10; ++d2) {
long intVal64;
int intVal32;
if (JDKUtils.BIG_ENDIAN) {
intVal64 = (d1 + 48) << 16 | (d2 + 48);
intVal32 = ((int) d1 + 48) << 8 | ((int) d2 + 48);
} else {
intVal64 = (d2 + 48) << 16 | (d1 + 48);
intVal32 = ((int) d2 + 48) << 8 | ((int) d1 + 48);
}
int k = (int) (d1 * 10 + d2);
TWO_DIGITS_32_BITS[k] = (int) intVal64;
TWO_DIGITS_16_BITS[k] = (short) intVal32;
}
}
}
private static int writeDecimal(long value, int digitCnt, int e10, byte[] buf, int off) {
if ((value & 1) == 0 && value % 5 == 0) {
while (value % 100 == 0) {
digitCnt -= 2;
value /= 100;
if (digitCnt == 1) {
break;
}
}
if ((value & 1) == 0 && value % 5 == 0) {
if (value > 0) {
--digitCnt;
value /= 10;
}
}
}
// Whether to use Scientific notation
boolean useScientific = e10 < -3 || e10 >= 7; // !((e10 >= -3) && (e10 < 7));
if (useScientific) {
if (digitCnt == 1) {
buf[off] = (byte) (value + 48);
IOUtils.putShortUnaligned(buf, off + 1, DOT_ZERO_16);
off += 3; // .0
} else {
int pos = digitCnt - 2;
// get the first digit
long tl = POW10_LONG_VALUES[pos];
int fd = (int) (value / tl);
buf[off] = (byte) (fd + 48);
buf[off + 1] = '.';
off += 2;
long pointAfter = value - fd * tl;
// fill zeros
while (--pos > -1 && pointAfter < POW10_LONG_VALUES[pos]) {
buf[off++] = '0';
}
off = IOUtils.writeInt64(buf, off, pointAfter);
}
buf[off++] = 'E';
if (e10 < 0) {
buf[off++] = '-';
e10 = -e10;
}
if (e10 > 99) {
int n = (int) (e10 * 1374389535L >> 37); //e10 / 100;
buf[off] = (byte) (n + 48);
e10 = e10 - n * 100;
IOUtils.putShortUnaligned(buf, off + 1, TWO_DIGITS_16_BITS[e10]);
off += 3;
} else {
if (e10 > 9) {
IOUtils.putShortUnaligned(buf, off, TWO_DIGITS_16_BITS[e10]);
off += 2;
} else {
buf[off++] = (byte) (e10 + 48);
}
}
} else {
// for non-scientific notation such as 12345, write a decimal point when size = decimalExp.
if (e10 < 0) {
// -1/-2/-3
IOUtils.putShortUnaligned(buf, off, ZERO_DOT_16); // 0.
off += 2;
if (e10 == -2) {
buf[off++] = '0';
} else if (e10 == -3) {
IOUtils.putShortUnaligned(buf, off, ZERO_ZERO_16); // 00
off += 2;
}
off = writeInt64(buf, off, value);
} else {
// 0 - 6
int decimalPointPos = (digitCnt - 1) - e10;
if (decimalPointPos > 0) {
int pos = decimalPointPos - 1;
long tl = POW10_LONG_VALUES[pos];
int pointBefore = (int) (value / tl);
off = writeInt32(buf, off, pointBefore);
buf[off++] = '.';
long pointAfter = value - pointBefore * tl;
// fill zeros
while (--pos > -1 && pointAfter < POW10_LONG_VALUES[pos]) {
buf[off++] = '0';
}
off = writeInt64(buf, off, pointAfter);
} else {
off = writeInt64(buf, off, value);
int zeroCnt = -decimalPointPos;
if (zeroCnt > 0) {
for (int i = 0; i < zeroCnt; ++i) {
buf[off++] = '0';
}
}
IOUtils.putShortUnaligned(buf, off, DOT_ZERO_16); // .0
off += 2;
}
}
}
return off;
}
private static int writeDecimal(long value, int digitCnt, int e10, char[] buf, int off) {
if ((value & 1) == 0 && value % 5 == 0) {
while (value % 100 == 0) {
digitCnt -= 2;
value /= 100;
if (digitCnt == 1) {
break;
}
}
if ((value & 1) == 0 && value % 5 == 0) {
if (value > 0) {
--digitCnt;
value /= 10;
}
}
}
// whether to use scientific notation
boolean useScientific = e10 < -3 || e10 >= 7; // !((e10 >= -3) && (e10 < 7));
if (useScientific) {
if (digitCnt == 1) {
buf[off] = (char) (value + 48);
IOUtils.putIntUnaligned(buf, off + 1, DOT_ZERO_32);
off += 3;
} else {
int pos = digitCnt - 2;
// get the first digit
long tl = POW10_LONG_VALUES[pos];
int fd = (int) (value / tl);
buf[off] = (char) (fd + 48);
buf[off + 1] = '.';
off += 2;
long pointAfter = value - fd * tl;
// fill zeros
while (--pos > -1 && pointAfter < POW10_LONG_VALUES[pos]) {
buf[off++] = '0';
}
off = IOUtils.writeInt64(buf, off, pointAfter);
}
buf[off++] = 'E';
if (e10 < 0) {
buf[off++] = '-';
e10 = -e10;
}
if (e10 > 99) {
int n = (int) (e10 * 1374389535L >> 37); //
buf[off] = (char) (n + 48);
e10 = e10 - n * 100;
IOUtils.putIntUnaligned(buf, off + 1, TWO_DIGITS_32_BITS[e10]);
off += 3;
} else {
if (e10 > 9) {
IOUtils.putIntUnaligned(buf, off, TWO_DIGITS_32_BITS[e10]);
off += 2;
} else {
buf[off++] = (char) (e10 + 48);
}
}
} else {
// for non-scientific notation such as 12345, write a decimal point when size = decimalExp.
if (e10 < 0) {
// -1/-2/-3
IOUtils.putIntUnaligned(buf, off, ZERO_DOT_32); // 0.
off += 2;
if (e10 == -2) {
buf[off++] = '0';
} else if (e10 == -3) {
IOUtils.putIntUnaligned(buf, off, ZERO_ZERO_32); // 00
off += 2;
}
off = writeInt64(buf, off, value);
} else {
// 0 - 6
int decimalPointPos = (digitCnt - 1) - e10;
if (decimalPointPos > 0) {
int pos = decimalPointPos - 1;
long tl = POW10_LONG_VALUES[pos];
int pointBefore = (int) (value / tl);
off = writeInt64(buf, off, pointBefore);
buf[off++] = '.';
long pointAfter = value - pointBefore * tl;
// fill zeros
while (--pos > -1 && pointAfter < POW10_LONG_VALUES[pos]) {
buf[off++] = '0';
}
off = writeInt64(buf, off, pointAfter);
} else {
off = writeInt64(buf, off, value);
int zeroCnt = -decimalPointPos;
if (zeroCnt > 0) {
for (int i = 0; i < zeroCnt; ++i) {
buf[off++] = '0';
}
}
IOUtils.putIntUnaligned(buf, off, DOT_ZERO_32); // .0
off += 2;
}
}
}
return off;
}
}