go_dreamfactory/lego/sys/codec/utils/float.go
2022-07-11 15:54:54 +08:00

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
}