gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

float.go (5697B)


      1 package strconv
      2 
      3 import (
      4 	"math"
      5 )
      6 
      7 var float64pow10 = []float64{
      8 	1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
      9 	1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
     10 	1e20, 1e21, 1e22,
     11 }
     12 
     13 // ParseFloat parses a byte-slice and returns the float it represents.
     14 // If an invalid character is encountered, it will stop there.
     15 func ParseFloat(b []byte) (float64, int) {
     16 	i := 0
     17 	neg := false
     18 	if i < len(b) && (b[i] == '+' || b[i] == '-') {
     19 		neg = b[i] == '-'
     20 		i++
     21 	}
     22 	start := i
     23 	dot := -1
     24 	trunk := -1
     25 	n := uint64(0)
     26 	for ; i < len(b); i++ {
     27 		c := b[i]
     28 		if c >= '0' && c <= '9' {
     29 			if trunk == -1 {
     30 				if n > math.MaxUint64/10 {
     31 					trunk = i
     32 				} else {
     33 					n *= 10
     34 					n += uint64(c - '0')
     35 				}
     36 			}
     37 		} else if dot == -1 && c == '.' {
     38 			dot = i
     39 		} else {
     40 			break
     41 		}
     42 	}
     43 	if i == start || i == start+1 && dot == start {
     44 		return 0.0, 0
     45 	}
     46 
     47 	f := float64(n)
     48 	if neg {
     49 		f = -f
     50 	}
     51 
     52 	mantExp := int64(0)
     53 	if dot != -1 {
     54 		if trunk == -1 {
     55 			trunk = i
     56 		}
     57 		mantExp = int64(trunk - dot - 1)
     58 	} else if trunk != -1 {
     59 		mantExp = int64(trunk - i)
     60 	}
     61 	expExp := int64(0)
     62 	if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
     63 		startExp := i
     64 		i++
     65 		if e, expLen := ParseInt(b[i:]); expLen > 0 {
     66 			expExp = e
     67 			i += expLen
     68 		} else {
     69 			i = startExp
     70 		}
     71 	}
     72 	exp := expExp - mantExp
     73 
     74 	// copied from strconv/atof.go
     75 	if exp == 0 {
     76 		return f, i
     77 	} else if exp > 0 && exp <= 15+22 { // int * 10^k
     78 		// If exponent is big but number of digits is not,
     79 		// can move a few zeros into the integer part.
     80 		if exp > 22 {
     81 			f *= float64pow10[exp-22]
     82 			exp = 22
     83 		}
     84 		if f <= 1e15 && f >= -1e15 {
     85 			return f * float64pow10[exp], i
     86 		}
     87 	} else if exp < 0 && exp >= -22 { // int / 10^k
     88 		return f / float64pow10[-exp], i
     89 	}
     90 	f *= math.Pow10(int(-mantExp))
     91 	return f * math.Pow10(int(expExp)), i
     92 }
     93 
     94 const log2 = 0.3010299956639812
     95 
     96 func float64exp(f float64) int {
     97 	exp2 := 0
     98 	if f != 0.0 {
     99 		x := math.Float64bits(f)
    100 		exp2 = int(x>>(64-11-1))&0x7FF - 1023 + 1
    101 	}
    102 
    103 	exp10 := float64(exp2) * log2
    104 	if exp10 < 0 {
    105 		exp10 -= 1.0
    106 	}
    107 	return int(exp10)
    108 }
    109 
    110 // 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.
    111 func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
    112 	if math.IsNaN(f) || math.IsInf(f, 0) {
    113 		return b, false
    114 	}
    115 
    116 	neg := false
    117 	if f < 0.0 {
    118 		f = -f
    119 		neg = true
    120 	}
    121 	if prec < 0 || 17 < prec {
    122 		prec = 17 // maximum number of significant digits in double
    123 	}
    124 	prec -= float64exp(f) // number of digits in front of the dot
    125 	f *= math.Pow10(prec)
    126 
    127 	// calculate mantissa and exponent
    128 	mant := int64(f)
    129 	mantLen := LenInt(mant)
    130 	mantExp := mantLen - prec - 1
    131 	if mant == 0 {
    132 		return append(b, '0'), true
    133 	}
    134 
    135 	// expLen is zero for positive exponents, because positive exponents are determined later on in the big conversion loop
    136 	exp := 0
    137 	expLen := 0
    138 	if mantExp > 0 {
    139 		// positive exponent is determined in the loop below
    140 		// but if we initially decreased the exponent to fit in an integer, we can't set the new exponent in the loop alone,
    141 		// since the number of zeros at the end determines the positive exponent in the loop, and we just artificially lost zeros
    142 		if prec < 0 {
    143 			exp = mantExp
    144 		}
    145 		expLen = 1 + LenInt(int64(exp)) // e + digits
    146 	} else if mantExp < -3 {
    147 		exp = mantExp
    148 		expLen = 2 + LenInt(int64(exp)) // e + minus + digits
    149 	} else if mantExp < -1 {
    150 		mantLen += -mantExp - 1 // extra zero between dot and first digit
    151 	}
    152 
    153 	// reserve space in b
    154 	i := len(b)
    155 	maxLen := 1 + mantLen + expLen // dot + mantissa digits + exponent
    156 	if neg {
    157 		maxLen++
    158 	}
    159 	if i+maxLen > cap(b) {
    160 		b = append(b, make([]byte, maxLen)...)
    161 	} else {
    162 		b = b[:i+maxLen]
    163 	}
    164 
    165 	// write to string representation
    166 	if neg {
    167 		b[i] = '-'
    168 		i++
    169 	}
    170 
    171 	// big conversion loop, start at the end and move to the front
    172 	// initially print trailing zeros and remove them later on
    173 	// 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
    174 	zero := true
    175 	last := i + mantLen      // right-most position of digit that is non-zero + dot
    176 	dot := last - prec - exp // position of dot
    177 	j := last
    178 	for mant > 0 {
    179 		if j == dot {
    180 			b[j] = '.'
    181 			j--
    182 		}
    183 		newMant := mant / 10
    184 		digit := mant - 10*newMant
    185 		if zero && digit > 0 {
    186 			// first non-zero digit, if we are still behind the dot we can trim the end to this position
    187 			// otherwise trim to the dot (including the dot)
    188 			if j > dot {
    189 				i = j + 1
    190 				// decrease negative exponent further to get rid of dot
    191 				if exp < 0 {
    192 					newExp := exp - (j - dot)
    193 					// getting rid of the dot shouldn't lower the exponent to more digits (e.g. -9 -> -10)
    194 					if LenInt(int64(newExp)) == LenInt(int64(exp)) {
    195 						exp = newExp
    196 						dot = j
    197 						j--
    198 						i--
    199 					}
    200 				}
    201 			} else {
    202 				i = dot
    203 			}
    204 			last = j
    205 			zero = false
    206 		}
    207 		b[j] = '0' + byte(digit)
    208 		j--
    209 		mant = newMant
    210 	}
    211 
    212 	if j > dot {
    213 		// extra zeros behind the dot
    214 		for j > dot {
    215 			b[j] = '0'
    216 			j--
    217 		}
    218 		b[j] = '.'
    219 	} else if last+3 < dot {
    220 		// add positive exponent because we have 3 or more zeros in front of the dot
    221 		i = last + 1
    222 		exp = dot - last - 1
    223 	} else if j == dot {
    224 		// handle 0.1
    225 		b[j] = '.'
    226 	}
    227 
    228 	// exponent
    229 	if exp != 0 {
    230 		if exp == 1 {
    231 			b[i] = '0'
    232 			i++
    233 		} else if exp == 2 {
    234 			b[i] = '0'
    235 			b[i+1] = '0'
    236 			i += 2
    237 		} else {
    238 			b[i] = 'e'
    239 			i++
    240 			if exp < 0 {
    241 				b[i] = '-'
    242 				i++
    243 				exp = -exp
    244 			}
    245 			i += LenInt(int64(exp))
    246 			j := i
    247 			for exp > 0 {
    248 				newExp := exp / 10
    249 				digit := exp - 10*newExp
    250 				j--
    251 				b[j] = '0' + byte(digit)
    252 				exp = newExp
    253 			}
    254 		}
    255 	}
    256 	return b[:i], true
    257 }