diff --git a/formula/dma.go b/formula/dma.go new file mode 100644 index 0000000000000000000000000000000000000000..7e561fc6bc316f5dc6a494259dba5b6382c17c5f --- /dev/null +++ b/formula/dma.go @@ -0,0 +1,53 @@ +package formula + +import ( + "gitee.com/quant1x/pandas" + "gitee.com/quant1x/pandas/stat" +) + +// DMA 返回动态移动平均 +// +// 求S的动态移动平均, A作平滑因子,必须 0= 1.05 { + t = stat.DType(1) + } + x0[idx] = t + }) + n := BARSLAST(pandas.NewSeries(pandas.SERIES_TYPE_FLOAT32, "", x0)) + fmt.Println(n[len(n)-10:]) + x := DMA(CLOSE, pandas.NewSeries(pandas.SERIES_TYPE_DTYPE, "", n)) + + //x := EMA(CLOSE, 7) + sx := pandas.NewSeries(pandas.SERIES_TYPE_DTYPE, "x", x) + df = pandas.NewDataFrame(CLOSE, sx) + fmt.Println(df) +} diff --git a/generic_ewm.go b/generic_ewm.go index 3ad3e36b3b072ef5662239d2f071967677b40227..7503b903f485824facae48cac5398274271d8702 100644 --- a/generic_ewm.go +++ b/generic_ewm.go @@ -9,21 +9,21 @@ type AlphaType int // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.ewm.html const ( - // AlphaNil Specify smoothing factor α directly, 0<α≤1. - AlphaNil AlphaType = iota + // AlphaAlpha Specify smoothing factor α directly, 0<α≤1. + AlphaAlpha AlphaType = iota // AlphaCom Specify decay in terms of center of mass, α=1/(1+com), for com ≥ 0. AlphaCom // AlphaSpan Specify decay in terms of span, α=2/(span+1), for span ≥ 1. AlphaSpan - // AlphaHalflife Specify decay in terms of half-life, α=1−exp(−ln(2)/halflife), for halflife > 0. - AlphaHalflife + // AlphaHalfLife Specify decay in terms of half-life, α=1−exp(−ln(2)/halflife), for halflife > 0. + AlphaHalfLife ) // EW (Factor) 指数加权(EW)计算Alpha 结构属性非0即为有效启动同名算法 type EW struct { Com stat.DType // 根据质心指定衰减 Span stat.DType // 根据跨度指定衰减 - Halflife stat.DType // 根据半衰期指定衰减 + HalfLife stat.DType // 根据半衰期指定衰减 Alpha stat.DType // 直接指定的平滑因子α Adjust bool // 除以期初的衰减调整系数以核算 相对权重的不平衡(将 EWMA 视为移动平均线) IgnoreNA bool // 计算权重时忽略缺失值 @@ -44,7 +44,7 @@ type ExponentialMovingWindow struct { // EWM provides exponential weighted calculations. func (s *NDFrame) EWM(alpha EW) ExponentialMovingWindow { - atype := AlphaNil + atype := AlphaAlpha param := 0.00 adjust := alpha.Adjust ignoreNA := alpha.IgnoreNA @@ -54,11 +54,11 @@ func (s *NDFrame) EWM(alpha EW) ExponentialMovingWindow { } else if alpha.Span != 0 { atype = AlphaSpan param = alpha.Span - } else if alpha.Halflife != 0 { - atype = AlphaHalflife - param = alpha.Halflife + } else if alpha.HalfLife != 0 { + atype = AlphaHalfLife + param = alpha.HalfLife } else { - atype = AlphaNil + atype = AlphaAlpha param = alpha.Alpha } @@ -77,7 +77,7 @@ func (w ExponentialMovingWindow) Mean() Series { var alpha stat.DType switch w.atype { - case AlphaNil: + case AlphaAlpha: if w.param <= 0 { panic("alpha param must be > 0") } @@ -95,7 +95,7 @@ func (w ExponentialMovingWindow) Mean() Series { } alpha = 2 / (w.param + 1) - case AlphaHalflife: + case AlphaHalfLife: if w.param <= 0 { panic("halflife param must be > 0") } diff --git a/stat/cumsum.go b/stat/cumsum.go index 2879a2dbd95f3e6f8cc2cb789cc2ab9cae2fa374..a1d43ea596e4710e678d47dcfa7186b47761adf3 100644 --- a/stat/cumsum.go +++ b/stat/cumsum.go @@ -4,7 +4,6 @@ import ( "github.com/viterin/vek" "github.com/viterin/vek/vek32" "golang.org/x/exp/slices" - "unsafe" ) // CumSum 计算累和 @@ -15,12 +14,12 @@ func CumSum[T StatType](f []T) []T { var d any var s any s = f - bitSize := unsafe.Sizeof(f[0]) - if bitSize == 4 { - d = vek32.CumSum(s.([]float32)) - } else if bitSize == 8 { - d = vek.CumSum(s.([]float64)) - } else { + switch fs := s.(type) { + case []float32: + d = vek32.CumSum(fs) + case []float64: + d = vek.CumSum(fs) + default: // 剩下的就是int32和int64, 循环吧 sum := T(0) x := slices.Clone(f) diff --git a/stat/fillna.go b/stat/fillna.go new file mode 100644 index 0000000000000000000000000000000000000000..a0201198d6bff90853095a046147a7171db9579d --- /dev/null +++ b/stat/fillna.go @@ -0,0 +1,132 @@ +package stat + +import "golang.org/x/exp/slices" + +// Fill 填充 +// +// Fill NA/NaN values using the specified method. +// Parameters +// ---------- +// value : scalar, dict, Series, or DataFrame +// Value to use to fill holes (e.g. 0), alternately a +// dict/Series/DataFrame of values specifying which value to use for +// each index (for a Series) or column (for a DataFrame). Values not +// in the dict/Series/DataFrame will not be filled. This value cannot +// be a list. +// method : {{'backfill', 'bfill', 'pad', 'ffill', None}}, default None +// Method to use for filling holes in reindexed Series +// pad / ffill: propagate last valid observation forward to next valid +// backfill / bfill: use next valid observation to fill gap. +// axis : {axes_single_arg} +// Axis along which to fill missing values. For `Series` +// this parameter is unused and defaults to 0. +// inplace : bool, default False [√] +// If True, fill in-place. Note: this will modify any +// other views on this object (e.g., a no-copy slice for a column in a +// DataFrame). +// limit : int, default None +// If method is specified, this is the maximum number of consecutive +// NaN values to forward/backward fill. In other words, if there is +// a gap with more than this number of consecutive NaNs, it will only +// be partially filled. If method is not specified, this is the +// maximum number of entries along the entire axis where NaNs will be +// filled. Must be greater than 0 if not None. +// downcast : dict, default is None +// A dict of item->dtype of what to downcast if possible, +// or the string 'infer' which will try to downcast to an appropriate +// equal type (e.g. float64 to int64 if possible). +// +// Returns +// ------- +// []T or None +func Fill[T StatType | ~string](v []T, d T, args ...any) (rows []T) { + // 默认不替换 + var __optInplace = false + if len(args) > 0 { + // 第一个参数为是否copy + if _cp, ok := args[0].(bool); ok { + __optInplace = _cp + } + } + var dest []T + if __optInplace { + dest = v + } else { + dest = slices.Clone(v) + } + var values any = dest + switch rows := values.(type) { + case []string: + ds := AnyToString(d) + for idx, iv := range rows { + if StringIsNaN(iv) { + rows[idx] = ds + } + } + case []float32: + df32 := AnyToFloat32(d) + for idx, iv := range rows { + if Float32IsNaN(iv) { + rows[idx] = df32 + } + } + case []float64: + df64 := AnyToFloat64(d) + for idx, iv := range rows { + if Float64IsNaN(iv) { + rows[idx] = df64 + } + } + default: + return dest + } + return dest +} + +// FillNa NaN填充默认值 +func FillNa[T StatType | ~string](v []T, args ...any) []T { + // 默认不copy + var __optInplace = false + if len(args) > 0 { + // 第一个参数为是否copy + if _cp, ok := args[0].(bool); ok { + __optInplace = _cp + } + } + var dest []T + if __optInplace { + dest = v + } else { + dest = slices.Clone(v) + } + var values any = dest + switch rows := values.(type) { + case []string: + for idx, iv := range rows { + if StringIsNaN(iv) { + rows[idx] = AnyToString(v) + } + } + case []int64: + for idx, iv := range rows { + if Float64IsNaN(float64(iv)) { + rows[idx] = AnyToInt64(v) + } + } + case []float32: + for idx, iv := range rows { + if Float32IsNaN(iv) { + rows[idx] = AnyToFloat32(v) + } + } + case []float64: + for idx, iv := range rows { + if Float64IsNaN(iv) { + rows[idx] = AnyToFloat64(v) + } + } + default: + return dest + } + return dest +} diff --git a/stat/fillna_test.go b/stat/fillna_test.go new file mode 100644 index 0000000000000000000000000000000000000000..10b497d1965279f3a6b431754d286df400cdaa8c --- /dev/null +++ b/stat/fillna_test.go @@ -0,0 +1,15 @@ +package stat + +import ( + "fmt" + "testing" +) + +func TestFill(t *testing.T) { + s2 := []float64{1, 2, 3, 4, 3, 3, 2, 1, DTypeNaN, DTypeNaN, DTypeNaN, DTypeNaN} + fmt.Println(s2) + Fill(s2, 1.0) + fmt.Println(s2) + Fill(s2, 1.0, true) + fmt.Println(s2) +} diff --git a/stat/max.go b/stat/max.go index d0d5b566a5db6f2d6efd7addbc96b8cfbdcd5f28..62fb5d8ec468fdfd27c6eb8d4c8b0e846928ea88 100644 --- a/stat/max.go +++ b/stat/max.go @@ -3,7 +3,6 @@ package stat import ( "github.com/viterin/vek" "github.com/viterin/vek/vek32" - "unsafe" ) // Max 计算最大值 @@ -11,17 +10,18 @@ func Max[T Float](f []T) T { if len(f) == 0 { return T(0) } + var d any var s any s = f - bitSize := unsafe.Sizeof(f[0]) - if bitSize == 4 { - d = vek32.Max(s.([]float32)) - } else if bitSize == 8 { - d = vek.Max(s.([]float64)) - } else { - // 应该不会走到这里 - d = T(0) + switch fs := s.(type) { + case []float32: + d = vek32.Max(fs) + case []float64: + d = vek.Max(fs) + default: + panic(ErrUnsupportedType) } + return d.(T) } diff --git a/stat/maximum.go b/stat/maximum.go index 83e06172c97f94b4dfa4ec49d6a9753b0bd18c26..82315c4ca9eab760df65318b36da039098d8edae 100644 --- a/stat/maximum.go +++ b/stat/maximum.go @@ -3,12 +3,12 @@ package stat import ( "github.com/viterin/vek" "github.com/viterin/vek/vek32" - "unsafe" + "golang.org/x/exp/slices" ) -// MaximumAvx2 两个序列横向比较最大值 +// Maximum AVX2版本, 两个序列横向比较最大值 // TODO:print(np.maximum(1.4, np.nan)) 输出nan -func MaximumAvx2[T Float](f1, f2 []T) []T { +func Maximum[T Float](f1, f2 []T) []T { xlen := len(f1) ylen := len(f2) // 第找出最大长度 @@ -33,26 +33,26 @@ func MaximumAvx2[T Float](f1, f2 []T) []T { s2 = f2 var d any - - bitSize := unsafe.Sizeof(f1[0]) - if bitSize == 4 { - d = vek32.Maximum(s1.([]float32), s2.([]float32)) - } else if bitSize == 8 { - d = vek.Maximum(s1.([]float64), s2.([]float64)) - } else { - // 应该不会走到这里 - panic("other types are not supported") + switch fs1 := s1.(type) { + case []float32: + d = vek32.Maximum(fs1, s2.([]float32)) + case []float64: + d = vek.Maximum(fs1, s2.([]float64)) + default: + // 目前暂时走不到这里 + f1 = slices.Clone(f1) + __maximum(f1, f2) + d = f1 } return d.([]T) } -// Maximum 两个序列横向比较最大值 -// TODO:print(np.maximum(1.4, np.nan)) 输出nan -func Maximum[T Float](f1, f2 []T) []T { +// Maximum_GO go版本, 两个序列横向比较最大值 +func Maximum_GO[T Float](f1, f2 []T) []T { xlen := len(f1) ylen := len(f2) - // 第找出最大长度 + // 第找出最大长度 maxLength := xlen if maxLength < ylen { maxLength = ylen @@ -69,14 +69,18 @@ func Maximum[T Float](f1, f2 []T) []T { } // 初始化返回值 d := make([]T, maxLength) - bitSize := unsafe.Sizeof(f1[0]) for i := 0; i < maxLength; i++ { if Float64IsNaN(float64(f1[i])) || Float64IsNaN(float64(f2[i])) { - if bitSize == 4 { + var s1 any = f1[i] + switch s1.(type) { + case float32: d[i] = T(Nil2Float32) - } else { + case []float64: d[i] = T(Nil2Float64) + default: + panic(ErrUnsupportedType) } + continue } if f1[i] > f2[i] { d[i] = f1[i] @@ -86,3 +90,11 @@ func Maximum[T Float](f1, f2 []T) []T { } return d } + +func __maximum[T Float](x, y []T) { + for i := 0; i < len(x); i++ { + if y[i] > x[i] { + x[i] = y[i] + } + } +} diff --git a/stat/maximum_test.go b/stat/maximum_test.go index 55bfdaf04e9cce39aefeaa3093ff86bc99347232..1c94c1e88d147692dc312fe2df3e45bbde39f167 100644 --- a/stat/maximum_test.go +++ b/stat/maximum_test.go @@ -11,6 +11,6 @@ func TestMaxinum(t *testing.T) { fmt.Println(1.4 < math.NaN()) f1 := []float32{1.1, 2.2, 1.3, 1.4} f2 := []float32{1.2, 1.2, 3.3} - fmt.Println(MaximumAvx2(f1, f2)) fmt.Println(Maximum(f1, f2)) + fmt.Println(Maximum_GO(f1, f2)) } diff --git a/stat/median.go b/stat/median.go index 6be0964b85e2b17df0c9ae98b5d8ace58546128d..91c956f22a5dc95549c2767ea0095140458e8e9b 100644 --- a/stat/median.go +++ b/stat/median.go @@ -2,7 +2,7 @@ package stat // Median returns median value of series. // Linear interpolation is used for odd length. -// TODO:未加验证 +// TODO:未加验证, 未加速 func Median[T StatType](values []T) DType { if len(values) == 0 { return DTypeNaN diff --git a/stat/min.go b/stat/min.go index 264fd2a39f56ce7a052ac1a1ab56c6685e672ff4..116b264a552f395947aa39320e1f3450305d3d7d 100644 --- a/stat/min.go +++ b/stat/min.go @@ -3,7 +3,6 @@ package stat import ( "github.com/viterin/vek" "github.com/viterin/vek/vek32" - "unsafe" ) // Min 计算最小值 @@ -14,14 +13,14 @@ func Min[T Float](f []T) T { var d any var s any s = f - bitSize := unsafe.Sizeof(f[0]) - if bitSize == 4 { - d = vek32.Min(s.([]float32)) - } else if bitSize == 8 { - d = vek.Min(s.([]float64)) - } else { + switch fs := s.(type) { + case []float32: + d = vek32.Min(fs) + case []float64: + d = vek.Min(fs) + default: // 应该不会走到这里 - d = T(0) + panic(ErrUnsupportedType) } return d.(T) } diff --git a/stat/minimum.go b/stat/minimum.go index 945cc175a4bc4d4f3bc9676c6e4439348d2c83a8..238d43389046d19df6a4b371cd1bdde5e7ebbd98 100644 --- a/stat/minimum.go +++ b/stat/minimum.go @@ -3,15 +3,15 @@ package stat import ( "github.com/viterin/vek" "github.com/viterin/vek/vek32" - "unsafe" + "golang.org/x/exp/slices" ) -// MinimumAvx2 两个序列横向比较最大值 -func MinimumAvx2[T Float](f1, f2 []T) []T { +// Minimum AVX2版本, 两个序列横向比较最大值 +func Minimum[T Float](f1, f2 []T) []T { xlen := len(f1) ylen := len(f2) - // 第找出最大长度 + // 第找出最大长度 maxLength := xlen if maxLength < ylen { maxLength = ylen @@ -32,21 +32,22 @@ func MinimumAvx2[T Float](f1, f2 []T) []T { s2 = f2 var d any - - bitSize := unsafe.Sizeof(f1[0]) - if bitSize == 4 { - d = vek32.Minimum(s1.([]float32), s2.([]float32)) - } else if bitSize == 8 { - d = vek.Minimum(s1.([]float64), s2.([]float64)) - } else { - // 应该不会走到这里 - panic("other types are not supported") + switch fs1 := s1.(type) { + case []float32: + d = vek32.Minimum(fs1, s2.([]float32)) + case []float64: + d = vek.Minimum(fs1, s2.([]float64)) + default: + // 目前暂时走不到这里 + f1 = slices.Clone(f1) + __minimum(f1, f2) + d = f1 } return d.([]T) } -// Minimum 两个序列横向比较最大值 -func Minimum[T Float](f1, f2 []T) []T { +// Minimum_GO go版本 两个序列横向比较最大值 +func Minimum_GO[T Float](f1, f2 []T) []T { xlen := len(f1) ylen := len(f2) // 第找出最大长度 @@ -67,15 +68,18 @@ func Minimum[T Float](f1, f2 []T) []T { } // 初始化返回值 d := make([]T, maxLength) - bitSize := unsafe.Sizeof(f1[0]) for i := 0; i < maxLength; i++ { if Float64IsNaN(float64(f1[i])) || Float64IsNaN(float64(f2[i])) { - if bitSize == 4 { + var s1 any = f1[i] + switch s1.(type) { + case float32: d[i] = T(Nil2Float32) - } else { + case []float64: d[i] = T(Nil2Float64) + default: + panic(ErrUnsupportedType) } - + continue } if f1[i] < f2[i] { d[i] = f1[i] @@ -85,3 +89,12 @@ func Minimum[T Float](f1, f2 []T) []T { } return d } + +// 暂时用不到, 先放在这里, 以后可能要扩展类型 +func __minimum[T Float](x, y []T) { + for i := 0; i < len(x); i++ { + if y[i] < x[i] { + x[i] = y[i] + } + } +} diff --git a/stat/minimum_test.go b/stat/minimum_test.go index 9f01efe2ff490a1b4b421a88e0ef9b3f8a4d159b..0355f492606a4516f25a7d0859ca76813cc9b449 100644 --- a/stat/minimum_test.go +++ b/stat/minimum_test.go @@ -8,6 +8,6 @@ import ( func TestMinimum(t *testing.T) { f1 := []float32{1.1, 2.2, 1.3, 1.4} f2 := []float32{1.2, 1.2, 3.3} - fmt.Println(MinimumAvx2(f1, f2)) fmt.Println(Minimum(f1, f2)) + fmt.Println(Minimum_GO(f1, f2)) } diff --git a/stat/repeat.go b/stat/repeat.go index 35b5ec2aa7922cebbbbf50a63c55a47c6ed9ba57..53c3922ea7383ac580d5fd463403fed6bc8bf7bc 100644 --- a/stat/repeat.go +++ b/stat/repeat.go @@ -3,18 +3,18 @@ package stat import ( "github.com/viterin/vek" "github.com/viterin/vek/vek32" - "unsafe" ) // Repeat repeat func Repeat[T StatType](f T, n int) []T { var d any - bitSize := unsafe.Sizeof(f) - if bitSize == 4 { - d = vek32.Repeat(float32(f), n) - } else if bitSize == 8 { - d = vek.Repeat(float64(f), n) - } else { + var s any = f + switch fs := s.(type) { + case float32: + d = vek32.Repeat(fs, n) + case float64: + d = vek.Repeat(fs, n) + default: // 应该不会走到这里 d = []T{} // 剩下的就是int32和int64, 循环吧 diff --git a/stat/stddev.go b/stat/stddev.go index 96cd569d426a56b1b8bbdde2d94ef08a4c184417..bd9d0336cc59f2e8e691183baac5616dbad3f83f 100644 --- a/stat/stddev.go +++ b/stat/stddev.go @@ -6,7 +6,6 @@ import ( "golang.org/x/exp/slices" "gonum.org/v1/gonum/stat" "math" - "unsafe" ) // Std_TODO StdDev 这个版本有bug, gonum计算的std不对 @@ -18,14 +17,15 @@ func Std_TODO[T Float](f []T) T { var d any var s any s = f - bitSize := unsafe.Sizeof(f[0]) - if bitSize == 4 { - d = vek32.Max(s.([]float32)) - } else if bitSize == 8 { - d = stat.StdDev(s.([]float64), nil) - } else { + switch fs := s.(type) { + case []float32: + d = f32_std(fs) + case []float64: + // 这里计算不对 + d = stat.StdDev(fs, nil) + default: // 应该不会走到这里 - d = T(0) + panic(ErrUnsupportedType) } return d.(T) @@ -39,14 +39,14 @@ func Std[T Float](f []T) T { var d any var s any s = f - bitSize := unsafe.Sizeof(f[0]) - if bitSize == 4 { - d = f32_std(s.([]float32)) - } else if bitSize == 8 { - d = f64_std(s.([]float64)) - } else { + switch fs := s.(type) { + case []float32: + d = f32_std(fs) + case []float64: + d = f64_std(fs) + default: // 应该不会走到这里 - d = T(0) + panic(ErrUnsupportedType) } return d.(T) diff --git a/stat/sum.go b/stat/sum.go index 7ab37ff2d413ee27890277c154f7d88c76da8b82..851ddfb26e5ee5a8e20778fbaf26b2ade2f12148 100644 --- a/stat/sum.go +++ b/stat/sum.go @@ -3,10 +3,9 @@ package stat import ( "github.com/viterin/vek" "github.com/viterin/vek/vek32" - "unsafe" ) -// Sum 计算累和 +// Sum 求和 func Sum[T StatType](f []T) T { if len(f) == 0 { return T(0) @@ -14,12 +13,12 @@ func Sum[T StatType](f []T) T { var d any var s any s = f - bitSize := unsafe.Sizeof(f[0]) - if bitSize == 4 { - d = vek32.Sum(s.([]float32)) - } else if bitSize == 8 { - d = vek.Sum(s.([]float64)) - } else { + switch fs := s.(type) { + case []float32: + d = vek32.Sum(fs) + case []float64: + d = vek.Sum(fs) + default: // 剩下的就是int32和int64, 循环吧 m := T(0) for _, v := range f { diff --git a/stat/sum_test.go b/stat/sum_test.go new file mode 100644 index 0000000000000000000000000000000000000000..55e9015cff9103ca577141fbeae2b021bd79b2a2 --- /dev/null +++ b/stat/sum_test.go @@ -0,0 +1,17 @@ +package stat + +import ( + "fmt" + "testing" +) + +func TestSum(t *testing.T) { + d1 := []float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + d2 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + d3 := []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + d4 := []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + fmt.Println(Sum(d1)) + fmt.Println(Sum(d2)) + fmt.Println(Sum(d3)) + fmt.Println(Sum(d4)) +} diff --git a/stat/type.go b/stat/type.go index da0d0e393c2208231125f20f422673284691cf04..18870a85d9be5eefbc21176491e0035dbe4efc74 100644 --- a/stat/type.go +++ b/stat/type.go @@ -6,6 +6,11 @@ import ( "reflect" ) +// GenericType Series支持的所有类型 +type GenericType interface { + ~bool | ~int32 | ~int64 | ~int | ~float32 | ~float64 | ~string +} + type Float interface { ~float32 | ~float64 } diff --git a/stat/type_int64.go b/stat/type_int64.go index 741fd8dd52457ff7038b6f2cb98f0caed8e732c0..f23cfed0c6f8c475e0218f885dc28b998a960bfa 100644 --- a/stat/type_int64.go +++ b/stat/type_int64.go @@ -1,6 +1,11 @@ package stat -import "math" +import ( + "fmt" + "github.com/mymmsc/gox/logger" + "math" + "strconv" +) const ( MaxInt64 = int64(math.MaxInt64) @@ -13,3 +18,54 @@ const ( StringTrue2Int64 = int64(1) // 字符串true转int64 StringFalse2Int64 = int64(0) // 字符串false转int64 ) + +// ParseInt64 解析int字符串, 尝试解析10进制和16进制 +func ParseInt64(s string, v any) int64 { + defer func() { + // 解析失败以后输出日志, 以备检查 + if err := recover(); err != nil { + logger.Errorf("ParseInt64 %+v, error=%+v\n", v, err) + } + }() + if IsEmpty(s) { + return Nil2Int64 + } + if isTrue(s) { + return StringTrue2Int64 + } else if isFalse(s) { + return StringFalse2Int64 + } + i, err := strconv.ParseInt(s, 10, 64) + if err == nil { + return i + } + // 解析失败继续解析16进制 + i, err = strconv.ParseInt(s, 16, 64) + if err == nil { + return i + } + logger.Errorf("%s, error=%+v\n", s, err) + if IgnoreParseExceptions { + i = StringBad2Int64 + } else { + _ = v.(int64) // Intentionally panic + } + return i +} + +func int64ToString(v int64) string { + if Float64IsNaN(float64(v)) { + return StringNaN + } + return fmt.Sprint(v) +} + +// AnyToInt64 any转换int64 +func AnyToInt64(v any) int64 { + if vv, ok := extraceValueFromPointer(v); ok { + v = vv + } + + f := valueToNumber[int64](v, Nil2Int64, boolToInt64, ParseInt64) + return f +} diff --git a/stat/type_string.go b/stat/type_string.go new file mode 100644 index 0000000000000000000000000000000000000000..c4d63b8eb634637f72a7de8fd271b430a67410a5 --- /dev/null +++ b/stat/type_string.go @@ -0,0 +1,97 @@ +package stat + +import ( + "github.com/mymmsc/gox/logger" + "strconv" + "strings" +) + +const ( + StringNaN = "NaN" // 字符串NaN + Nil2String = "NaN" // nil指针转string + True2String = "true" // true转string + False2String = "false" // false转string +) + +var ( + // PossibleNaOfString 有可能出现的NaN字符串的全部选项 + PossibleNaOfString = []string{"NA", "NaN", "nan", ""} +) + +// AnyToString any转string +func AnyToString(v any) string { + switch val := v.(type) { + case nil: + return Nil2String + case *bool: + if val == nil { + return Nil2String + } + if *val == true { + return True2String + } else { + return False2String + } + case bool: + if val == true { + return True2String + } else { + return False2String + } + case *string: + if val == nil { + return Nil2String + } + return []string{*val}[0] + case string: + return val + case *float64: + if val == nil { + return Nil2String + } + return strconv.FormatFloat(*val, 'G', -1, 64) + case float64: + return strconv.FormatFloat(val, 'G', -1, 64) + case *float32: + if val == nil { + return Nil2String + } + return strconv.FormatFloat(float64(*val), 'G', -1, 64) + case float32: + return strconv.FormatFloat(float64(val), 'G', -1, 64) + case *int64: + if val == nil { + return Nil2String + } + return strconv.FormatInt(*val, 10) + case int64: + return strconv.FormatInt(val, 10) + case *int: + if val == nil { + return Nil2String + } + return strconv.Itoa(*val) + case int: + return strconv.Itoa(val) + case *int32: + if val == nil { + return Nil2String + } + return strconv.FormatInt(int64(*val), 10) + case int32: + return strconv.FormatInt(int64(val), 10) + default: + logger.Errorf("%s, error=The type is not recognized\n", v) + _ = v.(string) // Intentionally panic + return Nil2String + } +} + +// StringIsNaN 判断字符串是否NaN +func StringIsNaN(s string) bool { + s = strings.TrimSpace(s) + if strings.ToLower(s) == "nan" { + return true + } + return false +}