2022-08-07 17:19:16 +01:00
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 ]
2023-08-07 09:28:49 +01:00
if '0' <= c && c <= '9' {
2022-08-07 17:19:16 +01:00
if trunk == - 1 {
2023-08-07 09:28:49 +01:00
if math . MaxUint64 / 10 < n {
2022-08-07 17:19:16 +01:00
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 ++
2023-08-07 09:28:49 +01:00
if e , expLen := ParseInt ( b [ i : ] ) ; 0 < expLen {
2022-08-07 17:19:16 +01:00
expExp = e
i += expLen
} else {
i = startExp
}
}
exp := expExp - mantExp
// copied from strconv/atof.go
if exp == 0 {
return f , i
2023-08-07 09:28:49 +01:00
} else if 0 < exp && exp <= 15 + 22 { // int * 10^k
2022-08-07 17:19:16 +01:00
// If exponent is big but number of digits is not,
// can move a few zeros into the integer part.
2023-08-07 09:28:49 +01:00
if 22 < exp {
2022-08-07 17:19:16 +01:00
f *= float64pow10 [ exp - 22 ]
exp = 22
}
2023-08-07 09:28:49 +01:00
if - 1e15 <= f && f <= 1e15 {
2022-08-07 17:19:16 +01:00
return f * float64pow10 [ exp ] , i
}
2023-08-07 09:28:49 +01:00
} else if - 22 <= exp && exp < 0 { // int / 10^k
2022-08-07 17:19:16 +01:00
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
2023-08-07 09:28:49 +01:00
if 0 < mantExp {
2022-08-07 17:19:16 +01:00
// 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 ++
}
2023-08-07 09:28:49 +01:00
if cap ( b ) < i + maxLen {
2022-08-07 17:19:16 +01:00
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
2023-08-07 09:28:49 +01:00
for 0 < mant {
2022-08-07 17:19:16 +01:00
if j == dot {
b [ j ] = '.'
j --
}
newMant := mant / 10
digit := mant - 10 * newMant
2023-08-07 09:28:49 +01:00
if zero && 0 < digit {
2022-08-07 17:19:16 +01:00
// 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)
2023-08-07 09:28:49 +01:00
if dot < j {
2022-08-07 17:19:16 +01:00
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
}
2023-08-07 09:28:49 +01:00
if dot < j {
2022-08-07 17:19:16 +01:00
// extra zeros behind the dot
2023-08-07 09:28:49 +01:00
for dot < j {
2022-08-07 17:19:16 +01:00
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
2023-08-07 09:28:49 +01:00
for 0 < exp {
2022-08-07 17:19:16 +01:00
newExp := exp / 10
digit := exp - 10 * newExp
j --
b [ j ] = '0' + byte ( digit )
exp = newExp
}
}
}
return b [ : i ] , true
}