Skip to content

Commit

Permalink
add float32/float64
Browse files Browse the repository at this point in the history
  • Loading branch information
shockerli committed Mar 9, 2021
1 parent b9988d8 commit 62feb40
Show file tree
Hide file tree
Showing 4 changed files with 459 additions and 9 deletions.
26 changes: 26 additions & 0 deletions cvt.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,29 @@ func Int(v interface{}, def ...int) int {

return 0
}

// Float64 convert an interface to a float64 type, with default value
func Float64(v interface{}, def ...float64) float64 {
if v, err := Float64E(v); err == nil {
return v
}

if len(def) > 0 {
return def[0]
}

return 0
}

// Float32 convert an interface to a float32 type, with default value
func Float32(v interface{}, def ...float32) float32 {
if v, err := Float32E(v); err == nil {
return v
}

if len(def) > 0 {
return def[0]
}

return 0
}
190 changes: 190 additions & 0 deletions cvt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cvt_test

import (
"fmt"
"math"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -1051,3 +1052,192 @@ func TestInt_BaseLine(t *testing.T) {
assert.Equal(t, tt.expect, v, msg)
}
}

func TestFloat64_HasDefault(t *testing.T) {
tests := []struct {
input interface{}
def float64
expect float64
}{
// supported value, def is not used, def != expect
{int(8), 1, 8},
{int8(8), 1, 8},
{int16(8), 1, 8},
{int32(8), 1, 8},
{int64(8), 1, 8},
{uint(8), 1, 8},
{uint8(8), 1, 8},
{uint16(8), 1, 8},
{uint32(8), 1, 8},
{uint64(8), 1, 8},
{float32(8.31), 1, 8.31},
{float64(8.31), 1, 8.31},
{"8", 2, 8},
{"8.00", 2, 8},
{"8.01", 2, 8.01},
{int(-8), 1, -8},
{int8(-8), 1, -8},
{int16(-8), 1, -8},
{int32(-8), 1, -8},
{int64(-8), 1, -8},
{float32(-8.31), 1, -8.31},
{float64(-8.31), 1, -8.31},
{int64(math.MaxInt64), 1, float64(math.MaxInt64)},
{uint64(math.MaxUint64), 1, float64(math.MaxUint64)},
{"-8", 1, -8},
{"-8.01", 1, -8.01},
{true, 2, 1},
{false, 2, 0},
{nil, 2, 0},
{aliasTypeInt_0, 2, 0},
{&aliasTypeInt_0, 2, 0},
{aliasTypeInt_1, 2, 1},
{&aliasTypeInt_1, 2, 1},
{aliasTypeString_0, 2, 0},
{&aliasTypeString_0, 2, 0},
{aliasTypeString_1, 2, 1},
{&aliasTypeString_1, 2, 1},
{aliasTypeString_8d15, 2, 8.15},
{&aliasTypeString_8d15, 2, 8.15},
{aliasTypeString_8d15_minus, 1, -8.15},
{&aliasTypeString_8d15_minus, 1, -8.15},

// unsupported value, def == expect
{"10a", 1.11, 1.11},
{"a10a", 1.11, 1.11},
{"8.01a", 1.11, 1.11},
{"8.01 ", 1.11, 1.11},
{"hello", 1.11, 1.11},
{testing.T{}, 1.11, 1.11},
{&testing.T{}, 1.11, 1.11},
{[]int{}, 1.11, 1.11},
{[]string{}, 1.11, 1.11},
{[...]string{}, 1.11, 1.11},
{map[int]string{}, 1.11, 1.11},
}

for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect)

v := cvt.Float64(tt.input, tt.def)
assert.Equal(t, tt.expect, v, msg)
}
}

func TestFloat64_BaseLine(t *testing.T) {
tests := []struct {
input interface{}
expect float64
}{
{"8.01a", 0},
{testing.T{}, 0},
{&testing.T{}, 0},
{[]int{}, 0},
{[]int{1, 2, 3}, 0},
{[]string{}, 0},
{[]string{"a", "b", "c"}, 0},
{[...]string{}, 0},
{map[int]string{}, 0},
}

for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect)

