package strconv // import "github.com/tdewolff/parse/strconv" import "math" var float64pow10 = []float64{ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, } // Float parses a byte-slice and returns the float it represents. // If an invalid character is encountered, it will stop there. func ParseFloat(b []byte) (float64, int) { i := 0 neg := false if i < len(b) && (b[i] == '+' || b[i] == '-') { neg = b[i] == '-' i++ } dot := -1 trunk := -1 n := uint64(0) for ; i < len(b); i++ { c := b[i] if c >= '0' && c <= '9' { if trunk == -1 { if n > math.MaxUint64/10 { trunk = i } else { n *= 10 n += uint64(c - '0') } } } else if dot == -1 && c == '.' { dot = i } else { break } } f := float64(n) if neg { f = -f } mantExp := int64(0) if dot != -1 { if trunk == -1 { trunk = i } mantExp = int64(trunk - dot - 1) } else if trunk != -1 { mantExp = int64(trunk - i) } expExp := int64(0) if i < len(b) && (b[i] == 'e' || b[i] == 'E') { i++ if e, expLen := ParseInt(b[i:]); expLen > 0 { expExp = e i += expLen } } exp := expExp - mantExp // copied from strconv/atof.go if exp == 0 { return f, i } else if exp > 0 && exp <= 15+22 { // int * 10^k // If exponent is big but number of digits is not, // can move a few zeros into the integer part. if exp > 22 { f *= float64pow10[exp-22] exp = 22 } if f <= 1e15 && f >= -1e15 { return f * float64pow10[exp], i } } else if exp < 0 && exp >= -22 { // int / 10^k return f / float64pow10[-exp], i } f *= math.Pow10(int(-mantExp)) return f * math.Pow10(int(expExp)), i } const log2 = 0.301029995 const int64maxlen = 18 func float64exp(f float64) int { exp2 := 0 if f != 0.0 { x := math.Float64bits(f) exp2 = int(x>>(64-11-1))&0x7FF - 1023 + 1 } exp10 := float64(exp2) * log2 if exp10 < 0 { exp10 -= 1.0 } return int(exp10) } func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) { if math.IsNaN(f) || math.IsInf(f, 0) { return b, false } else if prec >= int64maxlen { return b, false } neg := false if f < 0.0 { f = -f neg = true } if prec == -1 { prec = int64maxlen - 1 } prec -= float64exp(f) // number of digits in front of the dot f *= math.Pow10(prec) // calculate mantissa and exponent mant := int64(f) mantLen := LenInt(mant) mantExp := mantLen - prec - 1 if mant == 0 { return append(b, '0'), true } // expLen is zero for positive exponents, because positive exponents are determined later on in the big conversion loop exp := 0 expLen := 0 if mantExp > 0 { // positive exponent is determined in the loop below // but if we initially decreased the exponent to fit in an integer, we can't set the new exponent in the loop alone, // since the number of zeros at the end determines the positive exponent in the loop, and we just artificially lost zeros if prec < 0 { exp = mantExp } expLen = 1 + LenInt(int64(exp)) // e + digits } else if mantExp < -3 { exp = mantExp expLen = 2 + LenInt(int64(exp)) // e + minus + digits } else if mantExp < -1 { mantLen += -mantExp - 1 // extra zero between dot and first digit } // reserve space in b i := len(b) maxLen := 1 + mantLen + expLen // dot + mantissa digits + exponent if neg { maxLen++ } if i+maxLen > cap(b) { b = append(b, make([]byte, maxLen)...) } else { b = b[:i+maxLen] } // write to string representation if neg { b[i] = '-' i++ } // big conversion loop, start at the end and move to the front // initially print trailing zeros and remove them later on // for example if the first non-zero digit is three positions in front of the dot, it will overwrite the zeros with a positive exponent zero := true last := i + mantLen // right-most position of digit that is non-zero + dot dot := last - prec - exp // position of dot j := last for mant > 0 { if j == dot { b[j] = '.' j-- } newMant := mant / 10 digit := mant - 10*newMant if zero && digit > 0 { // first non-zero digit, if we are still behind the dot we can trim the end to this position // otherwise trim to the dot (including the dot) if j > dot { i = j + 1 // decrease negative exponent further to get rid of dot if exp < 0 { newExp := exp - (j - dot) // getting rid of the dot shouldn't lower the exponent to more digits (e.g. -9 -> -10) if LenInt(int64(newExp)) == LenInt(int64(exp)) { exp = newExp dot = j j-- i-- } } } else { i = dot } last = j zero = false } b[j] = '0' + byte(digit) j-- mant = newMant } if j > dot { // extra zeros behind the dot for j > dot { b[j] = '0' j-- } b[j] = '.' } else if last+3 < dot { // add positive exponent because we have 3 or more zeros in front of the dot i = last + 1 exp = dot - last - 1 } else if j == dot { // handle 0.1 b[j] = '.' } // exponent if exp != 0 { if exp == 1 { b[i] = '0' i++ } else if exp == 2 { b[i] = '0' b[i+1] = '0' i += 2 } else { b[i] = 'e' i++ if exp < 0 { b[i] = '-' i++ exp = -exp } i += LenInt(int64(exp)) j := i for exp > 0 { newExp := exp / 10 digit := exp - 10*newExp j-- b[j] = '0' + byte(digit) exp = newExp } } } return b[:i], true }