Skip to content

Commit

Permalink
Alter handling of float32 and float64 values within reflect.Number (#914
Browse files Browse the repository at this point in the history
)

  * The reflect.Number function was handling float32 and float64 values
    by determining whether the value in the *big.Float generated through
    calling val.As() was an exact representation by examining the accuracy
    produced when calling Float32() or Float64() on the *big.Float
  * The reflect.Number function has been altered to verify whether the
    string representation, generated by calling Text(), of the generated
    *big.Float and a *big.Float created from the float32 or float64 value
    are identical
  • Loading branch information
bendbennett committed Feb 6, 2024
1 parent f2e8b33 commit 8280c14
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 157 deletions.
72 changes: 18 additions & 54 deletions internal/reflect/number.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import (
"reflect"
"strconv"

"github.com/hashicorp/terraform-plugin-go/tftypes"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/attr/xattr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// Number creates a *big.Float and populates it with the data in `val`. It then
Expand Down Expand Up @@ -168,65 +169,28 @@ func Number(ctx context.Context, typ attr.Type, val tftypes.Value, target reflec
return reflect.ValueOf(uintResult), diags
}
case reflect.Float32:
floatResult, acc := result.Float32()
if acc != big.Exact && !opts.AllowRoundingNumbers {
return target, append(diags, roundingErrorDiag)
} else if acc == big.Above {
floatResult = math.MaxFloat32
} else if acc == big.Below {
floatResult = math.SmallestNonzeroFloat32
} else if acc != big.Exact {
err := fmt.Errorf("unsure how to round %s and %f", acc, floatResult)
diags.AddAttributeError(
path,
"Value Conversion Error",
"An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
floatResult, _ := result.Float32()

bf := big.NewFloat(float64(floatResult))

if result.Text('f', -1) != bf.Text('f', -1) {
diags.Append(roundingErrorDiag)

return target, diags
}

return reflect.ValueOf(floatResult), diags
case reflect.Float64:
floatResult, acc := result.Float64()
if acc != big.Exact && !opts.AllowRoundingNumbers {
return target, append(diags, roundingErrorDiag)
}
if acc == big.Above {
if floatResult == math.Inf(1) || floatResult == math.MaxFloat64 {
floatResult = math.MaxFloat64
} else if floatResult == 0.0 || floatResult == math.SmallestNonzeroFloat64 {
floatResult = -math.SmallestNonzeroFloat64
} else {
err := fmt.Errorf("not sure how to round %s and %f", acc, floatResult)
diags.AddAttributeError(
path,
"Value Conversion Error",
"An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
return target, diags
}
} else if acc == big.Below {
if floatResult == math.Inf(-1) || floatResult == -math.MaxFloat64 {
floatResult = -math.MaxFloat64
} else if floatResult == -0.0 || floatResult == -math.SmallestNonzeroFloat64 { //nolint:staticcheck
floatResult = math.SmallestNonzeroFloat64
} else {
err := fmt.Errorf("not sure how to round %s and %f", acc, floatResult)
diags.AddAttributeError(
path,
"Value Conversion Error",
"An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
return target, diags
}
} else if acc != big.Exact {
err := fmt.Errorf("not sure how to round %s and %f", acc, floatResult)
diags.AddAttributeError(
path,
"Value Conversion Error",
"An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
floatResult, _ := result.Float64()

bf := big.NewFloat(floatResult)

if result.Text('f', -1) != bf.Text('f', -1) {
diags.Append(roundingErrorDiag)

return target, diags
}

return reflect.ValueOf(floatResult), diags
}
err = fmt.Errorf("cannot convert number to %s", target.Type())
Expand Down
105 changes: 2 additions & 103 deletions internal/reflect/number_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-go/tftypes"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
refl "github.com/hashicorp/terraform-plugin-framework/internal/reflect"
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

var (
Expand Down Expand Up @@ -945,23 +946,6 @@ func TestNumber_float32(t *testing.T) {
t.Parallel()
}

func TestNumber_float32Overflow(t *testing.T) {
t.Parallel()

var n float32

result, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, math.MaxFloat64), reflect.ValueOf(n), refl.Options{
AllowRoundingNumbers: true,
}, path.Empty())
if diags.HasError() {
t.Errorf("Unexpected error: %v", diags)
}
reflect.ValueOf(&n).Elem().Set(result)
if n != math.MaxFloat32 {
t.Errorf("Expected %v, got %v", math.MaxFloat32, n)
}
}

func TestNumber_float32OverflowError(t *testing.T) {
t.Parallel()

Expand All @@ -981,23 +965,6 @@ func TestNumber_float32OverflowError(t *testing.T) {
}
}

func TestNumber_float32Underflow(t *testing.T) {
t.Parallel()

var n float32

result, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, math.SmallestNonzeroFloat64), reflect.ValueOf(n), refl.Options{
AllowRoundingNumbers: true,
}, path.Empty())
if diags.HasError() {
t.Errorf("Unexpected error: %v", diags)
}
reflect.ValueOf(&n).Elem().Set(result)
if n != math.SmallestNonzeroFloat32 {
t.Errorf("Expected %v, got %v", math.SmallestNonzeroFloat32, n)
}
}

func TestNumber_float32UnderflowError(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -1032,23 +999,6 @@ func TestNumber_float64(t *testing.T) {
}
}

func TestNumber_float64Overflow(t *testing.T) {
t.Parallel()

var n float64

result, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, overflowFloat), reflect.ValueOf(n), refl.Options{
AllowRoundingNumbers: true,
}, path.Empty())
if diags.HasError() {
t.Errorf("Unexpected error: %v", diags)
}
reflect.ValueOf(&n).Elem().Set(result)
if n != math.MaxFloat64 {
t.Errorf("Expected %v, got %v", math.MaxFloat64, n)
}
}

func TestNumber_float64OverflowError(t *testing.T) {
t.Parallel()

Expand All @@ -1068,23 +1018,6 @@ func TestNumber_float64OverflowError(t *testing.T) {
}
}

func TestNumber_float64OverflowNegative(t *testing.T) {
t.Parallel()

var n float64

result, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, overflowNegativeFloat), reflect.ValueOf(n), refl.Options{
AllowRoundingNumbers: true,
}, path.Empty())
if diags.HasError() {
t.Errorf("Unexpected error: %v", diags)
}
reflect.ValueOf(&n).Elem().Set(result)
if n != -math.MaxFloat64 {
t.Errorf("Expected %v, got %v", -math.MaxFloat64, n)
}
}

