252 lines
5.3 KiB
Go
252 lines
5.3 KiB
Go
|
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
|
||
|
}
|