Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 34 additions & 8 deletions big.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (bigIntCodec) nilsLast() Codec[*big.Int] {
// the standard library just doesn't expose that information.
// A description of the encoding and why it does what it does follows.
//
// Shift a copy of the big.Float so that:
// If not infinite or zero, shift a copy of the big.Float so that:
//
// all significant bits are to the left of the point,
// the high bit of the high byte is 1, and
Expand Down Expand Up @@ -156,7 +156,12 @@ func (bigIntCodec) nilsLast() Codec[*big.Int] {
// write prefixNilFirst/Last if value is nil and return immediately
// write prefixNonNil
// write int8: negInf/negFinite/negZero/posZero/posFinite/posInf
// if infinite or zero, we're done
// if infinite, we're done
// if zero
// write int32 precision
// negate precision first if Float is negative
// write uint8 rounding mode
// we're done
// write int32 exponent
// negate exponent first if Float is negative
// write the (big-endian) bytes of the big.Int of the shifted mantissa
Expand Down Expand Up @@ -213,7 +218,7 @@ func (c bigFloatCodec) Append(buf []byte, value *big.Float) []byte {
shift := computeShift(exp, prec)

isInf := value.IsInf()
isZero := prec == 0
isZero := value.Sign() == 0

var kind int8
switch {
Expand All @@ -231,9 +236,17 @@ func (c bigFloatCodec) Append(buf []byte, value *big.Float) []byte {
kind = posFinite
}
buf = stdInt8.Append(buf, kind)
if isInf || isZero {
if isInf {
return buf
}
if isZero {
if signbit {
prec = -prec
}
buf = stdInt32.Append(buf, int32(prec))
return modeCodec.Append(buf, mode)
}

mantSize := numBytes(prec)
start := len(buf)
// 9 = 4 (exp) + 4 (prec) + 1 (mode)
Expand Down Expand Up @@ -269,7 +282,7 @@ func (c bigFloatCodec) Append(buf []byte, value *big.Float) []byte {
return buf
}

//nolint:cyclop
//nolint:cyclop,funlen
func (c bigFloatCodec) Put(buf []byte, value *big.Float) []byte {
done, buf := c.prefix.Put(buf, value == nil)
if done {
Expand All @@ -284,7 +297,7 @@ func (c bigFloatCodec) Put(buf []byte, value *big.Float) []byte {
shift := computeShift(exp, prec)

isInf := value.IsInf()
isZero := prec == 0
isZero := value.Sign() == 0

var kind int8
switch {
Expand All @@ -302,11 +315,18 @@ func (c bigFloatCodec) Put(buf []byte, value *big.Float) []byte {
kind = posFinite
}
buf = stdInt8.Put(buf, kind)
if isInf || isZero {
if isInf {
return buf
}
mantSize := numBytes(prec)
if isZero {
if signbit {
prec = -prec
}
buf = stdInt32.Put(buf, int32(prec))
return modeCodec.Put(buf, mode)
}

mantSize := numBytes(prec)
var tmp big.Float
tmp.SetMantExp(value, shift)
mantInt, acc := tmp.Int(nil)
Expand Down Expand Up @@ -343,10 +363,16 @@ func (c bigFloatCodec) Get(buf []byte) (*big.Float, []byte) {
return value.SetInf(signbit), buf
}
if kind == negZero || kind == posZero {
//nolint:govet
prec, buf := stdInt32.Get(buf)
mode, buf := modeCodec.Get(buf)
var value big.Float
if signbit {
prec = -prec
value.Neg(&value)
}
value.SetPrec(uint(prec))
value.SetMode(mode)
return &value, buf
}

Expand Down
56 changes: 34 additions & 22 deletions big_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,19 @@ func newBigFloat64(f float64, shift int, prec uint) *big.Float {
return value
}

func newBigFloatPosZero(prec uint) *big.Float {
var value big.Float
value.SetPrec(prec)
return &value
}

func newBigFloatNegZero(prec uint) *big.Float {
var value big.Float
value.SetPrec(prec)
value.Neg(&value)
return &value
}

func newBigFloat(s string) *big.Float {
var value big.Float
// Parse truncates to 64 bits if precision is currently 0.
Expand All @@ -114,14 +127,9 @@ func newBigFloat(s string) *big.Float {

func TestBigFloat(t *testing.T) {
t.Parallel()
var negInf, posInf, negZero, posZero big.Float
var negInf, posInf big.Float
negInf.SetInf(true)
posInf.SetInf(false)
negZero.Neg(&negZero)
assert.True(t, negZero.Signbit())
assert.False(t, posZero.Signbit())
assert.Equal(t, 0, negZero.Cmp(&posZero))
assert.NotEqual(t, &negZero, &posZero)

wholeNumber := newBigFloat(manyDigits + manyDigits)
mixedNumber := newBigFloat(manyDigits + "." + manyDigits)
Expand All @@ -132,12 +140,12 @@ func TestBigFloat(t *testing.T) {
testCodec(t, codec, fillTestData(codec, []testCase[*big.Float]{
{"nil", nil, nil},
// example in implementation comments
{"seven(3)", newBigFloat64(7.0, 0, 3), nil},
{"seven(4)", newBigFloat64(7.0, 0, 4), nil},
{"seven(10)", newBigFloat64(7.0, 0, 10), nil},
{"-seven(3)", newBigFloat64(-7.0, 0, 3), nil},
{"-seven(4)", newBigFloat64(-7.0, 0, 4), nil},
{"-seven(10)", newBigFloat64(-7.0, 0, 10), nil},
{"seven (3)", newBigFloat64(7.0, 0, 3), nil},
{"seven (4)", newBigFloat64(7.0, 0, 4), nil},
{"seven (10)", newBigFloat64(7.0, 0, 10), nil},
{"-seven (3)", newBigFloat64(-7.0, 0, 3), nil},
{"-seven (4)", newBigFloat64(-7.0, 0, 4), nil},
{"-seven (10)", newBigFloat64(-7.0, 0, 10), nil},

{"tiny", newBigFloat64(12345.0, -100, 20), nil},
{"mixed", newBigFloat64(12345.0, -10, 20), nil},
Expand All @@ -148,8 +156,13 @@ func TestBigFloat(t *testing.T) {

{"-Inf", &negInf, nil},
{"+Inf", &posInf, nil},
{"-0", &negZero, nil},
{"+0", &posZero, nil},

{"-0.0 (0)", newBigFloatNegZero(0), nil},
{"-0.0 (10)", newBigFloatNegZero(10), nil},
{"-0.0 (20)", newBigFloatNegZero(20), nil},
{"+0.0 (0)", newBigFloatPosZero(0), nil},
{"+0.0 (10)", newBigFloatPosZero(10), nil},
{"+0.0 (20)", newBigFloatPosZero(20), nil},

{"long whole", wholeNumber, nil},
{"long mixed", mixedNumber, nil},
Expand All @@ -159,14 +172,9 @@ func TestBigFloat(t *testing.T) {

func TestBigFloatOrdering(t *testing.T) {
t.Parallel()
var negInf, posInf, negZero, posZero big.Float
var negInf, posInf big.Float
negInf.SetInf(true)
posInf.SetInf(false)
negZero.Neg(&negZero)
assert.True(t, negZero.Signbit())
assert.False(t, posZero.Signbit())
assert.Equal(t, 0, negZero.Cmp(&posZero))
assert.NotEqual(t, &negZero, &posZero)

// For the same matissa, a higher exponent (first) or precision is closer to infinity.
testOrdering(t, lexy.BigFloat(), []testCase[*big.Float]{
Expand Down Expand Up @@ -201,8 +209,12 @@ func TestBigFloatOrdering(t *testing.T) {
{"-12345.0 * 2^-10000 (19)", newBigFloat64(-12345.0, -10000, 19), nil},

// zeros
{"-0.0", &negZero, nil},
{"+0.0", &posZero, nil},
{"-0.0 (20)", newBigFloatNegZero(20), nil},
{"-0.0 (10)", newBigFloatNegZero(10), nil},
{"-0.0 (0)", newBigFloatNegZero(0), nil},
{"+0.0 (0)", newBigFloatPosZero(0), nil},
{"+0.0 (10)", newBigFloatPosZero(10), nil},
{"+0.0 (20)", newBigFloatPosZero(20), nil},

// very small positive numbers
{"12345.0 * 2^-10000 (19)", newBigFloat64(12345.0, -10000, 19), nil},
Expand Down
2 changes: 0 additions & 2 deletions float_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ import (
// Bit masks for the sign, exponent, and matissa of the
// IEEE 754 32- and 64- floating point representations.
const (
maskSign32 uint32 = 0x80_00_00_00
maskExp32 uint32 = 0x7F_80_00_00
maskMant32 uint32 = 0x00_7F_FF_FF
maskSign64 uint64 = 0x80_00_00_00_00_00_00_00
maskExp64 uint64 = 0x7F_F0_00_00_00_00_00_00
maskMant64 uint64 = 0x00_0F_FF_FF_FF_FF_FF_FF
)
Expand Down
2 changes: 1 addition & 1 deletion fuzz-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ do
do
echo "Fuzzing $func in $file"
parentDir=$(dirname $file)
go test $parentDir -run=$func -fuzz=$func -fuzztime=${fuzzTime}s
go test $parentDir -run=$func -fuzz=$func -fuzztime=${fuzzTime}s -timeout 0
done
done