Skip to content

Commit 865883d

Browse files
jbardinaustinvalle
andauthored
don't marshal integer values as msgpack floats (#396)
* 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 <[email protected]>
1 parent 6d19ca6 commit 865883d

File tree

3 files changed

+20
-4
lines changed

3 files changed

+20
-4
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: BUG FIXES
2+
body: 'tftypes: Large integers are always encoded as msgpack integers rather than
3+
float values to ensure the decoded value will not be rounded to 52-bit precision'
4+
time: 2024-04-18T12:18:11.880247-04:00
5+
custom:
6+
Issue: "396"

tftypes/value_msgpack.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ func marshalMsgPackNumber(val Value, typ Type, p *AttributePath, enc *msgpack.En
446446
if err != nil {
447447
return p.NewErrorf("error encoding int value: %w", err)
448448
}
449-
} else if fv, acc := n.Float64(); acc == big.Exact {
449+
} else if fv, acc := n.Float64(); acc == big.Exact && !n.IsInt() {
450450
err := enc.EncodeFloat64(fv)
451451
if err != nil {
452452
return p.NewErrorf("error encoding float value: %w", err)

tftypes/value_msgpack_test.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ func TestValueFromMsgPack(t *testing.T) {
2727
if err != nil {
2828
t.Fatalf("error parsing awkward fraction: %s", err)
2929
}
30+
31+
// integer under 64 bits which rounds incorrectly if parsed as a float64
32+
uint64AsFloat, _ := new(big.Float).SetString("9223372036854775808")
33+
3034
tests := map[string]testCase{
3135
"hello-string": {
3236
hex: "a568656c6c6f",
@@ -94,7 +98,8 @@ func TestValueFromMsgPack(t *testing.T) {
9498
typ: Number,
9599
},
96100
"float64-positive-number": {
97-
hex: "cb7fefffffffffffff",
101+
// Because MaxFloat64 is an integer value, it must be encoded as an integer to ensure we don't lose precision when decoding the value
102+
hex: "da0135313739373639333133343836323331353730303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030",
98103
value: NewValue(Number, new(big.Float).SetFloat64(math.MaxFloat64)),
99104
typ: Number,
100105
},
@@ -123,6 +128,11 @@ func TestValueFromMsgPack(t *testing.T) {
123128
value: NewValue(Number, big.NewFloat(math.Inf(-1))),
124129
typ: Number,
125130
},
131+
"large-uint64": {
132+
hex: "b339323233333732303336383534373735383038",
133+
value: NewValue(Number, uint64AsFloat),
134+
typ: Number,
135+
},
126136
"dynamic-bool": {
127137
hex: "92c40622626f6f6c22c3",
128138
value: NewValue(Bool, true),
@@ -545,8 +555,8 @@ func TestValueFromMsgPack(t *testing.T) {
545555
t.Fatalf("unexpected error unmarshaling: %s", err)
546556
}
547557

548-
if diff := cmp.Diff(test.value, val); diff != "" {
549-
t.Errorf("Unexpected results (-wanted +got): %s", diff)
558+
if test.value.String() != val.String() {
559+
t.Errorf("Unexpected results (-wanted +got): %s", cmp.Diff(test.value, val))
550560
}
551561
})
552562
}

0 commit comments

Comments
 (0)