Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R4R: Increase decimal precision to 18 #3315

Merged
merged 6 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
58 changes: 36 additions & 22 deletions types/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ type Dec struct {

// number of decimal places
const (
Precision = 10
Precision = 18

// bytes required to represent the above precision
// ceil(log2(9999999999))
DecimalPrecisionBits = 34
// Ceiling[Log2[999 999 999 999 999 999]]
DecimalPrecisionBits = 60
)

var (
Expand Down Expand Up @@ -142,12 +142,14 @@ func NewDecFromStr(str string) (d Dec, err Error) {
strs := strings.Split(str, ".")
lenDecs := 0
combinedStr := strs[0]
if len(strs) == 2 {

if len(strs) == 2 { // has a decimal place
lenDecs = len(strs[1])
if lenDecs == 0 || len(combinedStr) == 0 {
return d, ErrUnknownRequest("bad decimal length")
}
combinedStr = combinedStr + strs[1]

} else if len(strs) > 2 {
return d, ErrUnknownRequest("too many periods to be a decimal string")
}
Expand All @@ -162,7 +164,7 @@ func NewDecFromStr(str string) (d Dec, err Error) {
zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "")
combinedStr = combinedStr + zeros

combined, ok := new(big.Int).SetString(combinedStr, 10)
combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10
if !ok {
return d, ErrUnknownRequest(fmt.Sprintf("bad string to integer conversion, combinedStr: %v", combinedStr))
}
Expand Down Expand Up @@ -276,36 +278,48 @@ func (d Dec) String() string {
if d.IsNegative() {
d = d.Neg()
}
bz, err := d.Int.MarshalText()

bzInt, err := d.Int.MarshalText()
if err != nil {
return ""
}
var bzWDec []byte
inputSize := len(bz)
inputSize := len(bzInt)

var bzStr []byte

// TODO: Remove trailing zeros
// case 1, purely decimal
if inputSize <= 10 {
bzWDec = make([]byte, 12)
if inputSize <= Precision {

bzStr = make([]byte, Precision+2)

// 0. prefix
bzWDec[0] = byte('0')
bzWDec[1] = byte('.')
bzStr[0] = byte('0')
bzStr[1] = byte('.')

// set relevant digits to 0
for i := 0; i < 10-inputSize; i++ {
bzWDec[i+2] = byte('0')
for i := 0; i < Precision-inputSize; i++ {
bzStr[i+2] = byte('0')
}
// set last few digits
copy(bzWDec[2+(10-inputSize):], bz)

// set final digits
copy(bzStr[2+(Precision-inputSize):], bzInt)

} else {

// inputSize + 1 to account for the decimal point that is being added
bzWDec = make([]byte, inputSize+1)
copy(bzWDec, bz[:inputSize-10])
bzWDec[inputSize-10] = byte('.')
copy(bzWDec[inputSize-9:], bz[inputSize-10:])
bzStr = make([]byte, inputSize+1)
decPointPlace := inputSize - Precision

copy(bzStr, bzInt[:decPointPlace]) // pre-decimal digits
bzStr[decPointPlace] = byte('.') // decimal point
copy(bzStr[decPointPlace+1:], bzInt[decPointPlace:]) // post-decimal digits
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the vital change

}

if isNeg {
return "-" + string(bzWDec)
return "-" + string(bzStr)
}
return string(bzWDec)
return string(bzStr)
}

// ____
Expand Down
45 changes: 32 additions & 13 deletions types/decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func mustNewDecFromStr(t *testing.T, str string) (d Dec) {

func TestPrecisionMultiplier(t *testing.T) {
res := precisionMultiplier(5)
exp := big.NewInt(100000)
exp := big.NewInt(10000000000000)
require.Equal(t, 0, res.Cmp(exp), "equality was incorrect, res %v, exp %v", res, exp)
}

Expand Down Expand Up @@ -76,6 +76,25 @@ func TestNewDecFromStr(t *testing.T) {
}
}

func TestDecString(t *testing.T) {
tests := []struct {
d Dec
want string
}{
{NewDec(0), "0.000000000000000000"},
{NewDec(1), "1.000000000000000000"},
{NewDec(10), "10.000000000000000000"},
{NewDec(12340), "12340.000000000000000000"},
{NewDecWithPrec(12340, 4), "1.234000000000000000"},
{NewDecWithPrec(12340, 5), "0.123400000000000000"},
{NewDecWithPrec(12340, 8), "0.000123400000000000"},
{NewDecWithPrec(1009009009009009009, 17), "10.090090090090090090"},
}
for tcIndex, tc := range tests {
assert.Equal(t, tc.want, tc.d.String(), "bad String(), index: %v", tcIndex)
}
}

func TestEqualities(t *testing.T) {
tests := []struct {
d1, d2 Dec
Expand Down Expand Up @@ -140,7 +159,7 @@ func TestArithmetic(t *testing.T) {
d1, d2 Dec
expMul, expDiv, expAdd, expSub Dec
}{
// d1 d2 MUL DIV ADD SUB
// d1 d2 MUL DIV ADD SUB
{NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0)},
{NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(1)},
{NewDec(0), NewDec(1), NewDec(0), NewDec(0), NewDec(1), NewDec(-1)},
Expand All @@ -152,14 +171,14 @@ func TestArithmetic(t *testing.T) {
{NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(2)},
{NewDec(-1), NewDec(1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(-2)},

{NewDec(3), NewDec(7), NewDec(21), NewDecWithPrec(4285714286, 10), NewDec(10), NewDec(-4)},
{NewDec(3), NewDec(7), NewDec(21), NewDecWithPrec(428571428571428571, 18), NewDec(10), NewDec(-4)},
{NewDec(2), NewDec(4), NewDec(8), NewDecWithPrec(5, 1), NewDec(6), NewDec(-2)},
{NewDec(100), NewDec(100), NewDec(10000), NewDec(1), NewDec(200), NewDec(0)},

{NewDecWithPrec(15, 1), NewDecWithPrec(15, 1), NewDecWithPrec(225, 2),
NewDec(1), NewDec(3), NewDec(0)},
{NewDecWithPrec(3333, 4), NewDecWithPrec(333, 4), NewDecWithPrec(1109889, 8),
NewDecWithPrec(10009009009, 9), NewDecWithPrec(3666, 4), NewDecWithPrec(3, 1)},
MustNewDecFromStr("10.009009009009009009"), NewDecWithPrec(3666, 4), NewDecWithPrec(3, 1)},
}

for tcIndex, tc := range tests {
Expand Down Expand Up @@ -245,14 +264,14 @@ func TestDecMarshalJSON(t *testing.T) {
want string
wantErr bool // if wantErr = false, will also attempt unmarshaling
}{
{"zero", decimal(0), "\"0.0000000000\"", false},
{"one", decimal(1), "\"0.0000000001\"", false},
{"ten", decimal(10), "\"0.0000000010\"", false},
{"12340", decimal(12340), "\"0.0000012340\"", false},
{"zeroInt", NewDec(0), "\"0.0000000000\"", false},
{"oneInt", NewDec(1), "\"1.0000000000\"", false},
{"tenInt", NewDec(10), "\"10.0000000000\"", false},
{"12340Int", NewDec(12340), "\"12340.0000000000\"", false},
{"zero", decimal(0), "\"0.000000000000000000\"", false},
{"one", decimal(1), "\"0.000000000000000001\"", false},
{"ten", decimal(10), "\"0.000000000000000010\"", false},
{"12340", decimal(12340), "\"0.000000000000012340\"", false},
{"zeroInt", NewDec(0), "\"0.000000000000000000\"", false},
{"oneInt", NewDec(1), "\"1.000000000000000000\"", false},
{"tenInt", NewDec(10), "\"10.000000000000000000\"", false},
{"12340Int", NewDec(12340), "\"12340.000000000000000000\"", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -344,7 +363,7 @@ func TestStringOverflow(t *testing.T) {
require.NoError(t, err)
dec3 := dec1.Add(dec2)
require.Equal(t,
"19844653375691057515930281852116324640.0000000000",
"19844653375691057515930281852116324640.000000000000000000",
dec3.String(),
)
}
Expand Down