v := cvt.Float64(tt.input)
assert.Equal(t, tt.expect, v, msg)
}
}

func TestFloat32_HasDefault(t *testing.T) {
tests := []struct {
input interface{}
def float32
expect float32
}{
// supported value, def is not used, def != expect
{int(8), 1, 8},
{int8(8), 1, 8},
{int16(8), 1, 8},
{int32(8), 1, 8},
{int64(8), 1, 8},
{uint(8), 1, 8},
{uint8(8), 1, 8},
{uint16(8), 1, 8},
{uint32(8), 1, 8},
{uint64(8), 1, 8},
{float32(8.31), 1, 8.31},
{float64(8.31), 1, 8.31},
{"8", 2, 8},
{"8.00", 2, 8},
{"8.01", 2, 8.01},
{int(-8), 1, -8},
{int8(-8), 1, -8},
{int16(-8), 1, -8},
{int32(-8), 1, -8},
{int64(-8), 1, -8},
{float32(-8.31), 1, -8.31},
{float64(-8.31), 1, -8.31},
{int(math.MaxInt32), 1, float32(math.MaxInt32)},
{"-8", 1, -8},
{"-8.01", 1, -8.01},
{true, 2, 1},
{false, 2, 0},
{nil, 2, 0},
{aliasTypeInt_0, 2, 0},
{&aliasTypeInt_0, 2, 0},
{aliasTypeInt_1, 2, 1},
{&aliasTypeInt_1, 2, 1},
{aliasTypeString_0, 2, 0},
{&aliasTypeString_0, 2, 0},
{aliasTypeString_1, 2, 1},
{&aliasTypeString_1, 2, 1},
{aliasTypeString_8d15, 2, 8.15},
{&aliasTypeString_8d15, 2, 8.15},
{aliasTypeString_8d15_minus, 1, -8.15},
{&aliasTypeString_8d15_minus, 1, -8.15},

// unsupported value, def == expect
{"10a", 1.11, 1.11},
{"a10a", 1.11, 1.11},
{"8.01a", 1.11, 1.11},
{"8.01 ", 1.11, 1.11},
{"hello", 1.11, 1.11},
{testing.T{}, 1.11, 1.11},
{&testing.T{}, 1.11, 1.11},
{[]int{}, 1.11, 1.11},
{[]string{}, 1.11, 1.11},
{[...]string{}, 1.11, 1.11},
{map[int]string{}, 1.11, 1.11},
}

for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect)

v := cvt.Float32(tt.input, tt.def)
assert.Equal(t, tt.expect, v, msg)
}
}

func TestFloat32_BaseLine(t *testing.T) {
tests := []struct {
input interface{}
expect float32
}{
{"8.01a", 0},
{testing.T{}, 0},
{&testing.T{}, 0},
{[]int{}, 0},
{[]int{1, 2, 3}, 0},
{[]string{}, 0},
{[]string{"a", "b", "c"}, 0},
{[...]string{}, 0},
{map[int]string{}, 0},
}

for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect)

v := cvt.Float32(tt.input)
assert.Equal(t, tt.expect, v, msg)
}
}
73 changes: 64 additions & 9 deletions cvte.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (
)

var convErr = errors.New("convert failed")
var formatOutOfLimit = "%w, out of max limit value(%d)"
var formatOutOfLimitInt = "%w, out of max limit value(%d)"
var formatOutOfLimitFloat = "%w, out of max limit value(%f)"
var formatExtend = "%v, %w"

// BoolE convert an interface to a bool type
Expand Down Expand Up @@ -78,7 +79,7 @@ func Uint32E(val interface{}) (uint32, error) {
return 0, e
}
if v > math.MaxUint32 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint32"), uint32(math.MaxUint32))
return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "uint32"), uint32(math.MaxUint32))
}

