package cc.blynk.utils;
/**
* Optimized but less precise double parsing method. It is also doesn't spam objects.
*
* The Blynk Project.
* Created by Dmitriy Dumanskiy.
* Created on 23.01.17.
*/
public class NumberUtil {
public static final double NO_RESULT = Double.MIN_VALUE;
// Precompute Math.pow(10, n) as table:
private final static int POW_RANGE = 256;
private final static double[] POS_EXPS = new double[POW_RANGE];
private final static double[] NEG_EXPS = new double[POW_RANGE];
static {
for (int i = 0; i < POW_RANGE; i++) {
POS_EXPS[i] = Math.pow(10., i);
NEG_EXPS[i] = Math.pow(10., -i);
}
}
// Calculate the value of the specified exponent - reuse a precalculated value if possible
private static double getPow10(final int exp) {
if (exp > -POW_RANGE) {
if (exp <= 0) {
return NEG_EXPS[-exp];
} else if (exp < POW_RANGE) {
return POS_EXPS[exp];
}
}
return Math.pow(10., exp);
}
public static double parseDouble(final String s) throws NumberFormatException {
return parseDouble(s, 0, s.length());
}
private static double parseDouble(final String s,
final int offset, final int end) throws NumberFormatException {
int off = offset;
int len = end - offset;
if (len == 0) {
return NO_RESULT;
}
char ch;
boolean numSign = true;
ch = s.charAt(off);
if (ch == '+') {
off++;
len--;
} else if (ch == '-') {
numSign = false;
off++;
len--;
}
double number;
boolean error = true;
int startOffset = off;
double dval;
// TODO: check too many digits (overflow)
for (dval = 0d; (len > 0) && ((ch = s.charAt(off)) >= '0') && (ch <= '9');) {
dval *= 10d;
dval += ch - '0';
off++;
len--;
}
int numberLength = off - startOffset;
number = dval;
if (numberLength > 0) {
error = false;
}
// Check for fractional values after decimal
if ((len > 0) && (s.charAt(off) == '.')) {
off++;
len--;
startOffset = off;
// TODO: check too many digits (overflow)
for (dval = 0d; (len > 0) && ((ch = s.charAt(off)) >= '0') && (ch <= '9');) {
dval *= 10d;
dval += ch - '0';
off++;
len--;
}
numberLength = off - startOffset;
if (numberLength > 0) {
// TODO: try factorizing pow10 with exponent below: only 1 long + operation
number += getPow10(-numberLength) * dval;
error = false;
}
}
if (error) {
return NO_RESULT;
}
// Look for an exponent
if (len > 0) {
// note: ignore any non-digit character at end:
if ((ch = s.charAt(off)) == 'e' || ch == 'E') {
off++;
len--;
if (len > 0) {
boolean expSign = true;
ch = s.charAt(off);
if (ch == '+') {
off++;
len--;
} else if (ch == '-') {
expSign = false;
off++;
len--;
}
int exponent;
// note: ignore any non-digit character at end:
for (exponent = 0; (len > 0) && ((ch = s.charAt(off)) >= '0') && (ch <= '9');) {
exponent *= 10;
exponent += ch - '0';
off++;
len--;
}
// TODO: check exponent < 1024 (overflow)
if (!expSign) {
exponent = -exponent;
}
// For very small numbers we try to miminize
// effects of denormalization.
if (exponent > -300) {
// TODO: cache Math.pow ?? see web page
number *= getPow10(exponent);
} else {
number = 1.0E-300 * (number * getPow10(exponent + 300));
}
}
}
}
// check other characters:
if (len > 0) {
return NO_RESULT;
}
return (numSign) ? number : -number;
}
}