417 lines
9.4 KiB
Go
417 lines
9.4 KiB
Go
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
|
|
}
|