Skip to content

Commit

Permalink
reflect: Use float64 string representation for float32 reflection…
Browse files Browse the repository at this point in the history
… equivalency (#1015)

* Fix `float32` reflection logic and tests

* Add float32 negative overflow and underflow tests
  • Loading branch information
SBGoods authored Jul 2, 2024
1 parent 758afb7 commit 6ab653a
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 24 deletions.
24 changes: 21 additions & 3 deletions internal/reflect/number.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,17 +132,35 @@ func Number(ctx context.Context, typ attr.Type, val tftypes.Value, target reflec
return reflect.ValueOf(uintResult), diags
}
case reflect.Float32:
floatResult, _ := result.Float32()
float64Result, _ := result.Float64()

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

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

return target, diags
}

return reflect.ValueOf(floatResult), diags
float32Result, accuracy := result.Float32()

// Underflow
// Reference: https://pkg.go.dev/math/big#Float.Float32
if float32Result == 0 && accuracy != big.Exact {
diags.Append(roundingErrorDiag)

return target, diags
}

// Overflow
// Reference: https://pkg.go.dev/math/big#Float.Float32
if math.IsInf(float64(float32Result), 0) {
diags.Append(roundingErrorDiag)

return target, diags
}

return reflect.ValueOf(float32Result), diags
case reflect.Float64:
floatResult, _ := result.Float64()

Expand Down
84 changes: 63 additions & 21 deletions internal/reflect/number_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ import (
)

var (
overflowInt, _, _ = big.ParseFloat("9223372036854775808", 10, 53, big.ToPositiveInf)
overflowUint, _, _ = big.ParseFloat("18446744073709551616", 10, 53, big.ToPositiveInf)
overflowFloat, _, _ = big.ParseFloat("1e10000", 10, 53, big.ToPositiveInf)
overflowNegativeFloat, _, _ = big.ParseFloat("-1e10000", 10, 53, big.ToPositiveInf)
underflowInt, _, _ = big.ParseFloat("-9223372036854775809", 10, 53, big.ToNegativeInf)
underflowFloat, _, _ = big.ParseFloat("1e-1000", 10, 0, big.ToNegativeInf)
underflowNegativeFloat, _, _ = big.ParseFloat("-1e-1000", 10, 0, big.ToNegativeInf)
overflowInt, _, _ = big.ParseFloat("9223372036854775808", 10, 53, big.ToPositiveInf)
overflowUint, _, _ = big.ParseFloat("18446744073709551616", 10, 53, big.ToPositiveInf)
overflowFloat32, _, _ = big.ParseFloat("3.40282346638528859811704183484516925440e+39", 10, 24, big.ToPositiveInf)
overflowFloat64, _, _ = big.ParseFloat("1e10000", 10, 53, big.ToPositiveInf)
overflowNegativeFloat32, _, _ = big.ParseFloat("-3.40282346638528859811704183484516925440e+39", 10, 53, big.ToPositiveInf)
overflowNegativeFloat64, _, _ = big.ParseFloat("-1e10000", 10, 53, big.ToPositiveInf)
underflowInt, _, _ = big.ParseFloat("-9223372036854775809", 10, 53, big.ToNegativeInf)
underflowFloat32, _, _ = big.ParseFloat("1.401298464324817070923729583289916131280e-46", 10, 0, big.ToNegativeInf)
underflowFloat64, _, _ = big.ParseFloat("1e-1000", 10, 0, big.ToNegativeInf)
underflowNegativeFloat32, _, _ = big.ParseFloat("-1.401298464324817070923729583289916131280e-46", 10, 0, big.ToNegativeInf)
underflowNegativeFloat64, _, _ = big.ParseFloat("-1e-1000", 10, 0, big.ToNegativeInf)
)

