Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gostnops
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wei.xuan
gostnops
Commits
8640ee84
Commit
8640ee84
authored
Jul 05, 2019
by
aliiohs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add decimal and test case
parent
65c3ecbb
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
3284 additions
and
0 deletions
+3284
-0
decimal.go
math/big/decimal.go
+2288
-0
decimal_benchmark_test.go
math/big/decimal_benchmark_test.go
+59
-0
decimal_test.go
math/big/decimal_test.go
+937
-0
No files found.
math/big/decimal.go
0 → 100644
View file @
8640ee84
// Copyright 2016-2019 Alex Stocks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package
hessian
import
(
"github.com/pkg/errors"
"math"
"strconv"
)
var
(
ErrBadNumber
=
errors
.
Errorf
(
""
)
ErrOverflow
=
errors
.
Errorf
(
""
)
ErrTruncated
=
errors
.
Errorf
(
""
)
ErrDivByZero
=
errors
.
Errorf
(
""
)
)
// RoundMode is the type for round mode.
type
RoundMode
int32
// constant values.
const
(
ten0
=
1
ten1
=
10
ten2
=
100
ten3
=
1000
ten4
=
10000
ten5
=
100000
ten6
=
1000000
ten7
=
10000000
ten8
=
100000000
ten9
=
1000000000
maxWordBufLen
=
9
// A MyDecimal holds 9 words.
digitsPerWord
=
9
// A word holds 9 digits.
wordSize
=
4
// A word is 4 bytes int32.
digMask
=
ten8
wordBase
=
ten9
wordMax
=
wordBase
-
1
notFixedDec
=
31
DivFracIncr
=
4
// ModeHalfEven rounds normally.
ModeHalfEven
RoundMode
=
5
// Truncate just truncates the decimal.
ModeTruncate
RoundMode
=
10
// Ceiling is not supported now.
modeCeiling
RoundMode
=
0
maxDecimalScale
=
30
)
var
(
wordBufLen
=
9
mod9
=
[
128
]
int8
{
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
0
,
1
,
}
div9
=
[
128
]
int
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
10
,
10
,
10
,
10
,
10
,
10
,
10
,
10
,
10
,
11
,
11
,
11
,
11
,
11
,
11
,
11
,
11
,
11
,
12
,
12
,
12
,
12
,
12
,
12
,
12
,
12
,
12
,
13
,
13
,
13
,
13
,
13
,
13
,
13
,
13
,
13
,
14
,
14
,
}
powers10
=
[
10
]
int32
{
ten0
,
ten1
,
ten2
,
ten3
,
ten4
,
ten5
,
ten6
,
ten7
,
ten8
,
ten9
}
dig2bytes
=
[
10
]
int
{
0
,
1
,
1
,
2
,
2
,
3
,
3
,
4
,
4
,
4
}
fracMax
=
[
8
]
int32
{
900000000
,
990000000
,
999000000
,
999900000
,
999990000
,
999999000
,
999999900
,
999999990
,
}
zeroBigDecimal
=
BigDecimal
{}
)
// add adds a and b and carry, returns the sum and new carry.
func
add
(
a
,
b
,
carry
int32
)
(
int32
,
int32
)
{
sum
:=
a
+
b
+
carry
if
sum
>=
wordBase
{
carry
=
1
sum
-=
wordBase
}
else
{
carry
=
0
}
return
sum
,
carry
}
// add2 adds a and b and carry, returns the sum and new carry.
// It is only used in DecimalMul.
func
add2
(
a
,
b
,
carry
int32
)
(
int32
,
int32
)
{
sum
:=
int64
(
a
)
+
int64
(
b
)
+
int64
(
carry
)
if
sum
>=
wordBase
{
carry
=
1
sum
-=
wordBase
}
else
{
carry
=
0
}
if
sum
>=
wordBase
{
sum
-=
wordBase
carry
++
}
return
int32
(
sum
),
carry
}
// sub subtracts b and carry from a, returns the diff and new carry.
func
sub
(
a
,
b
,
carry
int32
)
(
int32
,
int32
)
{
diff
:=
a
-
b
-
carry
if
diff
<
0
{
carry
=
1
diff
+=
wordBase
}
else
{
carry
=
0
}
return
diff
,
carry
}
// sub2 subtracts b and carry from a, returns the diff and new carry.
// the new carry may be 2.
func
sub2
(
a
,
b
,
carry
int32
)
(
int32
,
int32
)
{
diff
:=
a
-
b
-
carry
if
diff
<
0
{
carry
=
1
diff
+=
wordBase
}
else
{
carry
=
0
}
if
diff
<
0
{
diff
+=
wordBase
carry
++
}
return
diff
,
carry
}
// fixWordCntError limits word count in wordBufLen, and returns overflow or truncate error.
func
fixWordCntError
(
wordsInt
,
wordsFrac
int
)
(
newWordsInt
int
,
newWordsFrac
int
,
err
error
)
{
if
wordsInt
+
wordsFrac
>
wordBufLen
{
if
wordsInt
>
wordBufLen
{
return
wordBufLen
,
0
,
ErrOverflow
}
return
wordsInt
,
wordBufLen
-
wordsInt
,
ErrTruncated
}
return
wordsInt
,
wordsFrac
,
nil
}
/*
countLeadingZeroes returns the number of leading zeroes that can be removed from fraction.
@param i start index
@param word value to compare against list of powers of 10
*/
func
countLeadingZeroes
(
i
int
,
word
int32
)
int
{
leading
:=
0
for
word
<
powers10
[
i
]
{
i
--
leading
++
}
return
leading
}
/*
countTrailingZeros returns the number of trailing zeroes that can be removed from fraction.
@param i start index
@param word value to compare against list of powers of 10
*/
func
countTrailingZeroes
(
i
int
,
word
int32
)
int
{
trailing
:=
0
for
word
%
powers10
[
i
]
==
0
{
i
++
trailing
++
}
return
trailing
}
func
digitsToWords
(
digits
int
)
int
{
if
digits
+
digitsPerWord
-
1
>=
0
&&
digits
+
digitsPerWord
-
1
<
128
{
return
div9
[
digits
+
digitsPerWord
-
1
]
}
return
(
digits
+
digitsPerWord
-
1
)
/
digitsPerWord
}
// BigDecimalStructSize is the struct size of BigDecimal.
const
BigDecimalStructSize
=
40
// BigDecimal represents a decimal value.
type
BigDecimal
struct
{
digitsInt
int8
// the number of *decimal* digits before the point.
digitsFrac
int8
// the number of decimal digits after the point.
resultFrac
int8
// result fraction digits.
negative
bool
// wordBuf is an array of int32 words.
// A word is an int32 value can hold 9 digits.(0 <= word < wordBase)
wordBuf
[
maxWordBufLen
]
int32
}
// IsNegative returns whether a decimal is negative.
func
(
d
*
BigDecimal
)
IsNegative
()
bool
{
return
d
.
negative
}
// GetDigitsFrac returns the digitsFrac.
func
(
d
*
BigDecimal
)
GetDigitsFrac
()
int8
{
return
d
.
digitsFrac
}
// String returns the decimal string representation rounded to resultFrac.
func
(
d
*
BigDecimal
)
String
()
string
{
tmp
:=
*
d
_
=
tmp
.
Round
(
&
tmp
,
int
(
tmp
.
resultFrac
),
ModeHalfEven
)
//todo terror.Log(errors.Trace(err))
return
string
(
tmp
.
ToString
())
}
func
(
d
*
BigDecimal
)
stringSize
()
int
{
// sign, zero integer and dot.
return
int
(
d
.
digitsInt
+
d
.
digitsFrac
+
3
)
}
func
(
d
*
BigDecimal
)
removeLeadingZeros
()
(
wordIdx
int
,
digitsInt
int
)
{
digitsInt
=
int
(
d
.
digitsInt
)
i
:=
((
digitsInt
-
1
)
%
digitsPerWord
)
+
1
for
digitsInt
>
0
&&
d
.
wordBuf
[
wordIdx
]
==
0
{
digitsInt
-=
i
i
=
digitsPerWord
wordIdx
++
}
if
digitsInt
>
0
{
digitsInt
-=
countLeadingZeroes
((
digitsInt
-
1
)
%
digitsPerWord
,
d
.
wordBuf
[
wordIdx
])
}
else
{
digitsInt
=
0
}
return
}
func
(
d
*
BigDecimal
)
removeTrailingZeros
()
(
lastWordIdx
int
,
digitsFrac
int
)
{
digitsFrac
=
int
(
d
.
digitsFrac
)
i
:=
((
digitsFrac
-
1
)
%
digitsPerWord
)
+
1
lastWordIdx
=
digitsToWords
(
int
(
d
.
digitsInt
))
+
digitsToWords
(
int
(
d
.
digitsFrac
))
for
digitsFrac
>
0
&&
d
.
wordBuf
[
lastWordIdx
-
1
]
==
0
{
digitsFrac
-=
i
i
=
digitsPerWord
lastWordIdx
--
}
if
digitsFrac
>
0
{
digitsFrac
-=
countTrailingZeroes
(
9
-
((
digitsFrac
-
1
)
%
digitsPerWord
),
d
.
wordBuf
[
lastWordIdx
-
1
])
}
else
{
digitsFrac
=
0
}
return
}
// ToString converts decimal to its printable string representation without rounding.
//
// RETURN VALUE
//
// str - result string
// errCode - eDecOK/eDecTruncate/eDecOverflow
//
func
(
d
*
BigDecimal
)
ToString
()
(
str
[]
byte
)
{
str
=
make
([]
byte
,
d
.
stringSize
())
digitsFrac
:=
int
(
d
.
digitsFrac
)
wordStartIdx
,
digitsInt
:=
d
.
removeLeadingZeros
()
if
digitsInt
+
digitsFrac
==
0
{
digitsInt
=
1
wordStartIdx
=
0
}
digitsIntLen
:=
digitsInt
if
digitsIntLen
==
0
{
digitsIntLen
=
1
}
digitsFracLen
:=
digitsFrac
length
:=
digitsIntLen
+
digitsFracLen
if
d
.
negative
{
length
++
}
if
digitsFrac
>
0
{
length
++
}
str
=
str
[
:
length
]
strIdx
:=
0
if
d
.
negative
{
str
[
strIdx
]
=
'-'
strIdx
++
}
var
fill
int
if
digitsFrac
>
0
{
fracIdx
:=
strIdx
+
digitsIntLen
fill
=
digitsFracLen
-
digitsFrac
wordIdx
:=
wordStartIdx
+
digitsToWords
(
digitsInt
)
str
[
fracIdx
]
=
'.'
fracIdx
++
for
;
digitsFrac
>
0
;
digitsFrac
-=
digitsPerWord
{
x
:=
d
.
wordBuf
[
wordIdx
]
wordIdx
++
for
i
:=
myMin
(
digitsFrac
,
digitsPerWord
);
i
>
0
;
i
--
{
y
:=
x
/
digMask
str
[
fracIdx
]
=
byte
(
y
)
+
'0'
fracIdx
++
x
-=
y
*
digMask
x
*=
10
}
}
for
;
fill
>
0
;
fill
--
{
str
[
fracIdx
]
=
'0'
fracIdx
++
}
}
fill
=
digitsIntLen
-
digitsInt
if
digitsInt
==
0
{
fill
--
/* symbol 0 before digital point */
}
for
;
fill
>
0
;
fill
--
{
str
[
strIdx
]
=
'0'
strIdx
++
}
if
digitsInt
>
0
{
strIdx
+=
digitsInt
wordIdx
:=
wordStartIdx
+
digitsToWords
(
digitsInt
)
for
;
digitsInt
>
0
;
digitsInt
-=
digitsPerWord
{
wordIdx
--
x
:=
d
.
wordBuf
[
wordIdx
]
for
i
:=
myMin
(
digitsInt
,
digitsPerWord
);
i
>
0
;
i
--
{
y
:=
x
/
10
strIdx
--
str
[
strIdx
]
=
'0'
+
byte
(
x
-
y
*
10
)
x
=
y
}
}
}
else
{
str
[
strIdx
]
=
'0'
}
return
}
// FromString parses decimal from string.
func
(
d
*
BigDecimal
)
FromString
(
str
[]
byte
)
error
{
for
i
:=
0
;
i
<
len
(
str
);
i
++
{
if
!
isSpace
(
str
[
i
])
{
str
=
str
[
i
:
]
break
}
}
if
len
(
str
)
==
0
{
*
d
=
zeroBigDecimal
return
ErrBadNumber
}
switch
str
[
0
]
{
case
'-'
:
d
.
negative
=
true
fallthrough
case
'+'
:
str
=
str
[
1
:
]
}
var
strIdx
int
for
strIdx
<
len
(
str
)
&&
isDigit
(
str
[
strIdx
])
{
strIdx
++
}
digitsInt
:=
strIdx
var
digitsFrac
int
var
endIdx
int
if
strIdx
<
len
(
str
)
&&
str
[
strIdx
]
==
'.'
{
endIdx
=
strIdx
+
1
for
endIdx
<
len
(
str
)
&&
isDigit
(
str
[
endIdx
])
{
endIdx
++
}
digitsFrac
=
endIdx
-
strIdx
-
1
}
else
{
digitsFrac
=
0
endIdx
=
strIdx
}
if
digitsInt
+
digitsFrac
==
0
{
*
d
=
zeroBigDecimal
return
ErrBadNumber
}
wordsInt
:=
digitsToWords
(
digitsInt
)
wordsFrac
:=
digitsToWords
(
digitsFrac
)
wordsInt
,
wordsFrac
,
err
:=
fixWordCntError
(
wordsInt
,
wordsFrac
)
if
err
!=
nil
{
digitsFrac
=
wordsFrac
*
digitsPerWord
if
err
==
ErrOverflow
{
digitsInt
=
wordsInt
*
digitsPerWord
}
}
d
.
digitsInt
=
int8
(
digitsInt
)
d
.
digitsFrac
=
int8
(
digitsFrac
)
wordIdx
:=
wordsInt
strIdxTmp
:=
strIdx
var
word
int32
var
innerIdx
int
for
digitsInt
>
0
{
digitsInt
--
strIdx
--
word
+=
int32
(
str
[
strIdx
]
-
'0'
)
*
powers10
[
innerIdx
]
innerIdx
++
if
innerIdx
==
digitsPerWord
{
wordIdx
--
d
.
wordBuf
[
wordIdx
]
=
word
word
=
0
innerIdx
=
0
}
}
if
innerIdx
!=
0
{
wordIdx
--
d
.
wordBuf
[
wordIdx
]
=
word
}
wordIdx
=
wordsInt
strIdx
=
strIdxTmp
word
=
0
innerIdx
=
0
for
digitsFrac
>
0
{
digitsFrac
--
strIdx
++
word
=
int32
(
str
[
strIdx
]
-
'0'
)
+
word
*
10
innerIdx
++
if
innerIdx
==
digitsPerWord
{
d
.
wordBuf
[
wordIdx
]
=
word
wordIdx
++
word
=
0
innerIdx
=
0
}
}
if
innerIdx
!=
0
{
d
.
wordBuf
[
wordIdx
]
=
word
*
powers10
[
digitsPerWord
-
innerIdx
]
}
if
endIdx
+
1
<=
len
(
str
)
&&
(
str
[
endIdx
]
==
'e'
||
str
[
endIdx
]
==
'E'
)
{
exponent
,
err1
:=
strToInt
(
string
(
str
[
endIdx
+
1
:
]))
if
err1
!=
nil
{
err
=
errors
.
Cause
(
err1
)
if
err
!=
ErrTruncated
{
*
d
=
zeroBigDecimal
}
}
if
exponent
>
math
.
MaxInt32
/
2
{
negative
:=
d
.
negative
maxDecimal
(
wordBufLen
*
digitsPerWord
,
0
,
d
)
d
.
negative
=
negative
err
=
ErrOverflow
}
if
exponent
<
math
.
MinInt32
/
2
&&
err
!=
ErrOverflow
{
*
d
=
zeroBigDecimal
err
=
ErrTruncated
}
if
err
!=
ErrOverflow
{
shiftErr
:=
d
.
Shift
(
int
(
exponent
))
if
shiftErr
!=
nil
{
if
shiftErr
==
ErrOverflow
{
negative
:=
d
.
negative
maxDecimal
(
wordBufLen
*
digitsPerWord
,
0
,
d
)
d
.
negative
=
negative
}
err
=
shiftErr
}
}
}
allZero
:=
true
for
i
:=
0
;
i
<
wordBufLen
;
i
++
{
if
d
.
wordBuf
[
i
]
!=
0
{
allZero
=
false
break
}
}
if
allZero
{
d
.
negative
=
false
}
d
.
resultFrac
=
d
.
digitsFrac
return
err
}
// Shift shifts decimal digits in given number (with rounding if it need), shift > 0 means shift to left shift,
// shift < 0 means right shift. In fact it is multiplying on 10^shift.
//
// RETURN
// eDecOK OK
// eDecOverflow operation lead to overflow, number is untoched
// eDecTruncated number was rounded to fit into buffer
//
func
(
d
*
BigDecimal
)
Shift
(
shift
int
)
error
{
var
err
error
if
shift
==
0
{
return
nil
}
var
(
// digitBegin is index of first non zero digit (all indexes from 0).
digitBegin
int
// digitEnd is index of position after last decimal digit.
digitEnd
int
// point is index of digit position just after point.
point
=
digitsToWords
(
int
(
d
.
digitsInt
))
*
digitsPerWord
// new point position.
newPoint
=
point
+
shift
// number of digits in result.
digitsInt
,
digitsFrac
int
newFront
int
)
digitBegin
,
digitEnd
=
d
.
digitBounds
()
if
digitBegin
==
digitEnd
{
*
d
=
zeroBigDecimal
return
nil
}
digitsInt
=
newPoint
-
digitBegin
if
digitsInt
<
0
{
digitsInt
=
0
}
digitsFrac
=
digitEnd
-
newPoint
if
digitsFrac
<
0
{
digitsFrac
=
0
}
wordsInt
:=
digitsToWords
(
digitsInt
)
wordsFrac
:=
digitsToWords
(
digitsFrac
)
newLen
:=
wordsInt
+
wordsFrac
if
newLen
>
wordBufLen
{
lack
:=
newLen
-
wordBufLen
if
wordsFrac
<
lack
{
return
ErrOverflow
}
/* cut off fraction part to allow new number to fit in our buffer */
err
=
ErrTruncated
wordsFrac
-=
lack
diff
:=
digitsFrac
-
wordsFrac
*
digitsPerWord
err1
:=
d
.
Round
(
d
,
digitEnd
-
point
-
diff
,
ModeHalfEven
)
if
err1
!=
nil
{
return
errors
.
Cause
(
err1
)
}
digitEnd
-=
diff
digitsFrac
=
wordsFrac
*
digitsPerWord
if
digitEnd
<=
digitBegin
{
/*
We lost all digits (they will be shifted out of buffer), so we can
just return 0.
*/
*
d
=
zeroBigDecimal
return
ErrTruncated
}
}
if
shift
%
digitsPerWord
!=
0
{
var
lMiniShift
,
rMiniShift
,
miniShift
int
var
doLeft
bool
/*
Calculate left/right shift to align decimal digits inside our bug
digits correctly.
*/
if
shift
>
0
{
lMiniShift
=
shift
%
digitsPerWord
rMiniShift
=
digitsPerWord
-
lMiniShift
doLeft
=
lMiniShift
<=
digitBegin
}
else
{
rMiniShift
=
(
-
shift
)
%
digitsPerWord
lMiniShift
=
digitsPerWord
-
rMiniShift
doLeft
=
(
digitsPerWord
*
wordBufLen
-
digitEnd
)
<
rMiniShift
}
if
doLeft
{
d
.
doMiniLeftShift
(
lMiniShift
,
digitBegin
,
digitEnd
)
miniShift
=
-
lMiniShift
}
else
{
d
.
doMiniRightShift
(
rMiniShift
,
digitBegin
,
digitEnd
)
miniShift
=
rMiniShift
}
newPoint
+=
miniShift
/*
If number is shifted and correctly aligned in buffer we can finish.
*/
if
shift
+
miniShift
==
0
&&
(
newPoint
-
digitsInt
)
<
digitsPerWord
{
d
.
digitsInt
=
int8
(
digitsInt
)
d
.
digitsFrac
=
int8
(
digitsFrac
)
return
err
/* already shifted as it should be */
}
digitBegin
+=
miniShift
digitEnd
+=
miniShift
}
/* if new 'decimal front' is in first digit, we do not need move digits */
newFront
=
newPoint
-
digitsInt
if
newFront
>=
digitsPerWord
||
newFront
<
0
{
/* need to move digits */
var
wordShift
int
if
newFront
>
0
{
/* move left */
wordShift
=
newFront
/
digitsPerWord
to
:=
digitBegin
/
digitsPerWord
-
wordShift
barier
:=
(
digitEnd
-
1
)
/
digitsPerWord
-
wordShift
for
;
to
<=
barier
;
to
++
{
d
.
wordBuf
[
to
]
=
d
.
wordBuf
[
to
+
wordShift
]
}
for
barier
+=
wordShift
;
to
<=
barier
;
to
++
{
d
.
wordBuf
[
to
]
=
0
}
wordShift
=
-
wordShift
}
else
{
/* move right */
wordShift
=
(
1
-
newFront
)
/
digitsPerWord
to
:=
(
digitEnd
-
1
)
/
digitsPerWord
+
wordShift
barier
:=
digitBegin
/
digitsPerWord
+
wordShift
for
;
to
>=
barier
;
to
--
{
d
.
wordBuf
[
to
]
=
d
.
wordBuf
[
to
-
wordShift
]
}
for
barier
-=
wordShift
;
to
>=
barier
;
to
--
{
d
.
wordBuf
[
to
]
=
0
}
}
digitShift
:=
wordShift
*
digitsPerWord
digitBegin
+=
digitShift
digitEnd
+=
digitShift
newPoint
+=
digitShift
}
/*
If there are gaps then fill them with 0.
Only one of following 'for' loops will work because wordIdxBegin <= wordIdxEnd.
*/
wordIdxBegin
:=
digitBegin
/
digitsPerWord
wordIdxEnd
:=
(
digitEnd
-
1
)
/
digitsPerWord
wordIdxNewPoint
:=
0
/* We don't want negative new_point below */
if
newPoint
!=
0
{
wordIdxNewPoint
=
(
newPoint
-
1
)
/
digitsPerWord
}
if
wordIdxNewPoint
>
wordIdxEnd
{
for
wordIdxNewPoint
>
wordIdxEnd
{
d
.
wordBuf
[
wordIdxNewPoint
]
=
0
wordIdxNewPoint
--
}
}
else
{
for
;
wordIdxNewPoint
<
wordIdxBegin
;
wordIdxNewPoint
++
{
d
.
wordBuf
[
wordIdxNewPoint
]
=
0
}
}
d
.
digitsInt
=
int8
(
digitsInt
)
d
.
digitsFrac
=
int8
(
digitsFrac
)
return
err
}
/*
digitBounds returns bounds of decimal digits in the number.
start - index (from 0 ) of first decimal digits.
end - index of position just after last decimal digit.
*/
func
(
d
*
BigDecimal
)
digitBounds
()
(
start
,
end
int
)
{
var
i
int
bufBeg
:=
0
bufLen
:=
digitsToWords
(
int
(
d
.
digitsInt
))
+
digitsToWords
(
int
(
d
.
digitsFrac
))
bufEnd
:=
bufLen
-
1
/* find non-zero digit from number beginning */
for
bufBeg
<
bufLen
&&
d
.
wordBuf
[
bufBeg
]
==
0
{
bufBeg
++
}
if
bufBeg
>=
bufLen
{
return
0
,
0
}
/* find non-zero decimal digit from number beginning */
if
bufBeg
==
0
&&
d
.
digitsInt
>
0
{
i
=
(
int
(
d
.
digitsInt
)
-
1
)
%
digitsPerWord
start
=
digitsPerWord
-
i
-
1
}
else
{
i
=
digitsPerWord
-
1
start
=
bufBeg
*
digitsPerWord
}
if
bufBeg
<
bufLen
{
start
+=
countLeadingZeroes
(
i
,
d
.
wordBuf
[
bufBeg
])
}
/* find non-zero digit at the end */
for
bufEnd
>
bufBeg
&&
d
.
wordBuf
[
bufEnd
]
==
0
{
bufEnd
--
}
/* find non-zero decimal digit from the end */
if
bufEnd
==
bufLen
-
1
&&
d
.
digitsFrac
>
0
{
i
=
(
int
(
d
.
digitsFrac
)
-
1
)
%
digitsPerWord
+
1
end
=
bufEnd
*
digitsPerWord
+
i
i
=
digitsPerWord
-
i
+
1
}
else
{
end
=
(
bufEnd
+
1
)
*
digitsPerWord
i
=
1
}
end
-=
countTrailingZeroes
(
i
,
d
.
wordBuf
[
bufEnd
])
return
start
,
end
}
/*
doMiniLeftShift does left shift for alignment of data in buffer.
shift number of decimal digits on which it should be shifted
beg/end bounds of decimal digits (see digitsBounds())
NOTE
Result fitting in the buffer should be garanted.
'shift' have to be from 1 to digitsPerWord-1 (inclusive)
*/
func
(
d
*
BigDecimal
)
doMiniLeftShift
(
shift
,
beg
,
end
int
)
{
bufFrom
:=
beg
/
digitsPerWord
bufEnd
:=
(
end
-
1
)
/
digitsPerWord
cShift
:=
digitsPerWord
-
shift
if
beg
%
digitsPerWord
<
shift
{
d
.
wordBuf
[
bufFrom
-
1
]
=
d
.
wordBuf
[
bufFrom
]
/
powers10
[
cShift
]
}
for
bufFrom
<
bufEnd
{
d
.
wordBuf
[
bufFrom
]
=
(
d
.
wordBuf
[
bufFrom
]
%
powers10
[
cShift
])
*
powers10
[
shift
]
+
d
.
wordBuf
[
bufFrom
+
1
]
/
powers10
[
cShift
]
bufFrom
++
}
d
.
wordBuf
[
bufFrom
]
=
(
d
.
wordBuf
[
bufFrom
]
%
powers10
[
cShift
])
*
powers10
[
shift
]
}
/*
doMiniRightShift does right shift for alignment of data in buffer.
shift number of decimal digits on which it should be shifted
beg/end bounds of decimal digits (see digitsBounds())
NOTE
Result fitting in the buffer should be garanted.
'shift' have to be from 1 to digitsPerWord-1 (inclusive)
*/
func
(
d
*
BigDecimal
)
doMiniRightShift
(
shift
,
beg
,
end
int
)
{
bufFrom
:=
(
end
-
1
)
/
digitsPerWord
bufEnd
:=
beg
/
digitsPerWord
cShift
:=
digitsPerWord
-
shift
if
digitsPerWord
-
((
end
-
1
)
%
digitsPerWord
+
1
)
<
shift
{
d
.
wordBuf
[
bufFrom
+
1
]
=
(
d
.
wordBuf
[
bufFrom
]
%
powers10
[
shift
])
*
powers10
[
cShift
]
}
for
bufFrom
>
bufEnd
{
d
.
wordBuf
[
bufFrom
]
=
d
.
wordBuf
[
bufFrom
]
/
powers10
[
shift
]
+
(
d
.
wordBuf
[
bufFrom
-
1
]
%
powers10
[
shift
])
*
powers10
[
cShift
]
bufFrom
--
}
d
.
wordBuf
[
bufFrom
]
=
d
.
wordBuf
[
bufFrom
]
/
powers10
[
shift
]
}
// Round rounds the decimal to "frac" digits.
//
// to - result buffer. d == to is allowed
// frac - to what position after fraction point to round. can be negative!
// roundMode - round to nearest even or truncate
// ModeHalfEven rounds normally.
// Truncate just truncates the decimal.
//
// NOTES
// scale can be negative !
// one TRUNCATED error (line XXX below) isn't treated very logical :(
//
// RETURN VALUE
// eDecOK/eDecTruncated
func
(
d
*
BigDecimal
)
Round
(
to
*
BigDecimal
,
frac
int
,
roundMode
RoundMode
)
(
err
error
)
{
// wordsFracTo is the number of fraction words in buffer.
wordsFracTo
:=
(
frac
+
1
)
/
digitsPerWord
if
frac
>
0
{
wordsFracTo
=
digitsToWords
(
frac
)
}
wordsFrac
:=
digitsToWords
(
int
(
d
.
digitsFrac
))
wordsInt
:=
digitsToWords
(
int
(
d
.
digitsInt
))
roundDigit
:=
int32
(
roundMode
)
/* TODO - fix this code as it won't work for CEILING mode */
if
wordsInt
+
wordsFracTo
>
wordBufLen
{
wordsFracTo
=
wordBufLen
-
wordsInt
frac
=
wordsFracTo
*
digitsPerWord
err
=
ErrTruncated
}
if
int
(
d
.
digitsInt
)
+
frac
<
0
{
*
to
=
zeroBigDecimal
return
nil
}
if
to
!=
d
{
copy
(
to
.
wordBuf
[
:
],
d
.
wordBuf
[
:
])
to
.
negative
=
d
.
negative
to
.
digitsInt
=
int8
(
myMin
(
wordsInt
,
wordBufLen
)
*
digitsPerWord
)
}
if
wordsFracTo
>
wordsFrac
{
idx
:=
wordsInt
+
wordsFrac
for
wordsFracTo
>
wordsFrac
{
wordsFracTo
--
to
.
wordBuf
[
idx
]
=
0
idx
++
}
to
.
digitsFrac
=
int8
(
frac
)
to
.
resultFrac
=
to
.
digitsFrac
return
}
if
frac
>=
int
(
d
.
digitsFrac
)
{
to
.
digitsFrac
=
int8
(
frac
)
to
.
resultFrac
=
to
.
digitsFrac
return
}
// Do increment.
toIdx
:=
wordsInt
+
wordsFracTo
-
1
if
frac
==
wordsFracTo
*
digitsPerWord
{
doInc
:=
false
switch
roundMode
{
// Notice: No support for ceiling mode now.
case
modeCeiling
:
// If any word after scale is not zero, do increment.
// e.g ceiling 3.0001 to scale 1, gets 3.1
idx
:=
toIdx
+
(
wordsFrac
-
wordsFracTo
)
for
idx
>
toIdx
{
if
d
.
wordBuf
[
idx
]
!=
0
{
doInc
=
true
break
}
idx
--
}
case
ModeHalfEven
:
digAfterScale
:=
d
.
wordBuf
[
toIdx
+
1
]
/
digMask
// the first digit after scale.
// If first digit after scale is 5 and round even, do increment if digit at scale is odd.
doInc
=
(
digAfterScale
>
5
)
||
(
digAfterScale
==
5
)
case
ModeTruncate
:
// Never round, just truncate.
doInc
=
false
}
if
doInc
{
if
toIdx
>=
0
{
to
.
wordBuf
[
toIdx
]
++
}
else
{
toIdx
++
to
.
wordBuf
[
toIdx
]
=
wordBase
}
}
else
if
wordsInt
+
wordsFracTo
==
0
{
*
to
=
zeroBigDecimal
return
nil
}
}
else
{
/* TODO - fix this code as it won't work for CEILING mode */
pos
:=
wordsFracTo
*
digitsPerWord
-
frac
-
1
shiftedNumber
:=
to
.
wordBuf
[
toIdx
]
/
powers10
[
pos
]
digAfterScale
:=
shiftedNumber
%
10
if
digAfterScale
>
roundDigit
||
(
roundDigit
==
5
&&
digAfterScale
==
5
)
{
shiftedNumber
+=
10
}
to
.
wordBuf
[
toIdx
]
=
powers10
[
pos
]
*
(
shiftedNumber
-
digAfterScale
)
}
/*
In case we're rounding e.g. 1.5e9 to 2.0e9, the decimal words inside
the buffer are as follows.
Before <1, 5e8>
After <2, 5e8>
Hence we need to set the 2nd field to 0.
The same holds if we round 1.5e-9 to 2e-9.
*/
if
wordsFracTo
<
wordsFrac
{
idx
:=
wordsInt
+
wordsFracTo
if
frac
==
0
&&
wordsInt
==
0
{
idx
=
1
}
for
idx
<
wordBufLen
{
to
.
wordBuf
[
idx
]
=
0
idx
++
}
}
// Handle carry.
var
carry
int32
if
to
.
wordBuf
[
toIdx
]
>=
wordBase
{
carry
=
1
to
.
wordBuf
[
toIdx
]
-=
wordBase
for
carry
==
1
&&
toIdx
>
0
{
toIdx
--
to
.
wordBuf
[
toIdx
],
carry
=
add
(
to
.
wordBuf
[
toIdx
],
0
,
carry
)
}
if
carry
>
0
{
if
wordsInt
+
wordsFracTo
>=
wordBufLen
{
wordsFracTo
--
frac
=
wordsFracTo
*
digitsPerWord
err
=
ErrTruncated
}
for
toIdx
=
wordsInt
+
myMax
(
wordsFracTo
,
0
);
toIdx
>
0
;
toIdx
--
{
if
toIdx
<
wordBufLen
{
to
.
wordBuf
[
toIdx
]
=
to
.
wordBuf
[
toIdx
-
1
]
}
else
{
err
=
ErrOverflow
}
}
to
.
wordBuf
[
toIdx
]
=
1
/* We cannot have more than 9 * 9 = 81 digits. */
if
int
(
to
.
digitsInt
)
<
digitsPerWord
*
wordBufLen
{
to
.
digitsInt
++
}
else
{
err
=
ErrOverflow
}
}
}
else
{
for
{
if
to
.
wordBuf
[
toIdx
]
!=
0
{
break
}
if
toIdx
==
0
{
/* making 'zero' with the proper scale */
idx
:=
wordsFracTo
+
1
to
.
digitsInt
=
1
to
.
digitsFrac
=
int8
(
myMax
(
frac
,
0
))
to
.
negative
=
false
for
toIdx
<
idx
{
to
.
wordBuf
[
toIdx
]
=
0
toIdx
++
}
to
.
resultFrac
=
to
.
digitsFrac
return
nil
}
toIdx
--
}
}
/* Here we check 999.9 -> 1000 case when we need to increase intDigCnt */
firstDig
:=
mod9
[
to
.
digitsInt
]
if
firstDig
>
0
&&
to
.
wordBuf
[
toIdx
]
>=
powers10
[
firstDig
]
{
to
.
digitsInt
++
}
if
frac
<
0
{
frac
=
0
}
to
.
digitsFrac
=
int8
(
frac
)
to
.
resultFrac
=
to
.
digitsFrac
return
}
// FromInt sets the decimal value from int64.
func
(
d
*
BigDecimal
)
FromInt
(
val
int64
)
*
BigDecimal
{
var
uVal
uint64
if
val
<
0
{
d
.
negative
=
true
uVal
=
uint64
(
-
val
)
}
else
{
uVal
=
uint64
(
val
)
}
return
d
.
FromUint
(
uVal
)
}
// FromUint sets the decimal value from uint64.
func
(
d
*
BigDecimal
)
FromUint
(
val
uint64
)
*
BigDecimal
{
x
:=
val
wordIdx
:=
1
for
x
>=
wordBase
{
wordIdx
++
x
/=
wordBase
}
d
.
digitsFrac
=
0
d
.
digitsInt
=
int8
(
wordIdx
*
digitsPerWord
)
x
=
val
for
wordIdx
>
0
{
wordIdx
--
y
:=
x
/
wordBase
d
.
wordBuf
[
wordIdx
]
=
int32
(
x
-
y
*
wordBase
)
x
=
y
}
return
d
}
// ToInt returns int part of the decimal, returns the result and errcode.
func
(
d
*
BigDecimal
)
ToInt
()
(
int64
,
error
)
{
var
x
int64
wordIdx
:=
0
for
i
:=
d
.
digitsInt
;
i
>
0
;
i
-=
digitsPerWord
{
y
:=
x
/*
Attention: trick!
we're calculating -|from| instead of |from| here
because |LONGLONG_MIN| > LONGLONG_MAX
so we can convert -9223372036854775808 correctly
*/
x
=
x
*
wordBase
-
int64
(
d
.
wordBuf
[
wordIdx
])
wordIdx
++
if
y
<
math
.
MinInt64
/
wordBase
||
x
>
y
{
/*
the decimal is bigger than any possible integer
return border integer depending on the sign
*/
if
d
.
negative
{
return
math
.
MinInt64
,
ErrOverflow
}
return
math
.
MaxInt64
,
ErrOverflow
}
}
/* boundary case: 9223372036854775808 */
if
!
d
.
negative
&&
x
==
math
.
MinInt64
{
return
math
.
MaxInt64
,
ErrOverflow
}
if
!
d
.
negative
{
x
=
-
x
}
for
i
:=
d
.
digitsFrac
;
i
>
0
;
i
-=
digitsPerWord
{
if
d
.
wordBuf
[
wordIdx
]
!=
0
{
return
x
,
ErrTruncated
}
wordIdx
++
}
return
x
,
nil
}
// ToUint returns int part of the decimal, returns the result and errcode.
func
(
d
*
BigDecimal
)
ToUint
()
(
uint64
,
error
)
{
if
d
.
negative
{
return
0
,
ErrOverflow
}
var
x
uint64
wordIdx
:=
0
for
i
:=
d
.
digitsInt
;
i
>
0
;
i
-=
digitsPerWord
{
y
:=
x
x
=
x
*
wordBase
+
uint64
(
d
.
wordBuf
[
wordIdx
])
wordIdx
++
if
y
>
math
.
MaxUint64
/
wordBase
||
x
<
y
{
return
math
.
MaxUint64
,
ErrOverflow
}
}
for
i
:=
d
.
digitsFrac
;
i
>
0
;
i
-=
digitsPerWord
{
if
d
.
wordBuf
[
wordIdx
]
!=
0
{
return
x
,
ErrTruncated
}
wordIdx
++
}
return
x
,
nil
}
// FromFloat64 creates a decimal from float64 value.
func
(
d
*
BigDecimal
)
FromFloat64
(
f
float64
)
error
{
s
:=
strconv
.
FormatFloat
(
f
,
'g'
,
-
1
,
64
)
return
d
.
FromString
([]
byte
(
s
))
}
// ToFloat64 converts decimal to float64 value.
func
(
d
*
BigDecimal
)
ToFloat64
()
(
float64
,
error
)
{
f
,
err
:=
strconv
.
ParseFloat
(
d
.
String
(),
64
)
if
err
!=
nil
{
err
=
ErrOverflow
}
return
f
,
err
}
/*
ToBin converts decimal to its binary fixed-length representation
two representations of the same length can be compared with memcmp
with the correct -1/0/+1 result
PARAMS
precision/frac - if precision is 0, internal value of the decimal will be used,
then the encoded value is not memory comparable.
NOTE
the buffer is assumed to be of the size decimalBinSize(precision, frac)
RETURN VALUE
bin - binary value
errCode - eDecOK/eDecTruncate/eDecOverflow
DESCRIPTION
for storage decimal numbers are converted to the "binary" format.
This format has the following properties:
1. length of the binary representation depends on the {precision, frac}
as provided by the caller and NOT on the digitsInt/digitsFrac of the decimal to
convert.
2. binary representations of the same {precision, frac} can be compared
with memcmp - with the same result as DecimalCompare() of the original
decimals (not taking into account possible precision loss during
conversion).
This binary format is as follows:
1. First the number is converted to have a requested precision and frac.
2. Every full digitsPerWord digits of digitsInt part are stored in 4 bytes
as is
3. The first digitsInt % digitesPerWord digits are stored in the reduced
number of bytes (enough bytes to store this number of digits -
see dig2bytes)
4. same for frac - full word are stored as is,
the last frac % digitsPerWord digits - in the reduced number of bytes.
5. If the number is negative - every byte is inversed.
5. The very first bit of the resulting byte array is inverted (because
memcmp compares unsigned bytes, see property 2 above)
Example:
1234567890.1234
internally is represented as 3 words
1 234567890 123400000
(assuming we want a binary representation with precision=14, frac=4)
in hex it's
00-00-00-01 0D-FB-38-D2 07-5A-EF-40
now, middle word is full - it stores 9 decimal digits. It goes
into binary representation as is:
........... 0D-FB-38-D2 ............
First word has only one decimal digit. We can store one digit in
one byte, no need to waste four:
01 0D-FB-38-D2 ............
now, last word. It's 123400000. We can store 1234 in two bytes:
01 0D-FB-38-D2 04-D2
So, we've packed 12 bytes number in 7 bytes.
And now we invert the highest bit to get the final result:
81 0D FB 38 D2 04 D2
And for -1234567890.1234 it would be
7E F2 04 C7 2D FB 2D
*/
func
(
d
*
BigDecimal
)
ToBin
(
precision
,
frac
int
)
([]
byte
,
error
)
{
if
precision
>
digitsPerWord
*
maxWordBufLen
||
precision
<
0
||
frac
>
maxDecimalScale
||
frac
<
0
{
return
nil
,
ErrBadNumber
}
var
err
error
var
mask
int32
if
d
.
negative
{
mask
=
-
1
}
digitsInt
:=
precision
-
frac
wordsInt
:=
digitsInt
/
digitsPerWord
leadingDigits
:=
digitsInt
-
wordsInt
*
digitsPerWord
wordsFrac
:=
frac
/
digitsPerWord
trailingDigits
:=
frac
-
wordsFrac
*
digitsPerWord
wordsFracFrom
:=
int
(
d
.
digitsFrac
)
/
digitsPerWord
trailingDigitsFrom
:=
int
(
d
.
digitsFrac
)
-
wordsFracFrom
*
digitsPerWord
intSize
:=
wordsInt
*
wordSize
+
dig2bytes
[
leadingDigits
]
fracSize
:=
wordsFrac
*
wordSize
+
dig2bytes
[
trailingDigits
]
fracSizeFrom
:=
wordsFracFrom
*
wordSize
+
dig2bytes
[
trailingDigitsFrom
]
originIntSize
:=
intSize
originFracSize
:=
fracSize
bin
:=
make
([]
byte
,
intSize
+
fracSize
)
binIdx
:=
0
wordIdxFrom
,
digitsIntFrom
:=
d
.
removeLeadingZeros
()
if
digitsIntFrom
+
fracSizeFrom
==
0
{
mask
=
0
digitsInt
=
1
}
wordsIntFrom
:=
digitsIntFrom
/
digitsPerWord
leadingDigitsFrom
:=
digitsIntFrom
-
wordsIntFrom
*
digitsPerWord
iSizeFrom
:=
wordsIntFrom
*
wordSize
+
dig2bytes
[
leadingDigitsFrom
]
if
digitsInt
<
digitsIntFrom
{
wordIdxFrom
+=
wordsIntFrom
-
wordsInt
if
leadingDigitsFrom
>
0
{
wordIdxFrom
++
}
if
leadingDigits
>
0
{
wordIdxFrom
--
}
wordsIntFrom
=
wordsInt
leadingDigitsFrom
=
leadingDigits
err
=
ErrOverflow
}
else
if
intSize
>
iSizeFrom
{
for
intSize
>
iSizeFrom
{
intSize
--
bin
[
binIdx
]
=
byte
(
mask
)
binIdx
++
}
}
if
fracSize
<
fracSizeFrom
{
wordsFracFrom
=
wordsFrac
trailingDigitsFrom
=
trailingDigits
err
=
ErrTruncated
}
else
if
fracSize
>
fracSizeFrom
&&
trailingDigitsFrom
>
0
{
if
wordsFrac
==
wordsFracFrom
{
trailingDigitsFrom
=
trailingDigits
fracSize
=
fracSizeFrom
}
else
{
wordsFracFrom
++
trailingDigitsFrom
=
0
}
}
// xIntFrom part
if
leadingDigitsFrom
>
0
{
i
:=
dig2bytes
[
leadingDigitsFrom
]
x
:=
(
d
.
wordBuf
[
wordIdxFrom
]
%
powers10
[
leadingDigitsFrom
])
^
mask
wordIdxFrom
++
writeWord
(
bin
[
binIdx
:
],
x
,
i
)
binIdx
+=
i
}
// wordsInt + wordsFrac part.
for
stop
:=
wordIdxFrom
+
wordsIntFrom
+
wordsFracFrom
;
wordIdxFrom
<
stop
;
binIdx
+=
wordSize
{
x
:=
d
.
wordBuf
[
wordIdxFrom
]
^
mask
wordIdxFrom
++
writeWord
(
bin
[
binIdx
:
],
x
,
4
)
}
// xFracFrom part
if
trailingDigitsFrom
>
0
{
var
x
int32
i
:=
dig2bytes
[
trailingDigitsFrom
]
lim
:=
trailingDigits
if
wordsFracFrom
<
wordsFrac
{
lim
=
digitsPerWord
}
for
trailingDigitsFrom
<
lim
&&
dig2bytes
[
trailingDigitsFrom
]
==
i
{
trailingDigitsFrom
++
}
x
=
(
d
.
wordBuf
[
wordIdxFrom
]
/
powers10
[
digitsPerWord
-
trailingDigitsFrom
])
^
mask
writeWord
(
bin
[
binIdx
:
],
x
,
i
)
binIdx
+=
i
}
if
fracSize
>
fracSizeFrom
{
binIdxEnd
:=
originIntSize
+
originFracSize
for
fracSize
>
fracSizeFrom
&&
binIdx
<
binIdxEnd
{
fracSize
--
bin
[
binIdx
]
=
byte
(
mask
)
binIdx
++
}
}
bin
[
0
]
^=
0x80
return
bin
,
err
}
// ToHashKey removes the leading and trailing zeros and generates a hash key.
// Two Decimals dec0 and dec1 with different fraction will generate the same hash keys if dec0.Compare(dec1) == 0.
func
(
d
*
BigDecimal
)
ToHashKey
()
([]
byte
,
error
)
{
_
,
digitsInt
:=
d
.
removeLeadingZeros
()
_
,
digitsFrac
:=
d
.
removeTrailingZeros
()
prec
:=
digitsInt
+
digitsFrac
if
prec
==
0
{
// zeroDecimal
prec
=
1
}
buf
,
err
:=
d
.
ToBin
(
prec
,
digitsFrac
)
if
err
==
ErrTruncated
{
// This err is caused by shorter digitsFrac;
// After removing the trailing zeros from a Decimal,
// so digitsFrac may be less than the real digitsFrac of the Decimal,
// thus ErrTruncated may be raised, we can ignore it here.
err
=
nil
}
return
buf
,
err
}
// PrecisionAndFrac returns the internal precision and frac number.
func
(
d
*
BigDecimal
)
PrecisionAndFrac
()
(
precision
,
frac
int
)
{
frac
=
int
(
d
.
digitsFrac
)
_
,
digitsInt
:=
d
.
removeLeadingZeros
()
precision
=
digitsInt
+
frac
if
precision
==
0
{
precision
=
1
}
return
}
// IsZero checks whether it's a zero decimal.
func
(
d
*
BigDecimal
)
IsZero
()
bool
{
isZero
:=
true
for
_
,
val
:=
range
d
.
wordBuf
{
if
val
!=
0
{
isZero
=
false
break
}
}
return
isZero
}
// FromBin Restores decimal from its binary fixed-length representation.
func
(
d
*
BigDecimal
)
FromBin
(
bin
[]
byte
,
precision
,
frac
int
)
(
binSize
int
,
err
error
)
{
if
len
(
bin
)
==
0
{
*
d
=
zeroBigDecimal
return
0
,
ErrBadNumber
}
digitsInt
:=
precision
-
frac
wordsInt
:=
digitsInt
/
digitsPerWord
leadingDigits
:=
digitsInt
-
wordsInt
*
digitsPerWord
wordsFrac
:=
frac
/
digitsPerWord
trailingDigits
:=
frac
-
wordsFrac
*
digitsPerWord
wordsIntTo
:=
wordsInt
if
leadingDigits
>
0
{
wordsIntTo
++
}
wordsFracTo
:=
wordsFrac
if
trailingDigits
>
0
{
wordsFracTo
++
}
binIdx
:=
0
mask
:=
int32
(
-
1
)
if
bin
[
binIdx
]
&
0x80
>
0
{
mask
=
0
}
binSize
=
decimalBinSize
(
precision
,
frac
)
dCopy
:=
make
([]
byte
,
40
)
dCopy
=
dCopy
[
:
binSize
]
copy
(
dCopy
,
bin
)
dCopy
[
0
]
^=
0x80
bin
=
dCopy
oldWordsIntTo
:=
wordsIntTo
wordsIntTo
,
wordsFracTo
,
err
=
fixWordCntError
(
wordsIntTo
,
wordsFracTo
)
if
err
!=
nil
{
if
wordsIntTo
<
oldWordsIntTo
{
binIdx
+=
dig2bytes
[
leadingDigits
]
+
(
wordsInt
-
wordsIntTo
)
*
wordSize
}
else
{
trailingDigits
=
0
wordsFrac
=
wordsFracTo
}
}
d
.
negative
=
mask
!=
0
d
.
digitsInt
=
int8
(
wordsInt
*
digitsPerWord
+
leadingDigits
)
d
.
digitsFrac
=
int8
(
wordsFrac
*
digitsPerWord
+
trailingDigits
)
wordIdx
:=
0
if
leadingDigits
>
0
{
i
:=
dig2bytes
[
leadingDigits
]
x
:=
readWord
(
bin
[
binIdx
:
],
i
)
binIdx
+=
i
d
.
wordBuf
[
wordIdx
]
=
x
^
mask
if
uint64
(
d
.
wordBuf
[
wordIdx
])
>=
uint64
(
powers10
[
leadingDigits
+
1
])
{
*
d
=
zeroBigDecimal
return
binSize
,
ErrBadNumber
}
if
wordIdx
>
0
||
d
.
wordBuf
[
wordIdx
]
!=
0
{
wordIdx
++
}
else
{
d
.
digitsInt
-=
int8
(
leadingDigits
)
}
}
for
stop
:=
binIdx
+
wordsInt
*
wordSize
;
binIdx
<
stop
;
binIdx
+=
wordSize
{
d
.
wordBuf
[
wordIdx
]
=
readWord
(
bin
[
binIdx
:
],
4
)
^
mask
if
uint32
(
d
.
wordBuf
[
wordIdx
])
>
wordMax
{
*
d
=
zeroBigDecimal
return
binSize
,
ErrBadNumber
}
if
wordIdx
>
0
||
d
.
wordBuf
[
wordIdx
]
!=
0
{
wordIdx
++
}
else
{
d
.
digitsInt
-=
digitsPerWord
}
}
for
stop
:=
binIdx
+
wordsFrac
*
wordSize
;
binIdx
<
stop
;
binIdx
+=
wordSize
{
d
.
wordBuf
[
wordIdx
]
=
readWord
(
bin
[
binIdx
:
],
4
)
^
mask
if
uint32
(
d
.
wordBuf
[
wordIdx
])
>
wordMax
{
*
d
=
zeroBigDecimal
return
binSize
,
ErrBadNumber
}
wordIdx
++
}
if
trailingDigits
>
0
{
i
:=
dig2bytes
[
trailingDigits
]
x
:=
readWord
(
bin
[
binIdx
:
],
i
)
d
.
wordBuf
[
wordIdx
]
=
(
x
^
mask
)
*
powers10
[
digitsPerWord
-
trailingDigits
]
if
uint32
(
d
.
wordBuf
[
wordIdx
])
>
wordMax
{
*
d
=
zeroBigDecimal
return
binSize
,
ErrBadNumber
}
}
if
d
.
digitsInt
==
0
&&
d
.
digitsFrac
==
0
{
*
d
=
zeroBigDecimal
}
d
.
resultFrac
=
int8
(
frac
)
return
binSize
,
err
}
// decimalBinSize returns the size of array to hold a binary representation of a decimal.
func
decimalBinSize
(
precision
,
frac
int
)
int
{
digitsInt
:=
precision
-
frac
wordsInt
:=
digitsInt
/
digitsPerWord
wordsFrac
:=
frac
/
digitsPerWord
xInt
:=
digitsInt
-
wordsInt
*
digitsPerWord
xFrac
:=
frac
-
wordsFrac
*
digitsPerWord
return
wordsInt
*
wordSize
+
dig2bytes
[
xInt
]
+
wordsFrac
*
wordSize
+
dig2bytes
[
xFrac
]
}
func
readWord
(
b
[]
byte
,
size
int
)
int32
{
var
x
int32
switch
size
{
case
1
:
x
=
int32
(
int8
(
b
[
0
]))
case
2
:
x
=
int32
(
int8
(
b
[
0
]))
<<
8
+
int32
(
b
[
1
])
case
3
:
if
b
[
0
]
&
128
>
0
{
x
=
int32
(
uint32
(
255
)
<<
24
|
uint32
(
b
[
0
])
<<
16
|
uint32
(
b
[
1
])
<<
8
|
uint32
(
b
[
2
]))
}
else
{
x
=
int32
(
uint32
(
b
[
0
])
<<
16
|
uint32
(
b
[
1
])
<<
8
|
uint32
(
b
[
2
]))
}
case
4
:
x
=
int32
(
b
[
3
])
+
int32
(
b
[
2
])
<<
8
+
int32
(
b
[
1
])
<<
16
+
int32
(
int8
(
b
[
0
]))
<<
24
}
return
x
}
func
writeWord
(
b
[]
byte
,
word
int32
,
size
int
)
{
v
:=
uint32
(
word
)
switch
size
{
case
1
:
b
[
0
]
=
byte
(
word
)
case
2
:
b
[
0
]
=
byte
(
v
>>
8
)
b
[
1
]
=
byte
(
v
)
case
3
:
b
[
0
]
=
byte
(
v
>>
16
)
b
[
1
]
=
byte
(
v
>>
8
)
b
[
2
]
=
byte
(
v
)
case
4
:
b
[
0
]
=
byte
(
v
>>
24
)
b
[
1
]
=
byte
(
v
>>
16
)
b
[
2
]
=
byte
(
v
>>
8
)
b
[
3
]
=
byte
(
v
)
}
}
// Compare compares one decimal to another, returns -1/0/1.
func
(
d
*
BigDecimal
)
Compare
(
to
*
BigDecimal
)
int
{
if
d
.
negative
==
to
.
negative
{
cmp
,
_
:=
doSub
(
d
,
to
,
nil
)
//todo terror.Log(errors.Trace(err))
return
cmp
}
if
d
.
negative
{
return
-
1
}
return
1
}
// DecimalNeg reverses decimal's sign.
func
DecimalNeg
(
from
*
BigDecimal
)
*
BigDecimal
{
to
:=
*
from
if
from
.
IsZero
()
{
return
&
to
}
to
.
negative
=
!
from
.
negative
return
&
to
}
// DecimalAdd adds two decimals, sets the result to 'to'.
// Note: DO NOT use `from1` or `from2` as `to` since the metadata
// of `to` may be changed during evaluating.
func
DecimalAdd
(
from1
,
from2
,
to
*
BigDecimal
)
error
{
to
.
resultFrac
=
myMaxInt8
(
from1
.
resultFrac
,
from2
.
resultFrac
)
if
from1
.
negative
==
from2
.
negative
{
return
doAdd
(
from1
,
from2
,
to
)
}
_
,
err
:=
doSub
(
from1
,
from2
,
to
)
return
err
}
// DecimalSub subs one decimal from another, sets the result to 'to'.
func
DecimalSub
(
from1
,
from2
,
to
*
BigDecimal
)
error
{
to
.
resultFrac
=
myMaxInt8
(
from1
.
resultFrac
,
from2
.
resultFrac
)
if
from1
.
negative
==
from2
.
negative
{
_
,
err
:=
doSub
(
from1
,
from2
,
to
)
return
err
}
return
doAdd
(
from1
,
from2
,
to
)
}
func
doSub
(
from1
,
from2
,
to
*
BigDecimal
)
(
cmp
int
,
err
error
)
{
var
(
wordsInt1
=
digitsToWords
(
int
(
from1
.
digitsInt
))
wordsFrac1
=
digitsToWords
(
int
(
from1
.
digitsFrac
))
wordsInt2
=
digitsToWords
(
int
(
from2
.
digitsInt
))
wordsFrac2
=
digitsToWords
(
int
(
from2
.
digitsFrac
))
wordsFracTo
=
myMax
(
wordsFrac1
,
wordsFrac2
)
start1
=
0
stop1
=
wordsInt1
idx1
=
0
start2
=
0
stop2
=
wordsInt2
idx2
=
0
)
if
from1
.
wordBuf
[
idx1
]
==
0
{
for
idx1
<
stop1
&&
from1
.
wordBuf
[
idx1
]
==
0
{
idx1
++
}
start1
=
idx1
wordsInt1
=
stop1
-
idx1
}
if
from2
.
wordBuf
[
idx2
]
==
0
{
for
idx2
<
stop2
&&
from2
.
wordBuf
[
idx2
]
==
0
{
idx2
++
}
start2
=
idx2
wordsInt2
=
stop2
-
idx2
}
var
carry
int32
if
wordsInt2
>
wordsInt1
{
carry
=
1
}
else
if
wordsInt2
==
wordsInt1
{
end1
:=
stop1
+
wordsFrac1
-
1
end2
:=
stop2
+
wordsFrac2
-
1
for
idx1
<=
end1
&&
from1
.
wordBuf
[
end1
]
==
0
{
end1
--
}
for
idx2
<=
end2
&&
from2
.
wordBuf
[
end2
]
==
0
{
end2
--
}
wordsFrac1
=
end1
-
stop1
+
1
wordsFrac2
=
end2
-
stop2
+
1
for
idx1
<=
end1
&&
idx2
<=
end2
&&
from1
.
wordBuf
[
idx1
]
==
from2
.
wordBuf
[
idx2
]
{
idx1
++
idx2
++
}
if
idx1
<=
end1
{
if
idx2
<=
end2
&&
from2
.
wordBuf
[
idx2
]
>
from1
.
wordBuf
[
idx1
]
{
carry
=
1
}
else
{
carry
=
0
}
}
else
{
if
idx2
<=
end2
{
carry
=
1
}
else
{
if
to
==
nil
{
return
0
,
nil
}
*
to
=
zeroBigDecimal
return
0
,
nil
}
}
}
if
to
==
nil
{
if
carry
>
0
==
from1
.
negative
{
// from2 is negative too.
return
1
,
nil
}
return
-
1
,
nil
}
to
.
negative
=
from1
.
negative
/* ensure that always idx1 > idx2 (and wordsInt1 >= wordsInt2) */
if
carry
>
0
{
from1
,
from2
=
from2
,
from1
start1
,
start2
=
start2
,
start1
wordsInt1
,
wordsInt2
=
wordsInt2
,
wordsInt1
wordsFrac1
,
wordsFrac2
=
wordsFrac2
,
wordsFrac1
to
.
negative
=
!
to
.
negative
}
wordsInt1
,
wordsFracTo
,
err
=
fixWordCntError
(
wordsInt1
,
wordsFracTo
)
idxTo
:=
wordsInt1
+
wordsFracTo
to
.
digitsFrac
=
from1
.
digitsFrac
if
to
.
digitsFrac
<
from2
.
digitsFrac
{
to
.
digitsFrac
=
from2
.
digitsFrac
}
to
.
digitsInt
=
int8
(
wordsInt1
*
digitsPerWord
)
if
err
!=
nil
{
if
to
.
digitsFrac
>
int8
(
wordsFracTo
*
digitsPerWord
)
{
to
.
digitsFrac
=
int8
(
wordsFracTo
*
digitsPerWord
)
}
if
wordsFrac1
>
wordsFracTo
{
wordsFrac1
=
wordsFracTo
}
if
wordsFrac2
>
wordsFracTo
{
wordsFrac2
=
wordsFracTo
}
if
wordsInt2
>
wordsInt1
{
wordsInt2
=
wordsInt1
}
}
carry
=
0
/* part 1 - max(frac) ... min (frac) */
if
wordsFrac1
>
wordsFrac2
{
idx1
=
start1
+
wordsInt1
+
wordsFrac1
stop1
=
start1
+
wordsInt1
+
wordsFrac2
idx2
=
start2
+
wordsInt2
+
wordsFrac2
for
wordsFracTo
>
wordsFrac1
{
wordsFracTo
--
idxTo
--
to
.
wordBuf
[
idxTo
]
=
0
}
for
idx1
>
stop1
{
idxTo
--
idx1
--
to
.
wordBuf
[
idxTo
]
=
from1
.
wordBuf
[
idx1
]
}
}
else
{
idx1
=
start1
+
wordsInt1
+
wordsFrac1
idx2
=
start2
+
wordsInt2
+
wordsFrac2
stop2
=
start2
+
wordsInt2
+
wordsFrac1
for
wordsFracTo
>
wordsFrac2
{
wordsFracTo
--
idxTo
--
to
.
wordBuf
[
idxTo
]
=
0
}
for
idx2
>
stop2
{
idxTo
--
idx2
--
to
.
wordBuf
[
idxTo
],
carry
=
sub
(
0
,
from2
.
wordBuf
[
idx2
],
carry
)
}
}
/* part 2 - min(frac) ... wordsInt2 */
for
idx2
>
start2
{
idxTo
--
idx1
--
idx2
--
to
.
wordBuf
[
idxTo
],
carry
=
sub
(
from1
.
wordBuf
[
idx1
],
from2
.
wordBuf
[
idx2
],
carry
)
}
/* part 3 - wordsInt2 ... wordsInt1 */
for
carry
>
0
&&
idx1
>
start1
{
idxTo
--
idx1
--
to
.
wordBuf
[
idxTo
],
carry
=
sub
(
from1
.
wordBuf
[
idx1
],
0
,
carry
)
}
for
idx1
>
start1
{
idxTo
--
idx1
--
to
.
wordBuf
[
idxTo
]
=
from1
.
wordBuf
[
idx1
]
}
for
idxTo
>
0
{
idxTo
--
to
.
wordBuf
[
idxTo
]
=
0
}
return
0
,
err
}
func
doAdd
(
from1
,
from2
,
to
*
BigDecimal
)
error
{
var
(
err
error
wordsInt1
=
digitsToWords
(
int
(
from1
.
digitsInt
))
wordsFrac1
=
digitsToWords
(
int
(
from1
.
digitsFrac
))
wordsInt2
=
digitsToWords
(
int
(
from2
.
digitsInt
))
wordsFrac2
=
digitsToWords
(
int
(
from2
.
digitsFrac
))
wordsIntTo
=
myMax
(
wordsInt1
,
wordsInt2
)
wordsFracTo
=
myMax
(
wordsFrac1
,
wordsFrac2
)
)
var
x
int32
if
wordsInt1
>
wordsInt2
{
x
=
from1
.
wordBuf
[
0
]
}
else
if
wordsInt2
>
wordsInt1
{
x
=
from2
.
wordBuf
[
0
]
}
else
{
x
=
from1
.
wordBuf
[
0
]
+
from2
.
wordBuf
[
0
]
}
if
x
>
wordMax
-
1
{
/* yes, there is */
wordsIntTo
++
to
.
wordBuf
[
0
]
=
0
/* safety */
}
wordsIntTo
,
wordsFracTo
,
err
=
fixWordCntError
(
wordsIntTo
,
wordsFracTo
)
if
err
==
ErrOverflow
{
maxDecimal
(
wordBufLen
*
digitsPerWord
,
0
,
to
)
return
err
}
idxTo
:=
wordsIntTo
+
wordsFracTo
to
.
negative
=
from1
.
negative
to
.
digitsInt
=
int8
(
wordsIntTo
*
digitsPerWord
)
to
.
digitsFrac
=
myMaxInt8
(
from1
.
digitsFrac
,
from2
.
digitsFrac
)
if
err
!=
nil
{
if
to
.
digitsFrac
>
int8
(
wordsFracTo
*
digitsPerWord
)
{
to
.
digitsFrac
=
int8
(
wordsFracTo
*
digitsPerWord
)
}
if
wordsFrac1
>
wordsFracTo
{
wordsFrac1
=
wordsFracTo
}
if
wordsFrac2
>
wordsFracTo
{
wordsFrac2
=
wordsFracTo
}
if
wordsInt1
>
wordsIntTo
{
wordsInt1
=
wordsIntTo
}
if
wordsInt2
>
wordsIntTo
{
wordsInt2
=
wordsIntTo
}
}
var
dec1
,
dec2
=
from1
,
from2
var
idx1
,
idx2
,
stop
,
stop2
int
/* part 1 - max(frac) ... min (frac) */
if
wordsFrac1
>
wordsFrac2
{
idx1
=
wordsInt1
+
wordsFrac1
stop
=
wordsInt1
+
wordsFrac2
idx2
=
wordsInt2
+
wordsFrac2
if
wordsInt1
>
wordsInt2
{
stop2
=
wordsInt1
-
wordsInt2
}
}
else
{
idx1
=
wordsInt2
+
wordsFrac2
stop
=
wordsInt2
+
wordsFrac1
idx2
=
wordsInt1
+
wordsFrac1
if
wordsInt2
>
wordsInt1
{
stop2
=
wordsInt2
-
wordsInt1
}
dec1
,
dec2
=
from2
,
from1
}
for
idx1
>
stop
{
idxTo
--
idx1
--
to
.
wordBuf
[
idxTo
]
=
dec1
.
wordBuf
[
idx1
]
}
/* part 2 - min(frac) ... min(digitsInt) */
carry
:=
int32
(
0
)
for
idx1
>
stop2
{
idx1
--
idx2
--
idxTo
--
to
.
wordBuf
[
idxTo
],
carry
=
add
(
dec1
.
wordBuf
[
idx1
],
dec2
.
wordBuf
[
idx2
],
carry
)
}
/* part 3 - min(digitsInt) ... max(digitsInt) */
stop
=
0
if
wordsInt1
>
wordsInt2
{
idx1
=
wordsInt1
-
wordsInt2
dec1
,
dec2
=
from1
,
from2
}
else
{
idx1
=
wordsInt2
-
wordsInt1
dec1
,
dec2
=
from2
,
from1
}
for
idx1
>
stop
{
idxTo
--
idx1
--
to
.
wordBuf
[
idxTo
],
carry
=
add
(
dec1
.
wordBuf
[
idx1
],
0
,
carry
)
}
if
carry
>
0
{
idxTo
--
to
.
wordBuf
[
idxTo
]
=
1
}
return
err
}
func
maxDecimal
(
precision
,
frac
int
,
to
*
BigDecimal
)
{
digitsInt
:=
precision
-
frac
to
.
negative
=
false
to
.
digitsInt
=
int8
(
digitsInt
)
idx
:=
0
if
digitsInt
>
0
{
firstWordDigits
:=
digitsInt
%
digitsPerWord
if
firstWordDigits
>
0
{
to
.
wordBuf
[
idx
]
=
powers10
[
firstWordDigits
]
-
1
/* get 9 99 999 ... */
idx
++
}
for
digitsInt
/=
digitsPerWord
;
digitsInt
>
0
;
digitsInt
--
{
to
.
wordBuf
[
idx
]
=
wordMax
idx
++
}
}
to
.
digitsFrac
=
int8
(
frac
)
if
frac
>
0
{
lastDigits
:=
frac
%
digitsPerWord
for
frac
/=
digitsPerWord
;
frac
>
0
;
frac
--
{
to
.
wordBuf
[
idx
]
=
wordMax
idx
++
}
if
lastDigits
>
0
{
to
.
wordBuf
[
idx
]
=
fracMax
[
lastDigits
-
1
]
}
}
}
/*
DecimalMul multiplies two decimals.
from1, from2 - factors
to - product
RETURN VALUE
E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW;
NOTES
in this implementation, with wordSize=4 we have digitsPerWord=9,
and 63-digit number will take only 7 words (basically a 7-digit
"base 999999999" number). Thus there's no need in fast multiplication
algorithms, 7-digit numbers can be multiplied with a naive O(n*n)
method.
XXX if this library is to be used with huge numbers of thousands of
digits, fast multiplication must be implemented.
*/
func
DecimalMul
(
from1
,
from2
,
to
*
BigDecimal
)
error
{
var
(
err
error
wordsInt1
=
digitsToWords
(
int
(
from1
.
digitsInt
))
wordsFrac1
=
digitsToWords
(
int
(
from1
.
digitsFrac
))
wordsInt2
=
digitsToWords
(
int
(
from2
.
digitsInt
))
wordsFrac2
=
digitsToWords
(
int
(
from2
.
digitsFrac
))
wordsIntTo
=
digitsToWords
(
int
(
from1
.
digitsInt
)
+
int
(
from2
.
digitsInt
))
wordsFracTo
=
wordsFrac1
+
wordsFrac2
idx1
=
wordsInt1
idx2
=
wordsInt2
idxTo
int
tmp1
=
wordsIntTo
tmp2
=
wordsFracTo
)
to
.
resultFrac
=
myMinInt8
(
from1
.
resultFrac
+
from2
.
resultFrac
,
maxDecimalScale
)
wordsIntTo
,
wordsFracTo
,
err
=
fixWordCntError
(
wordsIntTo
,
wordsFracTo
)
to
.
negative
=
from1
.
negative
!=
from2
.
negative
to
.
digitsFrac
=
from1
.
digitsFrac
+
from2
.
digitsFrac
if
to
.
digitsFrac
>
notFixedDec
{
to
.
digitsFrac
=
notFixedDec
}
to
.
digitsInt
=
int8
(
wordsIntTo
*
digitsPerWord
)
if
err
==
ErrOverflow
{
return
err
}
if
err
!=
nil
{
if
to
.
digitsFrac
>
int8
(
wordsFracTo
*
digitsPerWord
)
{
to
.
digitsFrac
=
int8
(
wordsFracTo
*
digitsPerWord
)
}
if
to
.
digitsInt
>
int8
(
wordsIntTo
*
digitsPerWord
)
{
to
.
digitsInt
=
int8
(
wordsIntTo
*
digitsPerWord
)
}
if
tmp1
>
wordsIntTo
{
tmp1
-=
wordsIntTo
tmp2
=
tmp1
>>
1
wordsInt2
-=
tmp1
-
tmp2
wordsFrac1
=
0
wordsFrac2
=
0
}
else
{
tmp2
-=
wordsFracTo
tmp1
=
tmp2
>>
1
if
wordsFrac1
<=
wordsFrac2
{
wordsFrac1
-=
tmp1
wordsFrac2
-=
tmp2
-
tmp1
}
else
{
wordsFrac2
-=
tmp1
wordsFrac1
-=
tmp2
-
tmp1
}
}
}
startTo
:=
wordsIntTo
+
wordsFracTo
-
1
start2
:=
idx2
+
wordsFrac2
-
1
stop1
:=
idx1
-
wordsInt1
stop2
:=
idx2
-
wordsInt2
to
.
wordBuf
=
zeroBigDecimal
.
wordBuf
for
idx1
+=
wordsFrac1
-
1
;
idx1
>=
stop1
;
idx1
--
{
carry
:=
int32
(
0
)
idxTo
=
startTo
idx2
=
start2
for
idx2
>=
stop2
{
var
hi
,
lo
int32
p
:=
int64
(
from1
.
wordBuf
[
idx1
])
*
int64
(
from2
.
wordBuf
[
idx2
])
hi
=
int32
(
p
/
wordBase
)
lo
=
int32
(
p
-
int64
(
hi
)
*
wordBase
)
to
.
wordBuf
[
idxTo
],
carry
=
add2
(
to
.
wordBuf
[
idxTo
],
lo
,
carry
)
carry
+=
hi
idx2
--
idxTo
--
}
if
carry
>
0
{
if
idxTo
<
0
{
return
ErrOverflow
}
to
.
wordBuf
[
idxTo
],
carry
=
add2
(
to
.
wordBuf
[
idxTo
],
0
,
carry
)
}
for
idxTo
--
;
carry
>
0
;
idxTo
--
{
if
idxTo
<
0
{
return
ErrOverflow
}
to
.
wordBuf
[
idxTo
],
carry
=
add
(
to
.
wordBuf
[
idxTo
],
0
,
carry
)
}
startTo
--
}
/* Now we have to check for -0.000 case */
if
to
.
negative
{
idx
:=
0
end
:=
wordsIntTo
+
wordsFracTo
for
{
if
to
.
wordBuf
[
idx
]
!=
0
{
break
}
idx
++
/* We got decimal zero */
if
idx
==
end
{
*
to
=
zeroBigDecimal
break
}
}
}
idxTo
=
0
dToMove
:=
wordsIntTo
+
digitsToWords
(
int
(
to
.
digitsFrac
))
for
to
.
wordBuf
[
idxTo
]
==
0
&&
to
.
digitsInt
>
digitsPerWord
{
idxTo
++
to
.
digitsInt
-=
digitsPerWord
dToMove
--
}
if
idxTo
>
0
{
curIdx
:=
0
for
dToMove
>
0
{
to
.
wordBuf
[
curIdx
]
=
to
.
wordBuf
[
idxTo
]
curIdx
++
idxTo
++
dToMove
--
}
}
return
err
}
// DecimalDiv does division of two decimals.
//
// from1 - dividend
// from2 - divisor
// to - quotient
// fracIncr - increment of fraction
func
DecimalDiv
(
from1
,
from2
,
to
*
BigDecimal
,
fracIncr
int
)
error
{
to
.
resultFrac
=
myMinInt8
(
from1
.
resultFrac
+
int8
(
fracIncr
),
maxDecimalScale
)
return
doDivMod
(
from1
,
from2
,
to
,
nil
,
fracIncr
)
}
/*
DecimalMod does modulus of two decimals.
from1 - dividend
from2 - divisor
to - modulus
RETURN VALUE
E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW/E_DEC_DIV_ZERO;
NOTES
see do_div_mod()
DESCRIPTION
the modulus R in R = M mod N
is defined as
0 <= |R| < |M|
sign R == sign M
R = M - k*N, where k is integer
thus, there's no requirement for M or N to be integers
*/
func
DecimalMod
(
from1
,
from2
,
to
*
BigDecimal
)
error
{
to
.
resultFrac
=
myMaxInt8
(
from1
.
resultFrac
,
from2
.
resultFrac
)
return
doDivMod
(
from1
,
from2
,
nil
,
to
,
0
)
}
func
doDivMod
(
from1
,
from2
,
to
,
mod
*
BigDecimal
,
fracIncr
int
)
error
{
var
(
frac1
=
digitsToWords
(
int
(
from1
.
digitsFrac
))
*
digitsPerWord
prec1
=
int
(
from1
.
digitsInt
)
+
frac1
frac2
=
digitsToWords
(
int
(
from2
.
digitsFrac
))
*
digitsPerWord
prec2
=
int
(
from2
.
digitsInt
)
+
frac2
)
if
mod
!=
nil
{
to
=
mod
}
/* removing all the leading zeros */
i
:=
((
prec2
-
1
)
%
digitsPerWord
)
+
1
idx2
:=
0
for
prec2
>
0
&&
from2
.
wordBuf
[
idx2
]
==
0
{
prec2
-=
i
i
=
digitsPerWord
idx2
++
}
if
prec2
<=
0
{
/* short-circuit everything: from2 == 0 */
return
ErrDivByZero
}
prec2
-=
countLeadingZeroes
((
prec2
-
1
)
%
digitsPerWord
,
from2
.
wordBuf
[
idx2
])
i
=
((
prec1
-
1
)
%
digitsPerWord
)
+
1
idx1
:=
0
for
prec1
>
0
&&
from1
.
wordBuf
[
idx1
]
==
0
{
prec1
-=
i
i
=
digitsPerWord
idx1
++
}
if
prec1
<=
0
{
/* short-circuit everything: from1 == 0 */
*
to
=
zeroBigDecimal
return
nil
}
prec1
-=
countLeadingZeroes
((
prec1
-
1
)
%
digitsPerWord
,
from1
.
wordBuf
[
idx1
])
/* let's fix fracIncr, taking into account frac1,frac2 increase */
fracIncr
-=
frac1
-
int
(
from1
.
digitsFrac
)
+
frac2
-
int
(
from2
.
digitsFrac
)
if
fracIncr
<
0
{
fracIncr
=
0
}
digitsIntTo
:=
(
prec1
-
frac1
)
-
(
prec2
-
frac2
)
if
from1
.
wordBuf
[
idx1
]
>=
from2
.
wordBuf
[
idx2
]
{
digitsIntTo
++
}
var
wordsIntTo
int
if
digitsIntTo
<
0
{
digitsIntTo
/=
digitsPerWord
wordsIntTo
=
0
}
else
{
wordsIntTo
=
digitsToWords
(
digitsIntTo
)
}
var
wordsFracTo
int
var
err
error
if
mod
!=
nil
{
// we're calculating N1 % N2.
// The result will have
// digitsFrac=max(frac1, frac2), as for subtraction
// digitsInt=from2.digitsInt
to
.
negative
=
from1
.
negative
to
.
digitsFrac
=
myMaxInt8
(
from1
.
digitsFrac
,
from2
.
digitsFrac
)
}
else
{
wordsFracTo
=
digitsToWords
(
frac1
+
frac2
+
fracIncr
)
wordsIntTo
,
wordsFracTo
,
err
=
fixWordCntError
(
wordsIntTo
,
wordsFracTo
)
to
.
negative
=
from1
.
negative
!=
from2
.
negative
to
.
digitsInt
=
int8
(
wordsIntTo
*
digitsPerWord
)
to
.
digitsFrac
=
int8
(
wordsFracTo
*
digitsPerWord
)
}
idxTo
:=
0
stopTo
:=
wordsIntTo
+
wordsFracTo
if
mod
==
nil
{
for
digitsIntTo
<
0
&&
idxTo
<
wordBufLen
{
to
.
wordBuf
[
idxTo
]
=
0
idxTo
++
digitsIntTo
++
}
}
i
=
digitsToWords
(
prec1
)
len1
:=
i
+
digitsToWords
(
2
*
frac2
+
fracIncr
+
1
)
+
1
if
len1
<
3
{
len1
=
3
}
tmp1
:=
make
([]
int32
,
len1
)
copy
(
tmp1
,
from1
.
wordBuf
[
idx1
:
idx1
+
i
])
start1
:=
0
var
stop1
int
start2
:=
idx2
stop2
:=
idx2
+
digitsToWords
(
prec2
)
-
1
/* removing end zeroes */
for
from2
.
wordBuf
[
stop2
]
==
0
&&
stop2
>=
start2
{
stop2
--
}
len2
:=
stop2
-
start2
stop2
++
/*
calculating norm2 (normalized from2.wordBuf[start2]) - we need from2.wordBuf[start2] to be large
(at least > DIG_BASE/2), but unlike Knuth's Alg. D we don't want to
normalize input numbers (as we don't make a copy of the divisor).
Thus we normalize first dec1 of buf2 only, and we'll normalize tmp1[start1]
on the fly for the purpose of guesstimation only.
It's also faster, as we're saving on normalization of from2.
*/
normFactor
:=
wordBase
/
int64
(
from2
.
wordBuf
[
start2
]
+
1
)
norm2
:=
int32
(
normFactor
*
int64
(
from2
.
wordBuf
[
start2
]))
if
len2
>
0
{
norm2
+=
int32
(
normFactor
*
int64
(
from2
.
wordBuf
[
start2
+
1
])
/
wordBase
)
}
dcarry
:=
int32
(
0
)
if
tmp1
[
start1
]
<
from2
.
wordBuf
[
start2
]
{
dcarry
=
tmp1
[
start1
]
start1
++
}
// main loop
var
guess
int64
for
;
idxTo
<
stopTo
;
idxTo
++
{
/* short-circuit, if possible */
if
dcarry
==
0
&&
tmp1
[
start1
]
<
from2
.
wordBuf
[
start2
]
{
guess
=
0
}
else
{
/* D3: make a guess */
x
:=
int64
(
tmp1
[
start1
])
+
int64
(
dcarry
)
*
wordBase
y
:=
int64
(
tmp1
[
start1
+
1
])
guess
=
(
normFactor
*
x
+
normFactor
*
y
/
wordBase
)
/
int64
(
norm2
)
if
guess
>=
wordBase
{
guess
=
wordBase
-
1
}
if
len2
>
0
{
/* remove normalization */
if
int64
(
from2
.
wordBuf
[
start2
+
1
])
*
guess
>
(
x
-
guess
*
int64
(
from2
.
wordBuf
[
start2
]))
*
wordBase
+
y
{
guess
--
}
if
int64
(
from2
.
wordBuf
[
start2
+
1
])
*
guess
>
(
x
-
guess
*
int64
(
from2
.
wordBuf
[
start2
]))
*
wordBase
+
y
{
guess
--
}
}
/* D4: multiply and subtract */
idx2
=
stop2
idx1
=
start1
+
len2
var
carry
int32
for
carry
=
0
;
idx2
>
start2
;
idx1
--
{
var
hi
,
lo
int32
idx2
--
x
=
guess
*
int64
(
from2
.
wordBuf
[
idx2
])
hi
=
int32
(
x
/
wordBase
)
lo
=
int32
(
x
-
int64
(
hi
)
*
wordBase
)
tmp1
[
idx1
],
carry
=
sub2
(
tmp1
[
idx1
],
lo
,
carry
)
carry
+=
hi
}
if
dcarry
<
carry
{
carry
=
1
}
else
{
carry
=
0
}
/* D5: check the remainder */
if
carry
>
0
{
/* D6: correct the guess */
guess
--
idx2
=
stop2
idx1
=
start1
+
len2
for
carry
=
0
;
idx2
>
start2
;
idx1
--
{
idx2
--
tmp1
[
idx1
],
carry
=
add
(
tmp1
[
idx1
],
from2
.
wordBuf
[
idx2
],
carry
)
}
}
}
if
mod
==
nil
{
to
.
wordBuf
[
idxTo
]
=
int32
(
guess
)
}
dcarry
=
tmp1
[
start1
]
start1
++
}
if
mod
!=
nil
{
/*
now the result is in tmp1, it has
digitsInt=prec1-frac1
digitsFrac=max(frac1, frac2)
*/
if
dcarry
!=
0
{
start1
--
tmp1
[
start1
]
=
dcarry
}
idxTo
=
0
digitsIntTo
=
prec1
-
frac1
-
start1
*
digitsPerWord
if
digitsIntTo
<
0
{
/* If leading zeroes in the fractional part were earlier stripped */
wordsIntTo
=
digitsIntTo
/
digitsPerWord
}
else
{
wordsIntTo
=
digitsToWords
(
digitsIntTo
)
}
wordsFracTo
=
digitsToWords
(
int
(
to
.
digitsFrac
))
err
=
nil
if
wordsIntTo
==
0
&&
wordsFracTo
==
0
{
*
to
=
zeroBigDecimal
return
err
}
if
wordsIntTo
<=
0
{
if
-
wordsIntTo
>=
wordBufLen
{
*
to
=
zeroBigDecimal
return
ErrTruncated
}
stop1
=
start1
+
wordsIntTo
+
wordsFracTo
wordsFracTo
+=
wordsIntTo
to
.
digitsInt
=
0
for
wordsIntTo
<
0
{
to
.
wordBuf
[
idxTo
]
=
0
idxTo
++
wordsIntTo
++
}
}
else
{
if
wordsIntTo
>
wordBufLen
{
to
.
digitsInt
=
int8
(
digitsPerWord
*
wordBufLen
)
to
.
digitsFrac
=
0
return
ErrOverflow
}
stop1
=
start1
+
wordsIntTo
+
wordsFracTo
to
.
digitsInt
=
int8
(
myMin
(
wordsIntTo
*
digitsPerWord
,
int
(
from2
.
digitsInt
)))
}
if
wordsIntTo
+
wordsFracTo
>
wordBufLen
{
stop1
-=
wordsIntTo
+
wordsFracTo
-
wordBufLen
wordsFracTo
=
wordBufLen
-
wordsIntTo
to
.
digitsFrac
=
int8
(
wordsFracTo
*
digitsPerWord
)
err
=
ErrTruncated
}
for
start1
<
stop1
{
to
.
wordBuf
[
idxTo
]
=
tmp1
[
start1
]
idxTo
++
start1
++
}
}
idxTo
,
digitsIntTo
=
to
.
removeLeadingZeros
()
to
.
digitsInt
=
int8
(
digitsIntTo
)
if
idxTo
!=
0
{
copy
(
to
.
wordBuf
[
:
],
to
.
wordBuf
[
idxTo
:
])
}
return
err
}
// DecimalPeak returns the length of the encoded decimal.
func
DecimalPeak
(
b
[]
byte
)
(
int
,
error
)
{
if
len
(
b
)
<
3
{
return
0
,
ErrBadNumber
}
precision
:=
int
(
b
[
0
])
frac
:=
int
(
b
[
1
])
return
decimalBinSize
(
precision
,
frac
)
+
2
,
nil
}
// NewDecFromInt creates a BigDecimal from int.
func
NewDecFromInt
(
i
int64
)
*
BigDecimal
{
return
new
(
BigDecimal
)
.
FromInt
(
i
)
}
// NewDecFromUint creates a BigDecimal from uint.
func
NewDecFromUint
(
i
uint64
)
*
BigDecimal
{
return
new
(
BigDecimal
)
.
FromUint
(
i
)
}
// NewDecFromFloatForTest creates a BigDecimal from float, as it returns no error, it should only be used in test.
func
NewDecFromFloatForTest
(
f
float64
)
*
BigDecimal
{
dec
:=
new
(
BigDecimal
)
_
=
dec
.
FromFloat64
(
f
)
//todo terror.Log(errors.Trace(err))
return
dec
}
// NewDecFromStringForTest creates a BigDecimal from string, as it returns no error, it should only be used in test.
func
NewDecFromStringForTest
(
s
string
)
*
BigDecimal
{
dec
:=
new
(
BigDecimal
)
_
=
dec
.
FromString
([]
byte
(
s
))
//todo terror.Log(errors.Trace(err))
return
dec
}
// NewMaxOrMinDec returns the max or min value decimal for given precision and fraction.
func
NewMaxOrMinDec
(
negative
bool
,
prec
,
frac
int
)
*
BigDecimal
{
str
:=
make
([]
byte
,
prec
+
2
)
for
i
:=
0
;
i
<
len
(
str
);
i
++
{
str
[
i
]
=
'9'
}
if
negative
{
str
[
0
]
=
'-'
}
else
{
str
[
0
]
=
'+'
}
str
[
1
+
prec
-
frac
]
=
'.'
dec
:=
new
(
BigDecimal
)
_
=
dec
.
FromString
(
str
)
//todo terror.Log(errors.Trace(err))
return
dec
}
math/big/decimal_benchmark_test.go
0 → 100644
View file @
8640ee84
// Copyright 2019 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package
hessian
import
"testing"
func
BenchmarkRound
(
b
*
testing
.
B
)
{
b
.
StopTimer
()
var
roundTo
BigDecimal
tests
:=
[]
struct
{
input
string
scale
int
inputDec
BigDecimal
}{
{
input
:
"123456789.987654321"
,
scale
:
1
},
{
input
:
"15.1"
,
scale
:
0
},
{
input
:
"15.5"
,
scale
:
0
},
{
input
:
"15.9"
,
scale
:
0
},
{
input
:
"-15.1"
,
scale
:
0
},
{
input
:
"-15.5"
,
scale
:
0
},
{
input
:
"-15.9"
,
scale
:
0
},
{
input
:
"15.1"
,
scale
:
1
},
{
input
:
"-15.1"
,
scale
:
1
},
{
input
:
"15.17"
,
scale
:
1
},
{
input
:
"15.4"
,
scale
:
-
1
},
{
input
:
"-15.4"
,
scale
:
-
1
},
{
input
:
"5.4"
,
scale
:
-
1
},
{
input
:
".999"
,
scale
:
0
},
{
input
:
"999999999"
,
scale
:
-
9
},
}
for
i
:=
0
;
i
<
len
(
tests
);
i
++
{
tests
[
i
]
.
inputDec
.
FromString
([]
byte
(
tests
[
i
]
.
input
))
}
b
.
StartTimer
()
for
n
:=
0
;
n
<
b
.
N
;
n
++
{
for
i
:=
0
;
i
<
len
(
tests
);
i
++
{
tests
[
i
]
.
inputDec
.
Round
(
&
roundTo
,
tests
[
i
]
.
scale
,
ModeHalfEven
)
}
for
i
:=
0
;
i
<
len
(
tests
);
i
++
{
tests
[
i
]
.
inputDec
.
Round
(
&
roundTo
,
tests
[
i
]
.
scale
,
ModeTruncate
)
}
for
i
:=
0
;
i
<
len
(
tests
);
i
++
{
tests
[
i
]
.
inputDec
.
Round
(
&
roundTo
,
tests
[
i
]
.
scale
,
modeCeiling
)
}
}
}
math/big/decimal_test.go
0 → 100644
View file @
8640ee84
// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package
hessian
import
(
"github.com/stretchr/testify/assert"
"strings"
"testing"
)
func
TestFromInt
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
int64
output
string
}{
{
-
12345
,
"-12345"
},
{
-
1
,
"-1"
},
{
1
,
"1"
},
{
-
9223372036854775807
,
"-9223372036854775807"
},
{
-
9223372036854775808
,
"-9223372036854775808"
},
}
for
_
,
tt
:=
range
tests
{
dec
:=
NewDecFromInt
(
tt
.
input
)
str
:=
dec
.
ToString
()
assert
.
Equal
(
t
,
string
(
str
),
tt
.
output
)
}
}
func
TestFromUint
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
uint64
output
string
}{
{
12345
,
"12345"
},
{
0
,
"0"
},
{
18446744073709551615
,
"18446744073709551615"
},
}
for
_
,
tt
:=
range
tests
{
var
dec
BigDecimal
dec
.
FromUint
(
tt
.
input
)
str
:=
dec
.
ToString
()
assert
.
Equal
(
t
,
string
(
str
),
tt
.
output
)
}
}
func
TestToInt
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
string
output
int64
err
error
}{
{
"18446744073709551615"
,
9223372036854775807
,
ErrOverflow
},
{
"-1"
,
-
1
,
nil
},
{
"1"
,
1
,
nil
},
{
"-1.23"
,
-
1
,
ErrTruncated
},
{
"-9223372036854775807"
,
-
9223372036854775807
,
nil
},
{
"-9223372036854775808"
,
-
9223372036854775808
,
nil
},
{
"9223372036854775808"
,
9223372036854775807
,
ErrOverflow
},
{
"-9223372036854775809"
,
-
9223372036854775808
,
ErrOverflow
},
}
for
_
,
tt
:=
range
tests
{
var
dec
BigDecimal
dec
.
FromString
([]
byte
(
tt
.
input
))
result
,
ec
:=
dec
.
ToInt
()
assert
.
Equal
(
t
,
ec
,
tt
.
err
)
assert
.
Equal
(
t
,
result
,
tt
.
output
)
}
}
func
TestToUint
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
string
output
uint64
err
error
}{
{
"12345"
,
12345
,
nil
},
{
"0"
,
0
,
nil
},
/* ULLONG_MAX = 18446744073709551615ULL */
{
"18446744073709551615"
,
18446744073709551615
,
nil
},
{
"18446744073709551616"
,
18446744073709551615
,
ErrOverflow
},
{
"-1"
,
0
,
ErrOverflow
},
{
"1.23"
,
1
,
ErrTruncated
},
{
"9999999999999999999999999.000"
,
18446744073709551615
,
ErrOverflow
},
}
for
_
,
tt
:=
range
tests
{
var
dec
BigDecimal
dec
.
FromString
([]
byte
(
tt
.
input
))
result
,
ec
:=
dec
.
ToUint
()
assert
.
Equal
(
t
,
ec
,
tt
.
err
)
assert
.
Equal
(
t
,
result
,
tt
.
output
)
}
}
func
TestFromFloat
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
s
string
f
float64
}{
{
"12345"
,
12345
},
{
"123.45"
,
123.45
},
{
"-123.45"
,
-
123.45
},
{
"0.00012345000098765"
,
0.00012345000098765
},
{
"1234500009876.5"
,
1234500009876.5
},
}
for
_
,
tt
:=
range
tests
{
dec
:=
NewDecFromFloatForTest
(
tt
.
f
)
str
:=
dec
.
ToString
()
assert
.
Equal
(
t
,
string
(
str
),
tt
.
s
)
}
}
func
TestToFloat
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
s
string
f
float64
}{
{
"12345"
,
12345
},
{
"123.45"
,
123.45
},
{
"-123.45"
,
-
123.45
},
{
"0.00012345000098765"
,
0.00012345000098765
},
{
"1234500009876.5"
,
1234500009876.5
},
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
dec
.
FromString
([]
byte
(
ca
.
s
))
f
,
_
:=
dec
.
ToFloat64
()
assert
.
Equal
(
t
,
f
,
ca
.
f
)
}
}
func
TestToHashKey
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
numbers
[]
string
}{
{[]
string
{
"1.1"
,
"1.1000"
,
"1.1000000"
,
"1.10000000000"
,
"01.1"
,
"0001.1"
,
"001.1000000"
}},
{[]
string
{
"-1.1"
,
"-1.1000"
,
"-1.1000000"
,
"-1.10000000000"
,
"-01.1"
,
"-0001.1"
,
"-001.1000000"
}},
{[]
string
{
".1"
,
"0.1"
,
"000000.1"
,
".10000"
,
"0000.10000"
,
"000000000000000000.1"
}},
{[]
string
{
"0"
,
"0000"
,
".0"
,
".00000"
,
"00000.00000"
,
"-0"
,
"-0000"
,
"-.0"
,
"-.00000"
,
"-00000.00000"
}},
{[]
string
{
".123456789123456789"
,
".1234567891234567890"
,
".12345678912345678900"
,
".123456789123456789000"
,
".1234567891234567890000"
,
"0.123456789123456789"
,
".1234567891234567890000000000"
,
"0000000.123456789123456789000"
}},
{[]
string
{
"12345"
,
"012345"
,
"0012345"
,
"0000012345"
,
"0000000012345"
,
"00000000000012345"
,
"12345."
,
"12345.00"
,
"12345.000000000"
,
"000012345.0000"
}},
{[]
string
{
"123E5"
,
"12300000"
,
"00123E5"
,
"000000123E5"
,
"12300000.00000000"
}},
{[]
string
{
"123E-2"
,
"1.23"
,
"00000001.23"
,
"1.2300000000000000"
,
"000000001.23000000000000"
}},
}
for
_
,
ca
:=
range
tests
{
keys
:=
make
([]
string
,
0
,
len
(
ca
.
numbers
))
for
_
,
num
:=
range
ca
.
numbers
{
var
dec
BigDecimal
assert
.
Equal
(
t
,
dec
.
FromString
([]
byte
(
num
)),
nil
)
key
,
err
:=
dec
.
ToHashKey
()
assert
.
Equal
(
t
,
err
,
nil
)
keys
=
append
(
keys
,
string
(
key
))
}
for
i
:=
1
;
i
<
len
(
keys
);
i
++
{
assert
.
Equal
(
t
,
keys
[
0
],
keys
[
i
])
}
}
binTests
:=
[]
struct
{
hashNumbers
[]
string
binNumbers
[]
string
}{
{[]
string
{
"1.1"
,
"1.1000"
,
"1.1000000"
,
"1.10000000000"
,
"01.1"
,
"0001.1"
,
"001.1000000"
},
[]
string
{
"1.1"
,
"0001.1"
,
"01.1"
}},
{[]
string
{
"-1.1"
,
"-1.1000"
,
"-1.1000000"
,
"-1.10000000000"
,
"-01.1"
,
"-0001.1"
,
"-001.1000000"
},
[]
string
{
"-1.1"
,
"-0001.1"
,
"-01.1"
}},
{[]
string
{
".1"
,
"0.1"
,
"000000.1"
,
".10000"
,
"0000.10000"
,
"000000000000000000.1"
},
[]
string
{
".1"
,
"0.1"
,
"000000.1"
,
"00.1"
}},
{[]
string
{
"0"
,
"0000"
,
".0"
,
".00000"
,
"00000.00000"
,
"-0"
,
"-0000"
,
"-.0"
,
"-.00000"
,
"-00000.00000"
},
[]
string
{
"0"
,
"0000"
,
"00"
,
"-0"
,
"-00"
,
"-000000"
}},
{[]
string
{
".123456789123456789"
,
".1234567891234567890"
,
".12345678912345678900"
,
".123456789123456789000"
,
".1234567891234567890000"
,
"0.123456789123456789"
,
".1234567891234567890000000000"
,
"0000000.123456789123456789000"
},
[]
string
{
".123456789123456789"
,
"0.123456789123456789"
,
"0000.123456789123456789"
,
"0000000.123456789123456789"
}},
{[]
string
{
"12345"
,
"012345"
,
"0012345"
,
"0000012345"
,
"0000000012345"
,
"00000000000012345"
,
"12345."
,
"12345.00"
,
"12345.000000000"
,
"000012345.0000"
},
[]
string
{
"12345"
,
"012345"
,
"000012345"
,
"000000000000012345"
}},
{[]
string
{
"123E5"
,
"12300000"
,
"00123E5"
,
"000000123E5"
,
"12300000.00000000"
},
[]
string
{
"12300000"
,
"123E5"
,
"00123E5"
,
"0000000000123E5"
}},
{[]
string
{
"123E-2"
,
"1.23"
,
"00000001.23"
,
"1.2300000000000000"
,
"000000001.23000000000000"
},
[]
string
{
"123E-2"
,
"1.23"
,
"000001.23"
,
"0000000000001.23"
}},
}
for
_
,
ca
:=
range
binTests
{
keys
:=
make
([]
string
,
0
,
len
(
ca
.
hashNumbers
)
+
len
(
ca
.
binNumbers
))
for
_
,
num
:=
range
ca
.
hashNumbers
{
var
dec
BigDecimal
assert
.
Equal
(
t
,
dec
.
FromString
([]
byte
(
num
)),
nil
)
key
,
err
:=
dec
.
ToHashKey
()
assert
.
Equal
(
t
,
err
,
nil
)
keys
=
append
(
keys
,
string
(
key
))
}
for
_
,
num
:=
range
ca
.
binNumbers
{
var
dec
BigDecimal
assert
.
Equal
(
t
,
dec
.
FromString
([]
byte
(
num
)),
nil
)
prec
,
frac
:=
dec
.
PrecisionAndFrac
()
// remove leading zeros but trailing zeros remain
key
,
err
:=
dec
.
ToBin
(
prec
,
frac
)
assert
.
Equal
(
t
,
err
,
nil
)
keys
=
append
(
keys
,
string
(
key
))
}
for
i
:=
1
;
i
<
len
(
keys
);
i
++
{
assert
.
Equal
(
t
,
keys
[
0
],
keys
[
i
])
}
}
}
func
TestRemoveTrailingZeros
(
t
*
testing
.
T
)
{
tests
:=
[]
string
{
"0"
,
"0.0"
,
".0"
,
".00000000"
,
"0.0000"
,
"0000"
,
"0000.0"
,
"0000.000"
,
"-0"
,
"-0.0"
,
"-.0"
,
"-.00000000"
,
"-0.0000"
,
"-0000"
,
"-0000.0"
,
"-0000.000"
,
"123123123"
,
"213123."
,
"21312.000"
,
"21321.123"
,
"213.1230000"
,
"213123.000123000"
,
"-123123123"
,
"-213123."
,
"-21312.000"
,
"-21321.123"
,
"-213.1230000"
,
"-213123.000123000"
,
"123E5"
,
"12300E-5"
,
"0.00100E1"
,
"0.001230E-3"
,
"123987654321.123456789000"
,
"000000000123"
,
"123456789.987654321"
,
"999.999000"
,
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
assert
.
Equal
(
t
,
dec
.
FromString
([]
byte
(
ca
)),
nil
)
// calculate the number of digits after point but trailing zero
digitsFracExp
:=
0
str
:=
string
(
dec
.
ToString
())
point
:=
strings
.
Index
(
str
,
"."
)
if
point
!=
-
1
{
pos
:=
len
(
str
)
-
1
for
pos
>
point
{
if
str
[
pos
]
!=
'0'
{
break
}
pos
--
}
digitsFracExp
=
pos
-
point
}
_
,
digitsFrac
:=
dec
.
removeTrailingZeros
()
assert
.
Equal
(
t
,
digitsFrac
,
digitsFracExp
)
}
}
func
TestShift
(
t
*
testing
.
T
)
{
type
tcase
struct
{
input
string
shift
int
output
string
err
error
}
var
dotest
=
func
(
t
*
testing
.
T
,
tests
[]
tcase
)
{
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
err
:=
dec
.
FromString
([]
byte
(
ca
.
input
))
//assert.Equal(t, err, IsNil)
//origin := dec
err
=
dec
.
Shift
(
ca
.
shift
)
assert
.
Equal
(
t
,
err
,
ca
.
err
)
result
:=
dec
.
ToString
()
//, Commentf("origin:%s\ndec:%s", origin.String(), string(result))
assert
.
Equal
(
t
,
string
(
result
),
ca
.
output
)
}
}
wordBufLen
=
maxWordBufLen
tests
:=
[]
tcase
{
{
"123.123"
,
1
,
"1231.23"
,
nil
},
{
"123457189.123123456789000"
,
1
,
"1234571891.23123456789"
,
nil
},
{
"123457189.123123456789000"
,
8
,
"12345718912312345.6789"
,
nil
},
{
"123457189.123123456789000"
,
9
,
"123457189123123456.789"
,
nil
},
{
"123457189.123123456789000"
,
10
,
"1234571891231234567.89"
,
nil
},
{
"123457189.123123456789000"
,
17
,
"12345718912312345678900000"
,
nil
},
{
"123457189.123123456789000"
,
18
,
"123457189123123456789000000"
,
nil
},
{
"123457189.123123456789000"
,
19
,
"1234571891231234567890000000"
,
nil
},
{
"123457189.123123456789000"
,
26
,
"12345718912312345678900000000000000"
,
nil
},
{
"123457189.123123456789000"
,
27
,
"123457189123123456789000000000000000"
,
nil
},
{
"123457189.123123456789000"
,
28
,
"1234571891231234567890000000000000000"
,
nil
},
{
"000000000000000000000000123457189.123123456789000"
,
26
,
"12345718912312345678900000000000000"
,
nil
},
{
"00000000123457189.123123456789000"
,
27
,
"123457189123123456789000000000000000"
,
nil
},
{
"00000000000000000123457189.123123456789000"
,
28
,
"1234571891231234567890000000000000000"
,
nil
},
{
"123"
,
1
,
"1230"
,
nil
},
{
"123"
,
10
,
"1230000000000"
,
nil
},
{
".123"
,
1
,
"1.23"
,
nil
},
{
".123"
,
10
,
"1230000000"
,
nil
},
{
".123"
,
14
,
"12300000000000"
,
nil
},
{
"000.000"
,
1000
,
"0"
,
nil
},
{
"000."
,
1000
,
"0"
,
nil
},
{
".000"
,
1000
,
"0"
,
nil
},
{
"1"
,
1000
,
"1"
,
ErrOverflow
},
{
"123.123"
,
-
1
,
"12.3123"
,
nil
},
{
"123987654321.123456789000"
,
-
1
,
"12398765432.1123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
2
,
"1239876543.21123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
3
,
"123987654.321123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
8
,
"1239.87654321123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
9
,
"123.987654321123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
10
,
"12.3987654321123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
11
,
"1.23987654321123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
12
,
"0.123987654321123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
13
,
"0.0123987654321123456789"
,
nil
},
{
"123987654321.123456789000"
,
-
14
,
"0.00123987654321123456789"
,
nil
},
{
"00000087654321.123456789000"
,
-
14
,
"0.00000087654321123456789"
,
nil
},
}
dotest
(
t
,
tests
)
wordBufLen
=
2
tests
=
[]
tcase
{
{
"123.123"
,
-
2
,
"1.23123"
,
nil
},
{
"123.123"
,
-
3
,
"0.123123"
,
nil
},
{
"123.123"
,
-
6
,
"0.000123123"
,
nil
},
{
"123.123"
,
-
7
,
"0.0000123123"
,
nil
},
{
"123.123"
,
-
15
,
"0.000000000000123123"
,
nil
},
{
"123.123"
,
-
16
,
"0.000000000000012312"
,
ErrTruncated
},
{
"123.123"
,
-
17
,
"0.000000000000001231"
,
ErrTruncated
},
{
"123.123"
,
-
18
,
"0.000000000000000123"
,
ErrTruncated
},
{
"123.123"
,
-
19
,
"0.000000000000000012"
,
ErrTruncated
},
{
"123.123"
,
-
20
,
"0.000000000000000001"
,
ErrTruncated
},
{
"123.123"
,
-
21
,
"0"
,
ErrTruncated
},
{
".000000000123"
,
-
1
,
"0.0000000000123"
,
nil
},
{
".000000000123"
,
-
6
,
"0.000000000000000123"
,
nil
},
{
".000000000123"
,
-
7
,
"0.000000000000000012"
,
ErrTruncated
},
{
".000000000123"
,
-
8
,
"0.000000000000000001"
,
ErrTruncated
},
{
".000000000123"
,
-
9
,
"0"
,
ErrTruncated
},
{
".000000000123"
,
1
,
"0.00000000123"
,
nil
},
{
".000000000123"
,
8
,
"0.0123"
,
nil
},
{
".000000000123"
,
9
,
"0.123"
,
nil
},
{
".000000000123"
,
10
,
"1.23"
,
nil
},
{
".000000000123"
,
17
,
"12300000"
,
nil
},
{
".000000000123"
,
18
,
"123000000"
,
nil
},
{
".000000000123"
,
19
,
"1230000000"
,
nil
},
{
".000000000123"
,
20
,
"12300000000"
,
nil
},
{
".000000000123"
,
21
,
"123000000000"
,
nil
},
{
".000000000123"
,
22
,
"1230000000000"
,
nil
},
{
".000000000123"
,
23
,
"12300000000000"
,
nil
},
{
".000000000123"
,
24
,
"123000000000000"
,
nil
},
{
".000000000123"
,
25
,
"1230000000000000"
,
nil
},
{
".000000000123"
,
26
,
"12300000000000000"
,
nil
},
{
".000000000123"
,
27
,
"123000000000000000"
,
nil
},
{
".000000000123"
,
28
,
"0.000000000123"
,
ErrOverflow
},
{
"123456789.987654321"
,
-
1
,
"12345678.998765432"
,
ErrTruncated
},
{
"123456789.987654321"
,
-
2
,
"1234567.899876543"
,
ErrTruncated
},
{
"123456789.987654321"
,
-
8
,
"1.234567900"
,
ErrTruncated
},
{
"123456789.987654321"
,
-
9
,
"0.123456789987654321"
,
nil
},
{
"123456789.987654321"
,
-
10
,
"0.012345678998765432"
,
ErrTruncated
},
{
"123456789.987654321"
,
-
17
,
"0.000000001234567900"
,
ErrTruncated
},
{
"123456789.987654321"
,
-
18
,
"0.000000000123456790"
,
ErrTruncated
},
{
"123456789.987654321"
,
-
19
,
"0.000000000012345679"
,
ErrTruncated
},
{
"123456789.987654321"
,
-
26
,
"0.000000000000000001"
,
ErrTruncated
},
{
"123456789.987654321"
,
-
27
,
"0"
,
ErrTruncated
},
{
"123456789.987654321"
,
1
,
"1234567900"
,
ErrTruncated
},
{
"123456789.987654321"
,
2
,
"12345678999"
,
ErrTruncated
},
{
"123456789.987654321"
,
4
,
"1234567899877"
,
ErrTruncated
},
{
"123456789.987654321"
,
8
,
"12345678998765432"
,
ErrTruncated
},
{
"123456789.987654321"
,
9
,
"123456789987654321"
,
nil
},
{
"123456789.987654321"
,
10
,
"123456789.987654321"
,
ErrOverflow
},
{
"123456789.987654321"
,
0
,
"123456789.987654321"
,
nil
},
}
dotest
(
t
,
tests
)
wordBufLen
=
maxWordBufLen
}
func
TestRoundWithHalfEven
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
string
scale
int
output
string
err
error
}{
{
"123456789.987654321"
,
1
,
"123456790.0"
,
nil
},
{
"15.1"
,
0
,
"15"
,
nil
},
{
"15.5"
,
0
,
"16"
,
nil
},
{
"15.9"
,
0
,
"16"
,
nil
},
{
"-15.1"
,
0
,
"-15"
,
nil
},
{
"-15.5"
,
0
,
"-16"
,
nil
},
{
"-15.9"
,
0
,
"-16"
,
nil
},
{
"15.1"
,
1
,
"15.1"
,
nil
},
{
"-15.1"
,
1
,
"-15.1"
,
nil
},
{
"15.17"
,
1
,
"15.2"
,
nil
},
{
"15.4"
,
-
1
,
"20"
,
nil
},
{
"-15.4"
,
-
1
,
"-20"
,
nil
},
{
"5.4"
,
-
1
,
"10"
,
nil
},
{
".999"
,
0
,
"1"
,
nil
},
{
"999999999"
,
-
9
,
"1000000000"
,
nil
},
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
dec
.
FromString
([]
byte
(
ca
.
input
))
var
rounded
BigDecimal
err
:=
dec
.
Round
(
&
rounded
,
ca
.
scale
,
ModeHalfEven
)
assert
.
Equal
(
t
,
err
,
ca
.
err
)
result
:=
rounded
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
ca
.
output
)
}
}
func
TestRoundWithTruncate
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
string
scale
int
output
string
err
error
}{
{
"123456789.987654321"
,
1
,
"123456789.9"
,
nil
},
{
"15.1"
,
0
,
"15"
,
nil
},
{
"15.5"
,
0
,
"15"
,
nil
},
{
"15.9"
,
0
,
"15"
,
nil
},
{
"-15.1"
,
0
,
"-15"
,
nil
},
{
"-15.5"
,
0
,
"-15"
,
nil
},
{
"-15.9"
,
0
,
"-15"
,
nil
},
{
"15.1"
,
1
,
"15.1"
,
nil
},
{
"-15.1"
,
1
,
"-15.1"
,
nil
},
{
"15.17"
,
1
,
"15.1"
,
nil
},
{
"15.4"
,
-
1
,
"10"
,
nil
},
{
"-15.4"
,
-
1
,
"-10"
,
nil
},
{
"5.4"
,
-
1
,
"0"
,
nil
},
{
".999"
,
0
,
"0"
,
nil
},
{
"999999999"
,
-
9
,
"0"
,
nil
},
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
dec
.
FromString
([]
byte
(
ca
.
input
))
var
rounded
BigDecimal
err
:=
dec
.
Round
(
&
rounded
,
ca
.
scale
,
ModeTruncate
)
assert
.
Equal
(
t
,
err
,
ca
.
err
)
result
:=
rounded
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
ca
.
output
)
}
}
func
TestRoundWithCeil
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
string
scale
int
output
string
err
error
}{
{
"123456789.987654321"
,
1
,
"123456790.0"
,
nil
},
{
"15.1"
,
0
,
"16"
,
nil
},
{
"15.5"
,
0
,
"16"
,
nil
},
{
"15.9"
,
0
,
"16"
,
nil
},
//TODO:fix me
{
"-15.1"
,
0
,
"-16"
,
nil
},
{
"-15.5"
,
0
,
"-16"
,
nil
},
{
"-15.9"
,
0
,
"-16"
,
nil
},
{
"15.1"
,
1
,
"15.1"
,
nil
},
{
"-15.1"
,
1
,
"-15.1"
,
nil
},
{
"15.17"
,
1
,
"15.2"
,
nil
},
{
"15.4"
,
-
1
,
"20"
,
nil
},
{
"-15.4"
,
-
1
,
"-20"
,
nil
},
{
"5.4"
,
-
1
,
"10"
,
nil
},
{
".999"
,
0
,
"1"
,
nil
},
{
"999999999"
,
-
9
,
"1000000000"
,
nil
},
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
dec
.
FromString
([]
byte
(
ca
.
input
))
var
rounded
BigDecimal
err
:=
dec
.
Round
(
&
rounded
,
ca
.
scale
,
modeCeiling
)
assert
.
Equal
(
t
,
err
,
ca
.
err
)
result
:=
rounded
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
ca
.
output
)
}
}
func
TestFromString
(
t
*
testing
.
T
)
{
type
tcase
struct
{
input
string
output
string
err
error
}
tests
:=
[]
tcase
{
{
"12345"
,
"12345"
,
nil
},
{
"12345."
,
"12345"
,
nil
},
{
"123.45."
,
"123.45"
,
nil
},
{
"-123.45."
,
"-123.45"
,
nil
},
{
".00012345000098765"
,
"0.00012345000098765"
,
nil
},
{
".12345000098765"
,
"0.12345000098765"
,
nil
},
{
"-.000000012345000098765"
,
"-0.000000012345000098765"
,
nil
},
{
"1234500009876.5"
,
"1234500009876.5"
,
nil
},
{
"123E5"
,
"12300000"
,
nil
},
{
"123E-2"
,
"1.23"
,
nil
},
{
"1e1073741823"
,
"999999999999999999999999999999999999999999999999999999999999999999999999999999999"
,
ErrOverflow
},
{
"-1e1073741823"
,
"-999999999999999999999999999999999999999999999999999999999999999999999999999999999"
,
ErrOverflow
},
{
"1e18446744073709551620"
,
"0"
,
ErrBadNumber
},
{
"1e"
,
"1"
,
ErrTruncated
},
{
"1e001"
,
"10"
,
nil
},
{
"1e00"
,
"1"
,
nil
},
{
"1eabc"
,
"1"
,
ErrTruncated
},
{
"1e 1dddd "
,
"10"
,
ErrTruncated
},
{
"1e - 1"
,
"1"
,
ErrTruncated
},
{
"1e -1"
,
"0.1"
,
nil
},
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
_
=
dec
.
FromString
([]
byte
(
ca
.
input
))
result
:=
string
(
dec
.
ToString
())
assert
.
Equal
(
t
,
result
,
ca
.
output
)
}
wordBufLen
=
1
tests
=
[]
tcase
{
{
"123450000098765"
,
"98765"
,
ErrOverflow
},
{
"123450.000098765"
,
"123450"
,
ErrTruncated
},
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
err
:=
dec
.
FromString
([]
byte
(
ca
.
input
))
assert
.
Equal
(
t
,
err
,
ca
.
err
)
result
:=
dec
.
ToString
()
//, Commentf("dec:%s", dec.String())
assert
.
Equal
(
t
,
string
(
result
),
ca
.
output
)
}
wordBufLen
=
maxWordBufLen
}
func
TestToString
(
t
*
testing
.
T
)
{
type
tcase
struct
{
input
string
output
string
}
tests
:=
[]
tcase
{
{
"123.123"
,
"123.123"
},
{
"123.1230"
,
"123.1230"
},
{
"00123.123"
,
"123.123"
},
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
dec
.
FromString
([]
byte
(
ca
.
input
))
result
:=
dec
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
ca
.
output
)
}
}
func
TestToBinFromBin
(
t
*
testing
.
T
)
{
type
tcase
struct
{
input
string
precision
int
frac
int
output
string
err
error
}
tests
:=
[]
tcase
{
{
"-10.55"
,
4
,
2
,
"-10.55"
,
nil
},
{
"0.0123456789012345678912345"
,
30
,
25
,
"0.0123456789012345678912345"
,
nil
},
{
"12345"
,
5
,
0
,
"12345"
,
nil
},
{
"12345"
,
10
,
3
,
"12345.000"
,
nil
},
{
"123.45"
,
10
,
3
,
"123.450"
,
nil
},
{
"-123.45"
,
20
,
10
,
"-123.4500000000"
,
nil
},
{
".00012345000098765"
,
15
,
14
,
"0.00012345000098"
,
ErrTruncated
},
{
".00012345000098765"
,
22
,
20
,
"0.00012345000098765000"
,
nil
},
{
".12345000098765"
,
30
,
20
,
"0.12345000098765000000"
,
nil
},
{
"-.000000012345000098765"
,
30
,
20
,
"-0.00000001234500009876"
,
ErrTruncated
},
{
"1234500009876.5"
,
30
,
5
,
"1234500009876.50000"
,
nil
},
{
"111111111.11"
,
10
,
2
,
"11111111.11"
,
ErrOverflow
},
{
"000000000.01"
,
7
,
3
,
"0.010"
,
nil
},
{
"123.4"
,
10
,
2
,
"123.40"
,
nil
},
{
"1000"
,
3
,
0
,
"0"
,
ErrOverflow
},
}
for
_
,
ca
:=
range
tests
{
var
dec
BigDecimal
err
:=
dec
.
FromString
([]
byte
(
ca
.
input
))
assert
.
Equal
(
t
,
err
,
nil
)
buf
,
err
:=
dec
.
ToBin
(
ca
.
precision
,
ca
.
frac
)
assert
.
Equal
(
t
,
err
,
ca
.
err
)
var
dec2
BigDecimal
_
,
err
=
dec2
.
FromBin
(
buf
,
ca
.
precision
,
ca
.
frac
)
assert
.
Equal
(
t
,
err
,
nil
)
str
:=
dec2
.
ToString
()
assert
.
Equal
(
t
,
string
(
str
),
ca
.
output
)
}
var
dec
BigDecimal
dec
.
FromInt
(
1
)
errTests
:=
[]
struct
{
prec
int
frac
int
}{
{
82
,
1
},
{
-
1
,
1
},
{
10
,
31
},
{
10
,
-
1
},
}
for
_
,
tt
:=
range
errTests
{
_
,
_
=
dec
.
ToBin
(
tt
.
prec
,
tt
.
frac
)
//assert.Equal(t,ErrBadNumber.Equal(err), IsTrue)
}
}
func
TestCompare
(
t
*
testing
.
T
)
{
type
tcase
struct
{
a
string
b
string
cmp
int
}
tests
:=
[]
tcase
{
{
"12"
,
"13"
,
-
1
},
{
"13"
,
"12"
,
1
},
{
"-10"
,
"10"
,
-
1
},
{
"10"
,
"-10"
,
1
},
{
"-12"
,
"-13"
,
1
},
{
"0"
,
"12"
,
-
1
},
{
"-10"
,
"0"
,
-
1
},
{
"4"
,
"4"
,
0
},
{
"-1.1"
,
"-1.2"
,
1
},
{
"1.2"
,
"1.1"
,
1
},
{
"1.1"
,
"1.2"
,
-
1
},
}
for
_
,
tt
:=
range
tests
{
var
a
,
b
BigDecimal
_
=
a
.
FromString
([]
byte
(
tt
.
a
))
_
=
b
.
FromString
([]
byte
(
tt
.
b
))
assert
.
Equal
(
t
,
a
.
Compare
(
&
b
),
tt
.
cmp
)
}
}
func
TestMaxDecimal
(
t
*
testing
.
T
)
{
type
tcase
struct
{
prec
int
frac
int
result
string
}
tests
:=
[]
tcase
{
{
1
,
1
,
"0.9"
},
{
1
,
0
,
"9"
},
{
2
,
1
,
"9.9"
},
{
4
,
2
,
"99.99"
},
{
6
,
3
,
"999.999"
},
{
8
,
4
,
"9999.9999"
},
{
10
,
5
,
"99999.99999"
},
{
12
,
6
,
"999999.999999"
},
{
14
,
7
,
"9999999.9999999"
},
{
16
,
8
,
"99999999.99999999"
},
{
18
,
9
,
"999999999.999999999"
},
{
20
,
10
,
"9999999999.9999999999"
},
{
20
,
20
,
"0.99999999999999999999"
},
{
20
,
0
,
"99999999999999999999"
},
{
40
,
20
,
"99999999999999999999.99999999999999999999"
},
}
for
_
,
tt
:=
range
tests
{
var
dec
BigDecimal
maxDecimal
(
tt
.
prec
,
tt
.
frac
,
&
dec
)
str
:=
dec
.
ToString
()
assert
.
Equal
(
t
,
string
(
str
),
tt
.
result
)
}
}
func
TestNeg
(
t
*
testing
.
T
)
{
type
testCase
struct
{
a
string
result
string
err
error
}
tests
:=
[]
testCase
{
{
"-0.0000000000000000000000000000000000000000000000000017382578996420603"
,
"0.0000000000000000000000000000000000000000000000000017382578996420603"
,
nil
},
{
"-13890436710184412000000000000000000000000000000000000000000000000000000000000"
,
"13890436710184412000000000000000000000000000000000000000000000000000000000000"
,
nil
},
{
"0"
,
"0"
,
nil
},
}
for
_
,
tt
:=
range
tests
{
a
:=
NewDecFromStringForTest
(
tt
.
a
)
negResult
:=
DecimalNeg
(
a
)
result
:=
negResult
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
tt
.
result
)
}
}
func
TestAdd
(
t
*
testing
.
T
)
{
type
testCase
struct
{
a
string
b
string
result
string
err
error
}
tests
:=
[]
testCase
{
{
".00012345000098765"
,
"123.45"
,
"123.45012345000098765"
,
nil
},
{
".1"
,
".45"
,
"0.55"
,
nil
},
{
"1234500009876.5"
,
".00012345000098765"
,
"1234500009876.50012345000098765"
,
nil
},
{
"9999909999999.5"
,
".555"
,
"9999910000000.055"
,
nil
},
{
"99999999"
,
"1"
,
"100000000"
,
nil
},
{
"989999999"
,
"1"
,
"990000000"
,
nil
},
{
"999999999"
,
"1"
,
"1000000000"
,
nil
},
{
"12345"
,
"123.45"
,
"12468.45"
,
nil
},
{
"-12345"
,
"-123.45"
,
"-12468.45"
,
nil
},
{
"-12345"
,
"123.45"
,
"-12221.55"
,
nil
},
{
"12345"
,
"-123.45"
,
"12221.55"
,
nil
},
{
"123.45"
,
"-12345"
,
"-12221.55"
,
nil
},
{
"-123.45"
,
"12345"
,
"12221.55"
,
nil
},
{
"5"
,
"-6.0"
,
"-1.0"
,
nil
},
{
"2"
+
strings
.
Repeat
(
"1"
,
71
),
strings
.
Repeat
(
"8"
,
81
),
"8888888890"
+
strings
.
Repeat
(
"9"
,
71
),
nil
},
}
for
_
,
tt
:=
range
tests
{
a
:=
NewDecFromStringForTest
(
tt
.
a
)
b
:=
NewDecFromStringForTest
(
tt
.
b
)
var
sum
BigDecimal
err
:=
DecimalAdd
(
a
,
b
,
&
sum
)
assert
.
Equal
(
t
,
err
,
tt
.
err
)
result
:=
sum
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
tt
.
result
)
}
}
func
TestSub
(
t
*
testing
.
T
)
{
type
tcase
struct
{
a
string
b
string
result
string
err
error
}
tests
:=
[]
tcase
{
{
".00012345000098765"
,
"123.45"
,
"-123.44987654999901235"
,
nil
},
{
"1234500009876.5"
,
".00012345000098765"
,
"1234500009876.49987654999901235"
,
nil
},
{
"9999900000000.5"
,
".555"
,
"9999899999999.945"
,
nil
},
{
"1111.5551"
,
"1111.555"
,
"0.0001"
,
nil
},
{
".555"
,
".555"
,
"0"
,
nil
},
{
"10000000"
,
"1"
,
"9999999"
,
nil
},
{
"1000001000"
,
".1"
,
"1000000999.9"
,
nil
},
{
"1000000000"
,
".1"
,
"999999999.9"
,
nil
},
{
"12345"
,
"123.45"
,
"12221.55"
,
nil
},
{
"-12345"
,
"-123.45"
,
"-12221.55"
,
nil
},
{
"123.45"
,
"12345"
,
"-12221.55"
,
nil
},
{
"-123.45"
,
"-12345"
,
"12221.55"
,
nil
},
{
"-12345"
,
"123.45"
,
"-12468.45"
,
nil
},
{
"12345"
,
"-123.45"
,
"12468.45"
,
nil
},
}
for
_
,
tt
:=
range
tests
{
var
a
,
b
,
sum
BigDecimal
a
.
FromString
([]
byte
(
tt
.
a
))
b
.
FromString
([]
byte
(
tt
.
b
))
err
:=
DecimalSub
(
&
a
,
&
b
,
&
sum
)
assert
.
Equal
(
t
,
err
,
tt
.
err
)
result
:=
sum
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
tt
.
result
)
}
}
func
TestMul
(
t
*
testing
.
T
)
{
type
tcase
struct
{
a
string
b
string
result
string
err
error
}
tests
:=
[]
tcase
{
{
"12"
,
"10"
,
"120"
,
nil
},
{
"-123.456"
,
"98765.4321"
,
"-12193185.1853376"
,
nil
},
{
"-123456000000"
,
"98765432100000"
,
"-12193185185337600000000000"
,
nil
},
{
"123456"
,
"987654321"
,
"121931851853376"
,
nil
},
{
"123456"
,
"9876543210"
,
"1219318518533760"
,
nil
},
{
"123"
,
"0.01"
,
"1.23"
,
nil
},
{
"123"
,
"0"
,
"0"
,
nil
},
{
"-0.0000000000000000000000000000000000000000000000000017382578996420603"
,
"-13890436710184412000000000000000000000000000000000000000000000000000000000000"
,
"0.000000000000000000000000000000"
,
ErrTruncated
},
{
"1"
+
strings
.
Repeat
(
"0"
,
60
),
"1"
+
strings
.
Repeat
(
"0"
,
60
),
"0"
,
ErrOverflow
},
{
"0.5999991229316"
,
"0.918755041726043"
,
"0.5512522192246113614062276588"
,
nil
},
{
"0.5999991229317"
,
"0.918755041726042"
,
"0.5512522192247026369112773314"
,
nil
},
}
for
_
,
tt
:=
range
tests
{
var
a
,
b
,
product
BigDecimal
a
.
FromString
([]
byte
(
tt
.
a
))
b
.
FromString
([]
byte
(
tt
.
b
))
err
:=
DecimalMul
(
&
a
,
&
b
,
&
product
)
assert
.
Equal
(
t
,
err
,
tt
.
err
)
result
:=
product
.
String
()
assert
.
Equal
(
t
,
result
,
tt
.
result
)
}
}
func
TestDivMod
(
t
*
testing
.
T
)
{
type
tcase
struct
{
a
string
b
string
result
string
err
error
}
tests
:=
[]
tcase
{
{
"120"
,
"10"
,
"12.000000000"
,
nil
},
{
"123"
,
"0.01"
,
"12300.000000000"
,
nil
},
{
"120"
,
"100000000000.00000"
,
"0.000000001200000000"
,
nil
},
{
"123"
,
"0"
,
""
,
ErrDivByZero
},
{
"0"
,
"0"
,
""
,
ErrDivByZero
},
{
"-12193185.1853376"
,
"98765.4321"
,
"-123.456000000000000000"
,
nil
},
{
"121931851853376"
,
"987654321"
,
"123456.000000000"
,
nil
},
{
"0"
,
"987"
,
"0"
,
nil
},
{
"1"
,
"3"
,
"0.333333333"
,
nil
},
{
"1.000000000000"
,
"3"
,
"0.333333333333333333"
,
nil
},
{
"1"
,
"1"
,
"1.000000000"
,
nil
},
{
"0.0123456789012345678912345"
,
"9999999999"
,
"0.000000000001234567890246913578148141"
,
nil
},
{
"10.333000000"
,
"12.34500"
,
"0.837019036046982584042122316"
,
nil
},
{
"10.000000000060"
,
"2"
,
"5.000000000030000000"
,
nil
},
{
"51"
,
"0.003430"
,
"14868.804664723032069970"
,
nil
},
}
for
_
,
tt
:=
range
tests
{
var
a
,
b
,
to
BigDecimal
a
.
FromString
([]
byte
(
tt
.
a
))
b
.
FromString
([]
byte
(
tt
.
b
))
err
:=
doDivMod
(
&
a
,
&
b
,
&
to
,
nil
,
5
)
assert
.
Equal
(
t
,
err
,
tt
.
err
)
if
tt
.
err
==
ErrDivByZero
{
continue
}
result
:=
to
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
tt
.
result
)
}
tests
=
[]
tcase
{
{
"234"
,
"10"
,
"4"
,
nil
},
{
"234.567"
,
"10.555"
,
"2.357"
,
nil
},
{
"-234.567"
,
"10.555"
,
"-2.357"
,
nil
},
{
"234.567"
,
"-10.555"
,
"2.357"
,
nil
},
{
"99999999999999999999999999999999999999"
,
"3"
,
"0"
,
nil
},
{
"51"
,
"0.003430"
,
"0.002760"
,
nil
},
{
"0.0000000001"
,
"1.0"
,
"0.0000000001"
,
nil
},
}
for
_
,
tt
:=
range
tests
{
var
a
,
b
,
to
BigDecimal
a
.
FromString
([]
byte
(
tt
.
a
))
b
.
FromString
([]
byte
(
tt
.
b
))
ec
:=
doDivMod
(
&
a
,
&
b
,
nil
,
&
to
,
0
)
assert
.
Equal
(
t
,
ec
,
tt
.
err
)
if
tt
.
err
==
ErrDivByZero
{
continue
}
result
:=
to
.
ToString
()
assert
.
Equal
(
t
,
string
(
result
),
tt
.
result
)
}
tests
=
[]
tcase
{
{
"1"
,
"1"
,
"1.0000"
,
nil
},
{
"1.00"
,
"1"
,
"1.000000"
,
nil
},
{
"1"
,
"1.000"
,
"1.0000"
,
nil
},
{
"2"
,
"3"
,
"0.6667"
,
nil
},
{
"51"
,
"0.003430"
,
"14868.8047"
,
nil
},
}
for
_
,
tt
:=
range
tests
{
var
a
,
b
,
to
BigDecimal
a
.
FromString
([]
byte
(
tt
.
a
))
b
.
FromString
([]
byte
(
tt
.
b
))
ec
:=
DecimalDiv
(
&
a
,
&
b
,
&
to
,
DivFracIncr
)
assert
.
Equal
(
t
,
ec
,
tt
.
err
)
if
tt
.
err
==
ErrDivByZero
{
continue
}
assert
.
Equal
(
t
,
to
.
String
(),
tt
.
result
)
}
tests
=
[]
tcase
{
{
"1"
,
"2.0"
,
"1.0"
,
nil
},
{
"1.0"
,
"2"
,
"1.0"
,
nil
},
{
"2.23"
,
"3"
,
"2.23"
,
nil
},
{
"51"
,
"0.003430"
,
"0.002760"
,
nil
},
}
for
_
,
tt
:=
range
tests
{
var
a
,
b
,
to
BigDecimal
a
.
FromString
([]
byte
(
tt
.
a
))
b
.
FromString
([]
byte
(
tt
.
b
))
ec
:=
DecimalMod
(
&
a
,
&
b
,
&
to
)
assert
.
Equal
(
t
,
ec
,
tt
.
err
)
if
tt
.
err
==
ErrDivByZero
{
continue
}
assert
.
Equal
(
t
,
to
.
String
(),
tt
.
result
)
}
}
func
TestMaxOrMin
(
t
*
testing
.
T
)
{
type
tcase
struct
{
neg
bool
prec
int
frac
int
result
string
}
tests
:=
[]
tcase
{
{
true
,
2
,
1
,
"-9.9"
},
{
false
,
1
,
1
,
"0.9"
},
{
true
,
1
,
0
,
"-9"
},
{
false
,
0
,
0
,
"0"
},
{
false
,
4
,
2
,
"99.99"
},
}
for
_
,
tt
:=
range
tests
{
dec
:=
NewMaxOrMinDec
(
tt
.
neg
,
tt
.
prec
,
tt
.
frac
)
assert
.
Equal
(
t
,
dec
.
String
(),
tt
.
result
)
}
}
func
benchmarkBigDecimalToBinOrHashCases
()
[]
string
{
return
[]
string
{
"1.000000000000"
,
"3"
,
"12.000000000"
,
"120"
,
"120000"
,
"100000000000.00000"
,
"0.000000001200000000"
,
"98765.4321"
,
"-123.456000000000000000"
,
"0"
,
"0000000000"
,
"0.00000000000"
,
}
}
func
BenchmarkBigDecimalToBin
(
b
*
testing
.
B
)
{
cases
:=
benchmarkBigDecimalToBinOrHashCases
()
decs
:=
make
([]
*
BigDecimal
,
0
,
len
(
cases
))
for
_
,
ca
:=
range
cases
{
var
dec
BigDecimal
if
err
:=
dec
.
FromString
([]
byte
(
ca
));
err
!=
nil
{
b
.
Fatal
(
err
)
}
decs
=
append
(
decs
,
&
dec
)
}
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
for
_
,
dec
:=
range
decs
{
prec
,
frac
:=
dec
.
PrecisionAndFrac
()
_
,
err
:=
dec
.
ToBin
(
prec
,
frac
)
if
err
!=
nil
{
b
.
Fatal
(
err
)
}
}
}
}
func
BenchmarkBigDecimalToHashKey
(
b
*
testing
.
B
)
{
cases
:=
benchmarkBigDecimalToBinOrHashCases
()
decs
:=
make
([]
*
BigDecimal
,
0
,
len
(
cases
))
for
_
,
ca
:=
range
cases
{
var
dec
BigDecimal
if
err
:=
dec
.
FromString
([]
byte
(
ca
));
err
!=
nil
{
b
.
Fatal
(
err
)
}
decs
=
append
(
decs
,
&
dec
)
}
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
for
_
,
dec
:=
range
decs
{
_
,
err
:=
dec
.
ToHashKey
()
if
err
!=
nil
{
b
.
Fatal
(
err
)
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment