github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/encoding/fixedn/decimal.go (about) 1 package fixedn 2 3 import ( 4 "errors" 5 "fmt" 6 "math/big" 7 "strconv" 8 "strings" 9 ) 10 11 const maxAllowedPrecision = 16 12 13 // ErrInvalidFormat is returned when decimal format is invalid. 14 var ErrInvalidFormat = errors.New("invalid decimal format") 15 16 var _pow10 []*big.Int 17 18 func init() { 19 var p = int64(1) 20 for i := 0; i <= maxAllowedPrecision; i++ { 21 _pow10 = append(_pow10, big.NewInt(p)) 22 p *= 10 23 } 24 } 25 26 func pow10(n int) *big.Int { 27 last := len(_pow10) - 1 28 if n <= last { 29 return _pow10[n] 30 } 31 p := new(big.Int) 32 p.Mul(_pow10[last], _pow10[1]) 33 for i := last + 1; i < n; i++ { 34 p.Mul(p, _pow10[1]) 35 } 36 return p 37 } 38 39 // ToString converts a big decimal with the specified precision to a string. 40 func ToString(bi *big.Int, precision int) string { 41 var dp, fp big.Int 42 dp.QuoRem(bi, pow10(precision), &fp) 43 44 var s = dp.String() 45 if fp.Sign() == 0 { 46 return s 47 } 48 frac := fp.Uint64() 49 trimmed := 0 50 for ; frac%10 == 0; frac /= 10 { 51 trimmed++ 52 } 53 return s + "." + fmt.Sprintf("%0"+strconv.FormatUint(uint64(precision-trimmed), 10)+"d", frac) 54 } 55 56 // FromString converts a string to a big decimal with the specified precision. 57 func FromString(s string, precision int) (*big.Int, error) { 58 parts := strings.SplitN(s, ".", 2) 59 bi, ok := new(big.Int).SetString(parts[0], 10) 60 if !ok { 61 return nil, ErrInvalidFormat 62 } 63 bi.Mul(bi, pow10(precision)) 64 if len(parts) == 1 { 65 return bi, nil 66 } 67 68 if len(parts[1]) > precision { 69 return nil, ErrInvalidFormat 70 } 71 fp, ok := new(big.Int).SetString(parts[1], 10) 72 if !ok { 73 return nil, ErrInvalidFormat 74 } 75 fp.Mul(fp, pow10(precision-len(parts[1]))) 76 if bi.Sign() == -1 { 77 return bi.Sub(bi, fp), nil 78 } 79 return bi.Add(bi, fp), nil 80 }