func TestNumber_bigFloat(t *testing.T) {
Expand Down Expand Up @@ -590,13 +594,13 @@ func TestNumber_float32(t *testing.T) {

var n float32

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

Expand All @@ -608,11 +612,30 @@ func TestNumber_float32OverflowError(t *testing.T) {
diag.NewAttributeErrorDiagnostic(
path.Empty(),
"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\ncannot store 1.797693135e+308 in float32",
"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\ncannot store 3.402823669e+39 in float32",
),
}

_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, overflowFloat32), reflect.ValueOf(n), refl.Options{}, path.Empty())

if diff := cmp.Diff(diags, expectedDiags); diff != "" {
t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff)
}
}

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

var n float32
expectedDiags := diag.Diagnostics{
diag.NewAttributeErrorDiagnostic(
path.Empty(),
"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\ncannot store -3.402823466e+39 in float32",
),
}

_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, math.MaxFloat64), reflect.ValueOf(n), refl.Options{}, path.Empty())
_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, overflowNegativeFloat32), reflect.ValueOf(n), refl.Options{}, path.Empty())

if diff := cmp.Diff(diags, expectedDiags); diff != "" {
t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff)
Expand All @@ -627,11 +650,30 @@ func TestNumber_float32UnderflowError(t *testing.T) {
diag.NewAttributeErrorDiagnostic(
path.Empty(),
"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\ncannot store 4.940656458e-324 in float32",
"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\ncannot store 1.401298464e-46 in float32",
),
}

_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, math.SmallestNonzeroFloat64), reflect.ValueOf(n), refl.Options{}, path.Empty())
_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, underflowFloat32), reflect.ValueOf(n), refl.Options{}, path.Empty())

if diff := cmp.Diff(diags, expectedDiags); diff != "" {
t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff)
}
}

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

var n float32
expectedDiags := diag.Diagnostics{
diag.NewAttributeErrorDiagnostic(
path.Empty(),
"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\ncannot store -1.401298464e-46 in float32",
),
}

_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, underflowNegativeFloat32), reflect.ValueOf(n), refl.Options{}, path.Empty())

if diff := cmp.Diff(diags, expectedDiags); diff != "" {
t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff)
Expand All @@ -643,13 +685,13 @@ func TestNumber_float64(t *testing.T) {

var n float64

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

Expand All @@ -665,7 +707,7 @@ func TestNumber_float64OverflowError(t *testing.T) {
),
}

_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, overflowFloat), reflect.ValueOf(n), refl.Options{}, path.Empty())
_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, overflowFloat64), reflect.ValueOf(n), refl.Options{}, path.Empty())

if diff := cmp.Diff(diags, expectedDiags); diff != "" {
t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff)
Expand All @@ -684,7 +726,7 @@ func TestNumber_float64OverflowNegativeError(t *testing.T) {
),
}

_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, overflowNegativeFloat), reflect.ValueOf(n), refl.Options{}, path.Empty())
_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, overflowNegativeFloat64), reflect.ValueOf(n), refl.Options{}, path.Empty())

if diff := cmp.Diff(diags, expectedDiags); diff != "" {
t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff)
Expand All @@ -703,7 +745,7 @@ func TestNumber_float64UnderflowError(t *testing.T) {
),
}

_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, underflowFloat), reflect.ValueOf(n), refl.Options{}, path.Empty())
_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, underflowFloat64), reflect.ValueOf(n), refl.Options{}, path.Empty())

if diff := cmp.Diff(diags, expectedDiags); diff != "" {
t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff)
Expand All @@ -722,7 +764,7 @@ func TestNumber_float64UnderflowNegativeError(t *testing.T) {
),
}

_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, underflowNegativeFloat), reflect.ValueOf(n), refl.Options{}, path.Empty())
_, diags := refl.Number(context.Background(), types.NumberType, tftypes.NewValue(tftypes.Number, underflowNegativeFloat64), reflect.ValueOf(n), refl.Options{}, path.Empty())

if diff := cmp.Diff(diags, expectedDiags); diff != "" {
t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff)
Expand Down

0 comments on commit 6ab653a

Please sign in to comment.