mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-05 08:10:50 +00:00
Merge pull request #314 from travisturner/decimal-adjust-precision
Adjust decimal precision if we have decimal places to sacrifice.
This commit is contained in:
commit
8cf06bf5d9
2 changed files with 65 additions and 2 deletions
|
|
@ -396,6 +396,17 @@ func ParseDecimal(s string) (Decimal, error) {
|
|||
scale = 0
|
||||
}
|
||||
|
||||
// If the mantissa can't be represented by an int64, but it contains
|
||||
// enough decimal places such that we can sacrifice precision, then
|
||||
// we do that. This is an attempt to be compatible with the way
|
||||
// `strconv.ParseFloat` works.
|
||||
if m, s, ok := reducePrecision(sign, mantissa, scale); ok {
|
||||
mantissa = m
|
||||
scale = s
|
||||
} else {
|
||||
return Decimal{}, errors.Errorf("value out of range: %s", mantissa)
|
||||
}
|
||||
|
||||
// We have to use ParseUint here (as opposed to ParseInt) because
|
||||
// math.MinInt64 is a valid value, but its absolute value is not.
|
||||
// So this allows us to handle that one value without overflow, and
|
||||
|
|
@ -420,6 +431,48 @@ func ParseDecimal(s string) (Decimal, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// reducePrecision takes a []byte mantissa and scale, and if possible
|
||||
// will adjust the mantissa (by reducing precision) until it can be
|
||||
// represented by an int64. The returned bool indicates whether the
|
||||
// reduction was successful.
|
||||
func reducePrecision(sign bool, mantissa []byte, scale int64) ([]byte, int64, bool) {
|
||||
// Trim leading zeros before considering length.
|
||||
var zeroIdx int
|
||||
for i := range mantissa {
|
||||
if mantissa[i] == '0' {
|
||||
zeroIdx++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
mantissa = mantissa[zeroIdx:]
|
||||
|
||||
// If we zero out the mantissa to an empty
|
||||
// string, that means it's value should be 0.
|
||||
if len(mantissa) == 0 {
|
||||
mantissa = []byte{'0'}
|
||||
return mantissa, scale, true
|
||||
}
|
||||
|
||||
lenMantissa := len(mantissa)
|
||||
maxStr := "9223372036854775807"
|
||||
if sign {
|
||||
maxStr = "9223372036854775808"
|
||||
}
|
||||
|
||||
if lenMantissa <= 18 || (lenMantissa == 19 && string(mantissa) <= maxStr) {
|
||||
return mantissa, scale, true
|
||||
}
|
||||
|
||||
// If we don't have any decimal places to sacrifice,
|
||||
// we can't change anything.
|
||||
if scale <= 0 {
|
||||
return mantissa, scale, false
|
||||
}
|
||||
|
||||
return reducePrecision(sign, mantissa[:len(mantissa)-1], scale-1)
|
||||
}
|
||||
|
||||
func quotient(d Decimal) int64 {
|
||||
if d.Scale == 0 {
|
||||
return d.Value
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ func TestDecimal(t *testing.T) {
|
|||
{"9223372036854775807000", pql.Decimal{9223372036854775807, -3}, ""},
|
||||
{"-9223372036854775807000", pql.Decimal{-9223372036854775807, -3}, ""},
|
||||
|
||||
// precision adjustment
|
||||
{"2.666666666666666667", pql.Decimal{2666666666666666667, 18}, ""},
|
||||
{"2.6666666666666666667", pql.Decimal{2666666666666666666, 18}, ""},
|
||||
{"2.6666666666666666666667", pql.Decimal{2666666666666666666, 18}, ""},
|
||||
{"-9.223372036854775808", pql.Decimal{-9223372036854775808, 18}, ""},
|
||||
{"-9.223372036854775809", pql.Decimal{-922337203685477580, 17}, ""},
|
||||
{"9.223372036854775807", pql.Decimal{9223372036854775807, 18}, ""},
|
||||
{"9.223372036854775808", pql.Decimal{922337203685477580, 17}, ""},
|
||||
|
||||
// Error cases.
|
||||
{"", pql.Decimal{}, "decimal string is empty"},
|
||||
{"-", pql.Decimal{}, "decimal string is empty"},
|
||||
|
|
@ -75,8 +84,9 @@ func TestDecimal(t *testing.T) {
|
|||
{"0.12.3", pql.Decimal{}, "invalid decimal string"},
|
||||
{"--12300", pql.Decimal{}, "invalid syntax"},
|
||||
|
||||
{"922337203685477580.9", pql.Decimal{}, "value out of range"},
|
||||
{"-922337203685477580.9", pql.Decimal{}, "value out of range"},
|
||||
// These are no longer error cases since we introduced precision adjustment.
|
||||
//{"922337203685477580.9", pql.Decimal{}, "value out of range"},
|
||||
//{"-922337203685477580.9", pql.Decimal{}, "value out of range"},
|
||||
{"9223372036854775808000", pql.Decimal{}, "value out of range"},
|
||||
{"-9223372036854775809000", pql.Decimal{}, "value out of range"},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue