-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnum.go
158 lines (126 loc) · 2.89 KB
/
num.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package xparse
import (
"errors"
"fmt"
"math"
"regexp"
"strings"
"github.com/spf13/cast"
)
const (
MaxUint = ^uint(0)
MinUint = 0
MaxInt = int(MaxUint >> 1)
MinInt = -MaxInt - 1
)
const (
_roundSignBase = 0.5
_precision = 2
)
var ErrNoNumbers = errors.New("no number found")
type NumOpts struct {
chars string
dft any
}
type NumOptFunc func(o *NumOpts)
// Chars the chars will be kept in CharToNum
// usually used as "decimal" like "."
func Chars(s string) NumOptFunc {
return func(o *NumOpts) {
o.chars = s
}
}
func Dft(i any) NumOptFunc {
return func(o *NumOpts) {
o.dft = i
}
}
func bindOpts(opt *NumOpts, opts ...NumOptFunc) {
for _, f := range opts {
f(opt)
}
}
// CharToNum extract `number+Chars` from source str
//
// the extracted value could be float value, so convert to float first, then return int by default
func CharToNum(rawStr string, opts ...NumOptFunc) (v any, e error) {
opt := NumOpts{chars: ".", dft: 1}
bindOpts(&opt, opts...)
a := "[0-9" + opt.chars + "]+"
re := regexp.MustCompile(a)
c := re.FindAllString(rawStr, -1)
joinStr := strings.Join(c, "")
if strings.Contains(opt.chars, ",") {
joinStr = strings.ReplaceAll(joinStr, ",", ".")
}
if joinStr == "" {
return joinStr, ErrNoNumbers
}
switch opt.dft.(type) {
case int:
v, e := cast.ToFloat64E(joinStr)
if e != nil {
return nil, e
}
return cast.ToIntE(v)
case int64:
// v could be float value
v, e := cast.ToFloat64E(joinStr)
if e != nil {
return nil, e
}
return cast.ToInt64E(v)
case float32:
return cast.ToFloat32E(joinStr)
case float64:
return cast.ToFloat64E(joinStr)
default:
return cast.ToStringE(joinStr)
}
}
func MustCharToNum(s string, opts ...NumOptFunc) (v any) {
v, e := CharToNum(s, opts...)
if e != nil {
PanicIfErr(e)
}
return v
}
func NumF64KMFromStr(str string, opts ...NumOptFunc) (i float64, b bool) {
unit := 1.0
if strings.Contains(strings.ToUpper(str), "K") {
unit = 1000.0
}
if strings.Contains(strings.ToUpper(str), "M") {
unit = 1000000.0
}
opt := NumOpts{chars: ".", dft: 1}
bindOpts(&opt, opts...)
if !strings.Contains(opt.chars, ".") {
opt.chars += "."
}
v := MustCharToNum(str, Chars(opt.chars), Dft(opt.dft))
if v == nil {
return
}
return cast.ToFloat64(v) * unit, true
}
func MustF64KMFromStr(str string, opts ...NumOptFunc) float64 {
if v, b := NumF64KMFromStr(str, opts...); !b {
panic(fmt.Sprintf("no number found in %s", str))
} else {
return v
}
}
func Round(num float64) int {
return int(num + math.Copysign(_roundSignBase, num))
}
func ToFixed(num float64, precision ...int) float64 {
p := FirstOrDefaultArgs(_precision, precision...)
output := math.Pow(10, float64(p)) //nolint:mnd
return float64(Round(num*output)) / output
}
func ToFixedStr(num float64, precision ...int) string {
p := FirstOrDefaultArgs(_precision, precision...)
output := ToFixed(num, p)
return cast.ToString(output)
}