Skip to content

Commit

Permalink
feat: add float32 and float63 support
Browse files Browse the repository at this point in the history
  • Loading branch information
ccoVeille committed Sep 6, 2024
1 parent f7927b7 commit 38cf8ce
Show file tree
Hide file tree
Showing 2 changed files with 244 additions and 2 deletions.
12 changes: 10 additions & 2 deletions conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"math"
)

// Number is an alias for the int, uint, int8, uint8, int16, uint16, int32, uint32, int64, and uint64 types.
// Number is an alias for the int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, and float64 types.
type Number interface {
~int | ~uint | ~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32 | ~int64 | ~uint64
~int | ~uint | ~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32 | ~int64 | ~uint64 | ~float32 | ~float64
}

var ErrOutOfRange = errors.New("out of range")
Expand Down Expand Up @@ -133,6 +133,10 @@ func ToInt64[T Number](i T) (int64, error) {
return 0, fmt.Errorf("%w: %v is greater than math.MaxInt64", ErrOutOfRange, i)
}

if float64(i) >= float64(math.MaxInt64) {
return 0, fmt.Errorf("%w: %v is greater than math.MaxInt64", ErrOutOfRange, i)
}

return int64(i), nil
}

Expand All @@ -144,6 +148,10 @@ func ToUint64[T Number](i T) (uint64, error) {
return 0, err
}

if float64(i) > math.MaxUint64 {
return 0, fmt.Errorf("%w: %v is greater than math.MaxUint64", ErrOutOfRange, i)
}

return uint64(i), nil
}

Expand Down
234 changes: 234 additions & 0 deletions conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,28 @@ func TestToInt8(t *testing.T) {
{name: "positive out of range", input: math.MaxInt8 + 1},
})
})

t.Run("from float32", func(t *testing.T) {
assertInt8OK(t, []caseInt8[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
})

assertInt8Error(t, []caseInt8[float32]{
{name: "positive out of range", input: math.MaxInt8 + 1},
})
})

t.Run("from float64", func(t *testing.T) {
assertInt8OK(t, []caseInt8[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
})

assertInt8Error(t, []caseInt8[float64]{
{name: "positive out of range", input: math.MaxInt8 + 1},
})
})
}

type caseUint8[in safecast.Number] struct {
Expand Down Expand Up @@ -325,6 +347,30 @@ func TestToUint8(t *testing.T) {
{name: "positive out of range", input: math.MaxUint8 + 1},
})
})

t.Run("from float32", func(t *testing.T) {
assertUint8OK(t, []caseUint8[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 100.9, want: 100},
})

assertUint8Error(t, []caseUint8[float32]{
{name: "positive out of range", input: math.MaxUint8 + 1},
})
})

t.Run("from float64", func(t *testing.T) {
assertUint8OK(t, []caseUint8[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 100.9, want: 100},
})

assertUint8Error(t, []caseUint8[float64]{
{name: "positive out of range", input: math.MaxUint8 + 1},
})
})
}

type caseInt16[in safecast.Number] struct {
Expand Down Expand Up @@ -463,6 +509,30 @@ func TestToInt16(t *testing.T) {
{name: "positive out of range", input: math.MaxInt16 + 1},
})
})

t.Run("from float32", func(t *testing.T) {
assertInt16OK(t, []caseInt16[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000, want: 10000},
})

assertInt16Error(t, []caseInt16[float32]{
{name: "positive out of range", input: math.MaxInt16 + 1},
})
})

t.Run("from float64", func(t *testing.T) {
assertInt16OK(t, []caseInt16[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000, want: 10000},
})

assertInt16Error(t, []caseInt16[float64]{
{name: "positive out of range", input: math.MaxInt16 + 1},
})
})
}

type caseUint16[in safecast.Number] struct {
Expand Down Expand Up @@ -594,6 +664,30 @@ func TestToUint16(t *testing.T) {
{name: "positive out of range", input: math.MaxUint16 + 1},
})
})

t.Run("from float32", func(t *testing.T) {
assertUint16OK(t, []caseUint16[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000, want: 10000},
})

assertUint16Error(t, []caseUint16[float32]{
{name: "positive out of range", input: math.MaxUint16 + 1},
})
})

t.Run("from float64", func(t *testing.T) {
assertUint16OK(t, []caseUint16[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000, want: 10000},
})

assertUint16Error(t, []caseUint16[float32]{
{name: "positive out of range", input: math.MaxUint16 + 1},
})
})
}

