123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- package 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,
- }
- // ParseFloat 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++
- }
- start := i
- dot := -1
- trunk := -1
- n := uint64(0)
- for ; i < len(b); i++ {
- c := b[i]
- if '0' <= c && c <= '9' {
- if trunk == -1 {
- if math.MaxUint64/10 < n {
- trunk = i
- } else {
- n *= 10
- n += uint64(c - '0')
- }
- }
- } else if dot == -1 && c == '.' {
- dot = i
- } else {
- break
- }
- }
- if i == start || i == start+1 && dot == start {
- return 0.0, 0
- }
- 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') {
- startExp := i
- i++
- if e, expLen := ParseInt(b[i:]); 0 < expLen {
- expExp = e
- i += expLen
- } else {
- i = startExp
- }
- }
- exp := expExp - mantExp
- // copied from strconv/atof.go
- if exp == 0 {
- return f, i
- } else if 0 < exp && 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 22 < exp {
- f *= float64pow10[exp-22]
- exp = 22
- }
- if -1e15 <= f && f <= 1e15 {
- return f * float64pow10[exp], i
- }
- } else if -22 <= exp && exp < 0 { // int / 10^k
- return f / float64pow10[-exp], i
- }
- f *= math.Pow10(int(-mantExp))
- return f * math.Pow10(int(expExp)), i
- }
- const log2 = 0.3010299956639812
- 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)
- }
- // AppendFloat appends a float to `b` with precision `prec`. It returns the new slice and whether successful or not. Precision is the number of decimals to display, thus prec + 1 == number of significant digits.
- func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
- if math.IsNaN(f) || math.IsInf(f, 0) {
- return b, false
- }
- neg := false
- if f < 0.0 {
- f = -f
- neg = true
- }
- if prec < 0 || 17 < prec {
- prec = 17 // maximum number of significant digits in double
- }
- 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 0 < mantExp {
- // 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 cap(b) < i+maxLen {
- 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 0 < mant {
- if j == dot {
- b[j] = '.'
- j--
- }
- newMant := mant / 10
- digit := mant - 10*newMant
- if zero && 0 < digit {
- // 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 dot < j {
- 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 dot < j {
- // extra zeros behind the dot
- for dot < j {
- 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 0 < exp {
- newExp := exp / 10
- digit := exp - 10*newExp
- j--
- b[j] = '0' + byte(digit)
- exp = newExp
- }
- }
- }
- return b[:i], true
- }
|