Skip to content

Commit

Permalink
don't marshal integer values as msgpack floats (#396)
Browse files Browse the repository at this point in the history
* don't marshal integer values as msgpack floats

Round-tripping a large integer through a float64, even if the binary
representation is exact, causes us to end up with a rounded string
representation after decoding, because the decoded number has 52-bit
precision instead of the 512 we use when dealing with string
representations of large integers.

* update CHANGELOG.md

* update to changie log

---------

Co-authored-by: Austin Valle <austinvalle@gmail.com>
  • Loading branch information
jbardin and austinvalle committed Apr 18, 2024
1 parent 6d19ca6 commit 865883d
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/BUG FIXES-20240418-121811.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: BUG FIXES
body: 'tftypes: Large integers are always encoded as msgpack integers rather than
float values to ensure the decoded value will not be rounded to 52-bit precision'
time: 2024-04-18T12:18:11.880247-04:00
custom:
Issue: "396"
2 changes: 1 addition & 1 deletion tftypes/value_msgpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ func marshalMsgPackNumber(val Value, typ Type, p *AttributePath, enc *msgpack.En
if err != nil {
return p.NewErrorf("error encoding int value: %w", err)
}
} else if fv, acc := n.Float64(); acc == big.Exact {
} else if fv, acc := n.Float64(); acc == big.Exact && !n.IsInt() {
err := enc.EncodeFloat64(fv)
if err != nil {
return p.NewErrorf("error encoding float value: %w", err)
Expand Down
16 changes: 13 additions & 3 deletions tftypes/value_msgpack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func TestValueFromMsgPack(t *testing.T) {
if err != nil {
t.Fatalf("error parsing awkward fraction: %s", err)
}

// integer under 64 bits which rounds incorrectly if parsed as a float64
uint64AsFloat, _ := new(big.Float).SetString("9223372036854775808")

tests := map[string]testCase{
"hello-string": {
hex: "a568656c6c6f",
Expand Down Expand Up @@ -94,7 +98,8 @@ func TestValueFromMsgPack(t *testing.T) {
typ: Number,
},
"float64-positive-number": {
hex: "cb7fefffffffffffff",
// Because MaxFloat64 is an integer value, it must be encoded as an integer to ensure we don't lose precision when decoding the value
hex: "da0135313739373639333133343836323331353730303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030",
value: NewValue(Number, new(big.Float).SetFloat64(math.MaxFloat64)),
typ: Number,
},
Expand Down Expand Up @@ -123,6 +128,11 @@ func TestValueFromMsgPack(t *testing.T) {
value: NewValue(Number, big.NewFloat(math.Inf(-1))),
typ: Number,
},
"large-uint64": {
hex: "b339323233333732303336383534373735383038",
value: NewValue(Number, uint64AsFloat),
typ: Number,
},
"dynamic-bool": {
hex: "92c40622626f6f6c22c3",
value: NewValue(Bool, true),
Expand Down Expand Up @@ -545,8 +555,8 @@ func TestValueFromMsgPack(t *testing.T) {
t.Fatalf("unexpected error unmarshaling: %s", err)
}

if diff := cmp.Diff(test.value, val); diff != "" {
t.Errorf("Unexpected results (-wanted +got): %s", diff)
if test.value.String() != val.String() {
t.Errorf("Unexpected results (-wanted +got): %s", cmp.Diff(test.value, val))
}
})
}
Expand Down

0 comments on commit 865883d

Please sign in to comment.