type caseInt32[in safecast.Number] struct {
Expand Down Expand Up @@ -723,6 +817,30 @@ func TestToInt32(t *testing.T) {
{name: "positive out of range", input: math.MaxInt32 + 1},
})
})

t.Run("from float32", func(t *testing.T) {
assertInt32OK(t, []caseInt32[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000, want: 10000},
})

assertInt32Error(t, []caseInt32[float32]{
{name: "positive out of range", input: math.MaxInt32 + 1},
})
})

t.Run("from float64", func(t *testing.T) {
assertInt32OK(t, []caseInt32[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000, want: 10000},
})

assertInt32Error(t, []caseInt32[float64]{
{name: "positive out of range", input: math.MaxInt32 + 1},
})
})
}

type caseUint32[in safecast.Number] struct {
Expand Down Expand Up @@ -857,6 +975,31 @@ func TestToUint32(t *testing.T) {
{name: "positive out of range", input: math.MaxUint32 + 1},
})
})

t.Run("from float32", func(t *testing.T) {
assertUint32OK(t, []caseUint32[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
})

assertUint32Error(t, []caseUint32[float32]{
{name: "positive out of range", input: math.MaxUint32 + 1},
})
})

t.Run("from float64", func(t *testing.T) {
assertUint32OK(t, []caseUint32[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
})

assertUint32Error(t, []caseUint32[float64]{
{name: "positive out of range", input: math.MaxUint32 + 1},
{name: "negative value", input: -1},
})
})
}

type caseInt64[in safecast.Number] struct {
Expand Down Expand Up @@ -972,6 +1115,32 @@ func TestToInt64(t *testing.T) {
{name: "positive out of range", input: math.MaxInt64 + 1},
})
})

t.Run("from float32", func(t *testing.T) {
assertInt64OK(t, []caseInt64[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
{name: "big value", input: math.MaxInt16, want: math.MaxInt16},
})

assertInt64Error(t, []caseInt64[float32]{
{name: "positive out of range", input: math.MaxFloat32 + 1},
})
})

t.Run("from float64", func(t *testing.T) {
assertInt64OK(t, []caseInt64[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
{name: "big value", input: math.MaxInt32, want: math.MaxInt32},
})

assertInt64Error(t, []caseInt64[float64]{
{name: "positive out of range", input: math.MaxInt64 + 1},
})
})
}

type caseUint64[in safecast.Number] struct {
Expand Down Expand Up @@ -1094,6 +1263,31 @@ func TestToUint64(t *testing.T) {
{name: "positive within range", input: 100, want: 100},
})
})

t.Run("from float32", func(t *testing.T) {
assertUint64OK(t, []caseUint64[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
})

assertUint64Error(t, []caseUint64[float32]{
{name: "out of range max float32", input: math.MaxFloat32},
})
})

t.Run("from float64", func(t *testing.T) {
assertUint64OK(t, []caseUint64[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
})

assertUint64Error(t, []caseUint64[float64]{
{name: "out of range max float32", input: math.MaxFloat32},
{name: "out of range max float64", input: math.MaxFloat64},
})
})
}

type caseInt[in safecast.Number] struct {
Expand Down Expand Up @@ -1209,6 +1403,30 @@ func TestToInt(t *testing.T) {
{name: "positive out of range", input: math.MaxInt64 + 1},
})
})

t.Run("from float32", func(t *testing.T) {
assertIntOK(t, []caseInt[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
})

assertIntError(t, []caseInt[float32]{
{name: "positive out of range", input: math.MaxFloat32 + 1},
})
})

t.Run("from float64", func(t *testing.T) {
assertIntOK(t, []caseInt[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
})

assertIntError(t, []caseInt[float32]{
{name: "positive out of range", input: math.MaxFloat32 + 1},
})
})
}

type caseUint[in safecast.Number] struct {
Expand Down Expand Up @@ -1300,6 +1518,22 @@ func TestToUint(t *testing.T) {
})
})

t.Run("from float32", func(t *testing.T) {
assertUintOK(t, []caseUint[float32]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
})
})

t.Run("from float64", func(t *testing.T) {
assertUintOK(t, []caseUint[float64]{
{name: "zero", input: 0.0, want: 0},
{name: "rounded value", input: 1.1, want: 1},
{name: "positive within range", input: 10000.9, want: 10000},
})
})

type UintAlias uint
t.Run("from type alias", func(t *testing.T) {
assertUintOK(t, []caseUint[UintAlias]{
Expand Down

0 comments on commit 38cf8ce

Please sign in to comment.