You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2339 lines
58 KiB
2339 lines
58 KiB
// 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 types
|
|
|
|
import (
|
|
"math"
|
|
"strconv"
|
|
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/log"
|
|
"github.com/pingcap/parser/mysql"
|
|
"github.com/pingcap/parser/terror"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// 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
|
|
)
|
|
|
|
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,
|
|
}
|
|
zeroMyDecimal = MyDecimal{}
|
|
)
|
|
|
|
// get the zero of MyDecimal with the specified result fraction digits
|
|
func zeroMyDecimalWithFrac(frac int8) MyDecimal {
|
|
zero := MyDecimal{}
|
|
zero.digitsFrac = frac
|
|
zero.resultFrac = frac
|
|
return zero
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// MyDecimalStructSize is the struct size of MyDecimal.
|
|
const MyDecimalStructSize = 40
|
|
|
|
// MyDecimal represents a decimal value.
|
|
type MyDecimal 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 *MyDecimal) IsNegative() bool {
|
|
return d.negative
|
|
}
|
|
|
|
// GetDigitsFrac returns the digitsFrac.
|
|
func (d *MyDecimal) GetDigitsFrac() int8 {
|
|
return d.digitsFrac
|
|
}
|
|
|
|
// GetDigitsInt returns the digitsInt.
|
|
func (d *MyDecimal) GetDigitsInt() int8 {
|
|
return d.digitsInt
|
|
}
|
|
|
|
// String returns the decimal string representation rounded to resultFrac.
|
|
func (d *MyDecimal) String() string {
|
|
tmp := *d
|
|
err := tmp.Round(&tmp, int(tmp.resultFrac), ModeHalfEven)
|
|
terror.Log(errors.Trace(err))
|
|
return string(tmp.ToString())
|
|
}
|
|
|
|
func (d *MyDecimal) stringSize() int {
|
|
// sign, zero integer and dot.
|
|
return int(d.digitsInt + d.digitsFrac + 3)
|
|
}
|
|
|
|
func (d *MyDecimal) 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 *MyDecimal) 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 *MyDecimal) 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 *MyDecimal) FromString(str []byte) error {
|
|
for i := 0; i < len(str); i++ {
|
|
if !isSpace(str[i]) {
|
|
str = str[i:]
|
|
break
|
|
}
|
|
}
|
|
if len(str) == 0 {
|
|
*d = zeroMyDecimal
|
|
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 = zeroMyDecimal
|
|
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 = zeroMyDecimal
|
|
}
|
|
}
|
|
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 = zeroMyDecimal
|
|
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 *MyDecimal) 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 = zeroMyDecimal
|
|
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.Trace(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 = zeroMyDecimal
|
|
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 *MyDecimal) 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 *MyDecimal) 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 *MyDecimal) 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 *MyDecimal) Round(to *MyDecimal, 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 = zeroMyDecimal
|
|
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 = zeroMyDecimal
|
|
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 *MyDecimal) FromInt(val int64) *MyDecimal {
|
|
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 *MyDecimal) FromUint(val uint64) *MyDecimal {
|
|
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 *MyDecimal) 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 *MyDecimal) 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 *MyDecimal) FromFloat64(f float64) error {
|
|
s := strconv.FormatFloat(f, 'g', -1, 64)
|
|
return d.FromString([]byte(s))
|
|
}
|
|
|
|
// ToFloat64 converts decimal to float64 value.
|
|
func (d *MyDecimal) 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 *MyDecimal) ToBin(precision, frac int) ([]byte, error) {
|
|
if precision > digitsPerWord*maxWordBufLen || precision < 0 || frac > mysql.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 ||
|
|
(fracSize == fracSizeFrom && (trailingDigits <= trailingDigitsFrom || wordsFrac <= wordsFracFrom)) {
|
|
if fracSize < fracSizeFrom || (fracSize == fracSizeFrom && trailingDigits < trailingDigitsFrom) || (fracSize == fracSizeFrom && wordsFrac < wordsFracFrom) {
|
|
err = ErrTruncated
|
|
}
|
|
wordsFracFrom = wordsFrac
|
|
trailingDigitsFrom = trailingDigits
|
|
} 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 *MyDecimal) 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
|
|
}
|
|
buf = append(buf, byte(digitsFrac))
|
|
return buf, err
|
|
}
|
|
|
|
// PrecisionAndFrac returns the internal precision and frac number.
|
|
func (d *MyDecimal) 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 *MyDecimal) 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 *MyDecimal) FromBin(bin []byte, precision, frac int) (binSize int, err error) {
|
|
if len(bin) == 0 {
|
|
*d = zeroMyDecimal
|
|
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 = zeroMyDecimal
|
|
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 = zeroMyDecimal
|
|
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 = zeroMyDecimal
|
|
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 = zeroMyDecimal
|
|
return binSize, ErrBadNumber
|
|
}
|
|
}
|
|
|
|
if d.digitsInt == 0 && d.digitsFrac == 0 {
|
|
*d = zeroMyDecimal
|
|
}
|
|
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 *MyDecimal) Compare(to *MyDecimal) int {
|
|
if d.negative == to.negative {
|
|
cmp, err := doSub(d, to, nil)
|
|
terror.Log(errors.Trace(err))
|
|
return cmp
|
|
}
|
|
if d.negative {
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
|
|
// DecimalNeg reverses decimal's sign.
|
|
func DecimalNeg(from *MyDecimal) *MyDecimal {
|
|
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 *MyDecimal) error {
|
|
from1, from2, to = validateArgs(from1, from2, to)
|
|
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 *MyDecimal) error {
|
|
from1, from2, to = validateArgs(from1, from2, to)
|
|
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 validateArgs(f1, f2, to *MyDecimal) (*MyDecimal, *MyDecimal, *MyDecimal) {
|
|
if to == nil {
|
|
return f1, f2, to
|
|
}
|
|
if f1 == to {
|
|
tmp := *f1
|
|
f1 = &tmp
|
|
}
|
|
if f2 == to {
|
|
tmp := *f2
|
|
f2 = &tmp
|
|
}
|
|
to.digitsFrac = 0
|
|
to.digitsInt = 0
|
|
to.resultFrac = 0
|
|
to.negative = false
|
|
for i := range to.wordBuf {
|
|
to.wordBuf[i] = 0
|
|
}
|
|
return f1, f2, to
|
|
}
|
|
|
|
func doSub(from1, from2, to *MyDecimal) (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 = zeroMyDecimalWithFrac(to.resultFrac)
|
|
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 *MyDecimal) 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 = from1
|
|
} else {
|
|
idx1 = wordsInt2 - wordsInt1
|
|
dec1 = from2
|
|
}
|
|
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 *MyDecimal) {
|
|
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 *MyDecimal) error {
|
|
from1, from2, to = validateArgs(from1, from2, to)
|
|
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, mysql.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 = zeroMyDecimal.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 = zeroMyDecimalWithFrac(to.resultFrac)
|
|
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 *MyDecimal, fracIncr int) error {
|
|
from1, from2, to = validateArgs(from1, from2, to)
|
|
to.resultFrac = myMinInt8(from1.resultFrac+int8(fracIncr), mysql.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 *MyDecimal) error {
|
|
from1, from2, to = validateArgs(from1, from2, to)
|
|
to.resultFrac = myMaxInt8(from1.resultFrac, from2.resultFrac)
|
|
return doDivMod(from1, from2, nil, to, 0)
|
|
}
|
|
|
|
func doDivMod(from1, from2, to, mod *MyDecimal, 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 = zeroMyDecimalWithFrac(to.resultFrac)
|
|
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 = zeroMyDecimal
|
|
return err
|
|
}
|
|
if wordsIntTo <= 0 {
|
|
if -wordsIntTo >= wordBufLen {
|
|
*to = zeroMyDecimal
|
|
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:])
|
|
}
|
|
|
|
if to.IsZero() {
|
|
to.negative = false
|
|
}
|
|
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 MyDecimal from int.
|
|
func NewDecFromInt(i int64) *MyDecimal {
|
|
return new(MyDecimal).FromInt(i)
|
|
}
|
|
|
|
// NewDecFromUint creates a MyDecimal from uint.
|
|
func NewDecFromUint(i uint64) *MyDecimal {
|
|
return new(MyDecimal).FromUint(i)
|
|
}
|
|
|
|
// NewDecFromFloatForTest creates a MyDecimal from float, as it returns no error, it should only be used in test.
|
|
func NewDecFromFloatForTest(f float64) *MyDecimal {
|
|
dec := new(MyDecimal)
|
|
err := dec.FromFloat64(f)
|
|
if err != nil {
|
|
log.Panic("encountered error", zap.Error(err), zap.String("DecimalStr", strconv.FormatFloat(f, 'g', -1, 64)))
|
|
}
|
|
return dec
|
|
}
|
|
|
|
// NewDecFromStringForTest creates a MyDecimal from string, as it returns no error, it should only be used in test.
|
|
func NewDecFromStringForTest(s string) *MyDecimal {
|
|
dec := new(MyDecimal)
|
|
err := dec.FromString([]byte(s))
|
|
if err != nil {
|
|
log.Panic("encountered error", zap.Error(err), zap.String("DecimalStr", s))
|
|
}
|
|
return dec
|
|
}
|
|
|
|
// NewMaxOrMinDec returns the max or min value decimal for given precision and fraction.
|
|
func NewMaxOrMinDec(negative bool, prec, frac int) *MyDecimal {
|
|
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(MyDecimal)
|
|
err := dec.FromString(str)
|
|
terror.Log(errors.Trace(err))
|
|
return dec
|
|
}
|
|
|