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  }