func TestNumber_float64OverflowNegativeError(t *testing.T) {
t.Parallel()

Expand All @@ -1104,23 +1037,6 @@ func TestNumber_float64OverflowNegativeError(t *testing.T) {
}
}

func TestNumber_float64Underflow(t *testing.T) {
t.Parallel()

var n float64

result, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, underflowFloat), reflect.ValueOf(n), refl.Options{
AllowRoundingNumbers: true,
}, path.Empty())
if diags.HasError() {
t.Errorf("Unexpected error: %v", diags)
}
reflect.ValueOf(&n).Elem().Set(result)
if n != math.SmallestNonzeroFloat64 {
t.Errorf("Expected %v, got %v", math.SmallestNonzeroFloat64, n)
}
}

func TestNumber_float64UnderflowError(t *testing.T) {
t.Parallel()

Expand All @@ -1140,23 +1056,6 @@ func TestNumber_float64UnderflowError(t *testing.T) {
}
}

func TestNumber_float64UnderflowNegative(t *testing.T) {
t.Parallel()

var n float64

result, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, underflowNegativeFloat), reflect.ValueOf(n), refl.Options{
AllowRoundingNumbers: true,
}, path.Empty())
if diags.HasError() {
t.Errorf("Unexpected error: %v", diags)
}
reflect.ValueOf(&n).Elem().Set(result)
if n != -math.SmallestNonzeroFloat64 {
t.Errorf("Expected %v, got %v", -math.SmallestNonzeroFloat64, n)
}
}

func TestNumber_float64UnderflowNegativeError(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 8280c14

Please sign in to comment.