Skip to content

Commit

Permalink
Revert decimal to latest working version.
Browse files Browse the repository at this point in the history
  • Loading branch information
Roman Golov committed May 17, 2024
1 parent 9d88fe4 commit 3c75022
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 429 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* Fixed incorrect formatting of decimal. Implementation of decimal has been reverted to latest working version

## v3.67.1
* Fixed race of stop internal processes on close topic writer
* Fixed goroutines leak within topic reader on network problems
Expand Down
170 changes: 59 additions & 111 deletions internal/decimal/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package decimal
import (
"math/big"
"math/bits"

"github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
)

const (
Expand Down Expand Up @@ -97,56 +99,27 @@ func Parse(s string, precision, scale uint32) (*big.Int, error) {
return v, nil
}

s, neg, specialValue := setSpecialValue(s, v)
if specialValue != nil {
return specialValue, nil
}
var err error
v, err = parseNumber(s, v, precision, scale, neg)
if err != nil {
return nil, err
}

return v, nil
}

func setSpecialValue(s string, v *big.Int) (string, bool, *big.Int) {
s, neg := parseSign(s)

return parseSpecialValue(s, neg, v)
}

func parseSign(s string) (string, bool) {
neg := s[0] == '-'
if neg || s[0] == '+' {
s = s[1:]
}

return s, neg
}

func parseSpecialValue(s string, neg bool, v *big.Int) (string, bool, *big.Int) {
if isInf(s) {
if neg {
return s, neg, v.Set(neginf)
return v.Set(neginf), nil
}

return s, neg, v.Set(inf)
return v.Set(inf), nil
}
if isNaN(s) {
if neg {
return s, neg, v.Set(negnan)
return v.Set(negnan), nil
}

return s, neg, v.Set(nan)
return v.Set(nan), nil
}

return s, neg, nil
}

func parseNumber(s string, v *big.Int, precision, scale uint32, neg bool) (*big.Int, error) {
var err error
integral := precision - scale

var dot bool
for ; len(s) > 0; s = s[1:] {
c := s[0]
Expand All @@ -158,10 +131,12 @@ func parseNumber(s string, v *big.Int, precision, scale uint32, neg bool) (*big.

continue
}
if dot && scale > 0 {
scale--
} else if dot {
break
if dot {
if scale > 0 {
scale--
} else {
break
}
}

if !isDigit(c) {
Expand All @@ -180,10 +155,30 @@ func parseNumber(s string, v *big.Int, precision, scale uint32, neg bool) (*big.
}
integral--
}
//nolint:nestif
if len(s) > 0 { // Characters remaining.
v, err = handleRemainingDigits(s, v, precision)
if err != nil {
return nil, err
c := s[0]
if !isDigit(c) {
return nil, syntaxError(s)
}
plus := c > '5'
if !plus && c == '5' {
var x big.Int
plus = x.And(v, one).Cmp(zero) != 0 // Last digit is not a zero.
for !plus && len(s) > 1 {
s = s[1:]
c := s[0]
if !isDigit(c) {
return nil, syntaxError(s)
}
plus = c != '0'
}
}
if plus {
v.Add(v, one)
if v.Cmp(pow(ten, precision)) >= 0 {
v.Set(inf)
}
}
}
v.Mul(v, pow(ten, scale))
Expand All @@ -194,56 +189,26 @@ func parseNumber(s string, v *big.Int, precision, scale uint32, neg bool) (*big.
return v, nil
}

func handleRemainingDigits(s string, v *big.Int, precision uint32) (*big.Int, error) {
c := s[0]
if !isDigit(c) {
return nil, syntaxError(s)
}
plus := c > '5'
if !plus && c == '5' {
var x big.Int
plus = x.And(v, one).Cmp(zero) != 0 // Last digit is not a zero.
for !plus && len(s) > 1 {
s = s[1:]
c := s[0]
if !isDigit(c) {
return nil, syntaxError(s)
}
plus = c != '0'
}
}
if plus {
v.Add(v, one)
if v.Cmp(pow(ten, precision)) >= 0 {
v.Set(inf)
}
}

return v, nil
}

// Format returns the string representation of x with the given precision and
// scale.
//
//nolint:funlen
func Format(x *big.Int, precision, scale uint32) string {
// Check for special values and nil pointer upfront.
if x == nil {
return "0"
}
if x.CmpAbs(inf) == 0 {
switch {
case x.CmpAbs(inf) == 0:
if x.Sign() < 0 {
return "-inf"
}

return "inf"
}
if x.CmpAbs(nan) == 0 {

case x.CmpAbs(nan) == 0:
if x.Sign() < 0 {
return "-nan"
}

return "nan"

case x == nil:
return "0"
}

v := big.NewInt(0).Set(x)
Expand All @@ -267,59 +232,42 @@ func Format(x *big.Int, precision, scale uint32) string {

digit.Mod(v, ten)
d := int(digit.Int64())

pos--
if d != 0 || scale == 0 || pos >= 0 {
setDigitAtPosition(bts, pos, d)
if d != 0 || scale == 0 || pos > 0 {
const numbers = "0123456789"
pos--
bts[pos] = numbers[d]
}

if scale > 0 {
scale--
if scale == 0 && pos > 0 {
bts[pos-1] = '.'
pos--
bts[pos] = '.'
}
}
}

for ; scale > 0; scale-- {
if precision == 0 {
pos = 0

break
if scale > 0 {
for ; scale > 0; scale-- {
if precision == 0 {
return errorTag
}
precision--
pos--
bts[pos] = '0'
}
precision--

pos--
bts[pos] = '0'
bts[pos] = '.'
}

if bts[pos] == '.' {
pos--
bts[pos] = '0'
}

if neg {
pos--
bts[pos] = '-'
}

return string(bts[pos:])
}

func abs(x *big.Int) (*big.Int, bool) {
v := big.NewInt(0).Set(x)
neg := x.Sign() < 0
if neg {
// Convert negative to positive.
v.Neg(x)
}

return v, neg
}

func setDigitAtPosition(bts []byte, pos, digit int) {
const numbers = "0123456789"
bts[pos] = numbers[digit]
return xstring.FromBytes(bts[pos:])
}

// BigIntToByte returns the 16-byte array representation of x.
Expand Down
Loading

0 comments on commit 3c75022

Please sign in to comment.