package utils import ( "errors" "fmt" "math" "math/big" "strconv" "strings" "unsafe" ) var pow10 []uint64 var floatDigits []int8 const invalidCharForNumber = int8(-1) const endOfNumber = int8(-2) const dotInNumber = int8(-3) func init() { pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000} floatDigits = make([]int8, 256) for i := 0; i < len(floatDigits); i++ { floatDigits[i] = invalidCharForNumber } for i := int8('0'); i <= int8('9'); i++ { floatDigits[i] = i - int8('0') } floatDigits[','] = endOfNumber floatDigits[']'] = endOfNumber floatDigits['}'] = endOfNumber floatDigits[' '] = endOfNumber floatDigits['\t'] = endOfNumber floatDigits['\n'] = endOfNumber floatDigits['.'] = dotInNumber } //Read Float For String------------------------------------------------------------------------------------------------------------------------------------------------------------- //从字符串中读取float 对象 func ReadBigFloatForString(buf []byte) (ret *big.Float, n int, err error) { var str string if str, n, err = readNumberAsString(buf); err == nil { return } prec := 64 if len(str) > prec { prec = len(str) } ret, _, err = big.ParseFloat(str, 10, uint(prec), big.ToZero) return } func ReadFloat32ForString(buf []byte) (ret float32, n int, err error) { if buf[0] == '-' { ret, n, err = readPositiveFloat32(buf[1:]) ret = -ret return } return readPositiveFloat32(buf) } func ReadFloat64ForString(buf []byte) (ret float64, n int, err error) { if buf[0] == '-' { ret, n, err = readPositiveFloat64(buf[1:]) ret = -ret return } return readPositiveFloat64(buf) } //读取float32 func readPositiveFloat32(buf []byte) (ret float32, n int, err error) { i := 0 // first char if len(buf) == 0 { return readFloat32SlowPath(buf) } c := buf[i] i++ ind := floatDigits[c] switch ind { case invalidCharForNumber: return readFloat32SlowPath(buf) case endOfNumber: err = errors.New("readFloat32 empty number") return case dotInNumber: err = errors.New("readFloat32 leading dot is invalid") return case 0: if i == len(buf) { return readFloat32SlowPath(buf) } c = buf[i] switch c { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': err = errors.New("readFloat32 leading zero is invalid") return } } value := uint64(ind) // chars before dot non_decimal_loop: for ; i < len(buf); i++ { c = buf[i] ind := floatDigits[c] switch ind { case invalidCharForNumber: return readFloat32SlowPath(buf) case endOfNumber: n = i ret = float32(value) return case dotInNumber: break non_decimal_loop } if value > uint64SafeToMultiple10 { return readFloat32SlowPath(buf) } value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; } // chars after dot if c == '.' { i++ decimalPlaces := 0 if i == len(buf) { return readFloat32SlowPath(buf) } for ; i < len(buf); i++ { c = buf[i] ind := floatDigits[c] switch ind { case endOfNumber: if decimalPlaces > 0 && decimalPlaces < len(pow10) { n = i ret = float32(float64(value) / float64(pow10[decimalPlaces])) return } // too many decimal places return readFloat32SlowPath(buf) case invalidCharForNumber, dotInNumber: return readFloat32SlowPath(buf) } decimalPlaces++ if value > uint64SafeToMultiple10 { return readFloat32SlowPath(buf) } value = (value << 3) + (value << 1) + uint64(ind) } } return readFloat32SlowPath(buf) } func readFloat32SlowPath(buf []byte) (ret float32, n int, err error) { var str string if str, n, err = readNumberAsString(buf); err != nil { return } errMsg := validateFloat(str) if errMsg != "" { err = errors.New(errMsg) return } var val float64 if val, err = strconv.ParseFloat(str, 32); err != nil { return } ret = float32(val) return } //读取float64 func readPositiveFloat64(buf []byte) (ret float64, n int, err error) { i := 0 // first char if i == len(buf) { return readFloat64SlowPath(buf) } c := buf[i] i++ ind := floatDigits[c] switch ind { case invalidCharForNumber: return readFloat64SlowPath(buf) case endOfNumber: err = errors.New("readFloat64 empty number") return case dotInNumber: err = errors.New("readFloat64 leading dot is invalid") return case 0: if i == len(buf) { return readFloat64SlowPath(buf) } c = buf[i] switch c { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': err = errors.New("readFloat64 leading zero is invalid") return } } value := uint64(ind) // chars before dot non_decimal_loop: for ; i < len(buf); i++ { c = buf[i] ind := floatDigits[c] switch ind { case invalidCharForNumber: return readFloat64SlowPath(buf) case endOfNumber: n = i ret = float64(value) return case dotInNumber: break non_decimal_loop } if value > uint64SafeToMultiple10 { return readFloat64SlowPath(buf) } value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; } // chars after dot if c == '.' { i++ decimalPlaces := 0 if i == len(buf) { return readFloat64SlowPath(buf) } for ; i < len(buf); i++ { c = buf[i] ind := floatDigits[c] switch ind { case endOfNumber: if decimalPlaces > 0 && decimalPlaces < len(pow10) { n = i ret = float64(value) / float64(pow10[decimalPlaces]) return } // too many decimal places return readFloat64SlowPath(buf) case invalidCharForNumber, dotInNumber: return readFloat64SlowPath(buf) } decimalPlaces++ if value > uint64SafeToMultiple10 { return readFloat64SlowPath(buf) } value = (value << 3) + (value << 1) + uint64(ind) if value > maxFloat64 { return readFloat64SlowPath(buf) } } } return readFloat64SlowPath(buf) } func readFloat64SlowPath(buf []byte) (ret float64, n int, err error) { var str string if str, n, err = readNumberAsString(buf); err != nil { return } errMsg := validateFloat(str) if errMsg != "" { err = fmt.Errorf("readFloat64SlowPath:%v", errMsg) return } ret, err = strconv.ParseFloat(str, 64) return } //读取字符串数字 func readNumberAsString(buf []byte) (ret string, n int, err error) { strBuf := [16]byte{} str := strBuf[0:0] load_loop: for i := 0; i < len(buf); i++ { c := buf[i] switch c { case '+', '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': str = append(str, c) continue default: n = i break load_loop } } if len(str) == 0 { err = errors.New("readNumberAsString invalid number") } ret = *(*string)(unsafe.Pointer(&str)) return } //字符串float 错误检验 func validateFloat(str string) string { if len(str) == 0 { return "empty number" } if str[0] == '-' { return "-- is not valid" } dotPos := strings.IndexByte(str, '.') if dotPos != -1 { if dotPos == len(str)-1 { return "dot can not be last character" } switch str[dotPos+1] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': default: return "missing digit after dot" } } return "" } //Write Float To String------------------------------------------------------------------------------------------------------------------------------------------------------------- func WriteFloat32ToString(buff *[]byte, val float32) (err error) { if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { err = fmt.Errorf("unsupported value: %f", val) return } abs := math.Abs(float64(val)) fmt := byte('f') if abs != 0 { if float32(abs) < 1e-6 || float32(abs) >= 1e21 { fmt = 'e' } } *buff = strconv.AppendFloat(*buff, float64(val), fmt, -1, 32) return } //float32 写入只有 6 位精度的流,但速度要快得多 func WriteFloat32LossyToString(buf *[]byte, val float32) (err error) { if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { err = fmt.Errorf("unsupported value: %f", val) return } if val < 0 { WriteChar(buf, '-') val = -val } if val > 0x4ffffff { WriteFloat32ToString(buf, val) return } precision := 6 exp := uint64(1000000) // 6 lval := uint64(float64(val)*float64(exp) + 0.5) WriteUint64ToString(buf, lval/exp) fval := lval % exp if fval == 0 { return } WriteChar(buf, '.') for p := precision - 1; p > 0 && fval < pow10[p]; p-- { WriteChar(buf, '0') } WriteUint64ToString(buf, fval) temp := *buf for temp[len(temp)-1] == '0' { temp = temp[:len(temp)-1] } buf = &temp return } func WriteFloat64ToString(buf *[]byte, val float64) (err error) { if math.IsInf(val, 0) || math.IsNaN(val) { err = fmt.Errorf("unsupported value: %f", val) return } abs := math.Abs(val) fmt := byte('f') // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. if abs != 0 { if abs < 1e-6 || abs >= 1e21 { fmt = 'e' } } *buf = strconv.AppendFloat(*buf, float64(val), fmt, -1, 64) return } //将 float64 写入只有 6 位精度的流,但速度要快得多 func WriteFloat64LossyToString(buf *[]byte, val float64) (err error) { if math.IsInf(val, 0) || math.IsNaN(val) { err = fmt.Errorf("unsupported value: %f", val) return } if val < 0 { WriteChar(buf, '-') val = -val } if val > 0x4ffffff { WriteFloat64ToString(buf, val) return } precision := 6 exp := uint64(1000000) // 6 lval := uint64(val*float64(exp) + 0.5) WriteUint64ToString(buf, lval/exp) fval := lval % exp if fval == 0 { return } WriteChar(buf, '.') for p := precision - 1; p > 0 && fval < pow10[p]; p-- { WriteChar(buf, '0') } WriteUint64ToString(buf, fval) temp := *buf for temp[len(temp)-1] == '0' { temp = temp[:len(temp)-1] } buf = &temp return }