return uint32(v), nil
Expand All @@ -91,7 +92,7 @@ func Uint16E(val interface{}) (uint16, error) {
return 0, e
}
if v > math.MaxUint16 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint16"), uint16(math.MaxUint16))
return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "uint16"), uint16(math.MaxUint16))
}

return uint16(v), nil
Expand All @@ -104,7 +105,7 @@ func Uint8E(val interface{}) (uint8, error) {
return 0, e
}
if v > math.MaxUint8 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint8"), uint8(math.MaxUint8))
return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "uint8"), uint8(math.MaxUint8))
}

return uint8(v), nil
Expand All @@ -117,7 +118,7 @@ func UintE(val interface{}) (uint, error) {
return 0, e
}
if v > uint64(^uint(0)) {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint"), ^uint(0))
return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "uint"), ^uint(0))
}

return uint(v), nil
Expand Down Expand Up @@ -176,7 +177,7 @@ func Int32E(val interface{}) (int32, error) {
return 0, e
}
if v > math.MaxInt32 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "int32"), int32(math.MaxInt32))
return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int32"), int32(math.MaxInt32))
}

return int32(v), nil
Expand All @@ -189,7 +190,7 @@ func Int16E(val interface{}) (int16, error) {
return 0, e
}
if v > math.MaxInt16 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "int16"), int16(math.MaxInt16))
return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int16"), int16(math.MaxInt16))
}

return int16(v), nil
Expand All @@ -202,7 +203,7 @@ func Int8E(val interface{}) (int8, error) {
return 0, e
}
if v > math.MaxInt8 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "int8"), int8(math.MaxInt8))
return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int8"), int8(math.MaxInt8))
}

return int8(v), nil
Expand All @@ -215,7 +216,7 @@ func IntE(val interface{}) (int, error) {
return 0, e
}
if strconv.IntSize == 32 && v > math.MaxInt32 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "int"), int32(math.MaxInt32))
return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int"), int32(math.MaxInt32))
}

return int(v), nil
Expand Down Expand Up @@ -257,6 +258,59 @@ func convInt64(val interface{}) (int64, error) {
return 0, convErr
}

// Float64E convert an interface to a float64 type
func Float64E(val interface{}) (float64, error) {
v, _, rv := Indirect(val)

switch vv := v.(type) {
case nil:
return 0, nil
case bool:
if vv {
return 1, nil
}
return 0, nil
case string:
vvv, err := strconv.ParseFloat(vv, 64)
if err == nil {
return vvv, nil
}
case []byte:
vvv, err := strconv.ParseFloat(string(vv), 64)
if err == nil {
return vvv, nil
}
case uint, uint8, uint16, uint32, uint64, uintptr:
return float64(rv.Uint()), nil
case int, int8, int16, int32, int64:
return float64(rv.Int()), nil
case float32:
// use fmt to fix float32 -> float64 precision loss
// eg: cvt.Float64E(float32(8.31))
vvv, err := strconv.ParseFloat(fmt.Sprintf("%f", vv), 64)
if err == nil {
return vvv, nil
}
case float64:
return vv, nil
}

return 0, convErr
}

// Float32E convert an interface to a float32 type
func Float32E(val interface{}) (float32, error) {
v, e := Float64E(val)
if e := catch("float32", val, e); e != nil {
return 0, e
}
if v > math.MaxFloat32 {
return 0, fmt.Errorf(formatOutOfLimitFloat, newErr(val, "float32"), float32(math.MaxFloat32))
}

return float32(v), nil
}

// Indirect returns the value with base type
func Indirect(a interface{}) (val interface{}, k reflect.Kind, v reflect.Value) {
if a == nil {
Expand Down Expand Up @@ -318,6 +372,7 @@ func newErr(val interface{}, t string) error {
return fmt.Errorf("unable to convert %#v of type %T to %s", val, val, t)
}

// catching an error and return a new
func catch(t string, val interface{}, e error) error {
if e != nil {
if errors.Is(e, convErr) {
Expand Down
Loading

0 comments on commit 62feb40

Please sign in to comment.