alpacadecimal

package module
v0.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2023 License: MIT Imports: 6 Imported by: 0

README

alpacadecimal

Similar and compatible with decimal.Decimal, but optimized for Alpaca's data sets.

Goal
  • optimize for Alpaca data sets (99% of decimals are within 10 millions with up to 12 precisions).
  • compatible with decimal.Decimal so that it could be a drop-in replacement for current decimal.Decimal usage.
Key Ideas

The original decimal.Decimal package has bottleneck on big.Int operations, e.g. sql serialization / deserialization, addition, multiplication etc. These operations took fair amount cpu and memory during our profiling / monitoring.

profiling result

The optimization this library is to represent most decimal numbers with int64 instead of big.Int. To keep this library to be compatible with original decimal.Decimal package, we use original as a fallback solution when int64 is not enough (e.g. number is too big / small, too many precisions).

The core data struct is like following:

type Decimal struct {
	// represent decimal with 12 precision, 1.23 will have `fixed = 1_230_000_000_000`
	// max support decimal is 9_223_372.000_000_000_000
	// min support decimal is -9_223_372.000_000_000_000
	fixed int64

	// fallback to original decimal.Decimal if necessary
	fallback *decimal.Decimal
}

We pick 12 precisions because it could cover 99% of Alpaca common cases.

Compatibility

In general, alpacadecimal.Decimal is fully compatible with decimal.Decimal package, as decimal.Decimal is used as a fallback solution for overflow cases.

There are a few special cases / APIs that alpacadecimal.Decimal behaves different from decimal.Decimal (behaviour is still correct / valid, just different). Affected APIs:

  • Decimal.Exponent()
  • Decimal.Coefficient()
  • Decimal.CoefficientInt64()
  • Decimal.NumDigits()

For optimized case, alpacadecimal.Decimal always assume that exponent is 12, which results in a valid but different decimal representation. For example,

x := alpacadecimal.NewFromInt(123)
require.Equal(t, int32(-12), x.Exponent())
require.Equal(t, "123000000000000", x.Coefficient().String())
require.Equal(t, int64(123000000000000), x.CoefficientInt64())
require.Equal(t, 15, x.NumDigits())

y := decimal.NewFromInt(123)
require.Equal(t, int32(0), y.Exponent())
require.Equal(t, "123", y.Coefficient().String())
require.Equal(t, int64(123), y.CoefficientInt64())
require.Equal(t, 3, y.NumDigits())
  • big.NewInt optimization from here might help to speed up some big.Int related operations.
  • big.Int.String slowness is tracked by this issue. The approach we reduce this slowness is to use int64 to represent the number if possible to avoid big.Int operations.
Benchmark

Generally, for general case (99%), the speedup varies from 5x to 100x.

$ make bench
go test -bench=. --cpuprofile profile.out --memprofile memprofile.out
goos: darwin
goarch: amd64
pkg: github.com/alpacahq/alpacadecimal
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkValue/alpacadecimal.Decimal_Cached_Case-16             314870084                3.498 ns/op
BenchmarkValue/alpacadecimal.Decimal_Optimized_Case-16          15383466                70.27 ns/op
BenchmarkValue/alpacadecimal.Decimal_Fallback_Case-16            5603755               209.2 ns/op
BenchmarkValue/decimal.Decimal-16                                6167956               184.5 ns/op
BenchmarkValue/eric.Decimal-16                                   7021383               162.2 ns/op
BenchmarkAdd/alpacadecimal.Decimal-16                           556380649                2.132 ns/op
BenchmarkAdd/decimal.Decimal-16                                 15557970                68.31 ns/op
BenchmarkAdd/eric.Decimal-16                                    27423730                40.34 ns/op
BenchmarkSub/alpacadecimal.Decimal-16                           268269063                4.410 ns/op
BenchmarkSub/decimal.Decimal-16                                 17239782                59.17 ns/op
BenchmarkSub/eric.Decimal-16                                    24690660                40.81 ns/op
BenchmarkScan/alpacadecimal.Decimal-16                          87226915                13.46 ns/op
BenchmarkScan/decimal.Decimal-16                                 6075110               191.1 ns/op
BenchmarkScan/eric.Decimal-16                                    6422792               174.4 ns/op
BenchmarkMul/alpacadecimal.Decimal-16                           168732728                7.176 ns/op
BenchmarkMul/decimal.Decimal-16                                 16051546                66.57 ns/op
BenchmarkMul/eric.Decimal-16                                    39927952                28.20 ns/op
BenchmarkDiv/alpacadecimal.Decimal-16                           152054401                7.772 ns/op
BenchmarkDiv/decimal.Decimal-16                                  4098888               281.7 ns/op
BenchmarkDiv/eric.Decimal-16                                    34245668                31.42 ns/op
BenchmarkString/alpacadecimal.Decimal-16                        385985688                3.032 ns/op
BenchmarkString/decimal.Decimal-16                               7750777               150.9 ns/op
BenchmarkString/eric.Decimal-16                                  6694531               167.0 ns/op
BenchmarkRound/alpacadecimal.Decimal-16                         88814521                11.92 ns/op
BenchmarkRound/decimal.Decimal-16                                4333029               255.7 ns/op
BenchmarkRound/eric.Decimal-16                                  55717095                21.34 ns/op
PASS
ok      github.com/alpacahq/alpacadecimal       37.671s

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DivisionPrecision = decimal.DivisionPrecision

Variables

View Source
var ExpMaxIterations = decimal.ExpMaxIterations
View Source
var MarshalJSONWithoutQuotes = decimal.MarshalJSONWithoutQuotes
View Source
var Zero = Decimal{/* contains filtered or unexported fields */}

Functions

func RescalePair

func RescalePair(d1 Decimal, d2 Decimal) (Decimal, Decimal)

Types

type Decimal

type Decimal struct {
	// contains filtered or unexported fields
}

func Avg

func Avg(first Decimal, rest ...Decimal) Decimal

optimized: Avg returns the average value of the provided first and rest Decimals

func Max

func Max(first Decimal, rest ...Decimal) Decimal

optimized: Max returns the largest Decimal that was passed in the arguments.

func Min

func Min(first Decimal, rest ...Decimal) Decimal

optimized: Min returns the smallest Decimal that was passed in the arguments.

func New

func New(value int64, exp int32) Decimal

optimized: New returns a new fixed-point decimal, value * 10 ^ exp.

func NewFromBigInt

func NewFromBigInt(value *big.Int, exp int32) Decimal

fallback: NewFromBigInt returns a new Decimal from a big.Int, value * 10 ^ exp

func NewFromFloat

func NewFromFloat(f float64) Decimal

optimized: NewFromFloat converts a float64 to Decimal.

NOTE: this will panic on NaN, +/-inf

func NewFromFloat32

func NewFromFloat32(f float32) Decimal

fallback: NewFromFloat32 converts a float32 to Decimal.

The converted number will contain the number of significant digits that can be represented in a float with reliable roundtrip. This is typically 6-8 digits depending on the input. See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information.

For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms.

NOTE: this will panic on NaN, +/-inf

func NewFromFloatWithExponent

func NewFromFloatWithExponent(value float64, exp int32) Decimal

fallback: NewFromFloatWithExponent converts a float64 to Decimal, with an arbitrary number of fractional digits.

Example:

NewFromFloatWithExponent(123.456, -2).String() // output: "123.46"

func NewFromFormattedString

func NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, error)

fallback: NewFromFormattedString returns a new Decimal from a formatted string representation. The second argument - replRegexp, is a regular expression that is used to find characters that should be removed from given decimal string representation. All matched characters will be replaced with an empty string.

func NewFromInt

func NewFromInt(x int64) Decimal

optimized: NewFromInt converts a int64 to Decimal.

func NewFromInt32

func NewFromInt32(value int32) Decimal

optimized: NewFromInt32 converts a int32 to Decimal.

func NewFromString

func NewFromString(value string) (Decimal, error)

optimized: NewFromString returns a new Decimal from a string representation.

func RequireFromString

func RequireFromString(value string) Decimal

optimized: RequireFromString returns a new Decimal from a string representation or panics if NewFromString would have returned an error.

func Sum

func Sum(first Decimal, rest ...Decimal) Decimal

optimized: Sum returns the combined total of the provided first and rest Decimals

func (Decimal) Abs

func (d Decimal) Abs() Decimal

optimized: Abs returns the absolute value of the decimal.

func (Decimal) Add

func (d Decimal) Add(d2 Decimal) Decimal

optimized: Add returns d + d2.

func (Decimal) Atan

func (d Decimal) Atan() Decimal

fallback: Atan returns the arctangent, in radians, of x.

func (Decimal) BigFloat

func (d Decimal) BigFloat() *big.Float

fallback: BigFloat returns decimal as BigFloat.

func (Decimal) BigInt

func (d Decimal) BigInt() *big.Int

fallback: BigInt returns integer component of the decimal as a BigInt.

func (Decimal) Ceil

func (d Decimal) Ceil() Decimal

optimized: Ceil returns the nearest integer value greater than or equal to d.

func (Decimal) Cmp

func (d Decimal) Cmp(d2 Decimal) int

optimized: Cmp compares the numbers represented by d and d2 and returns:

-1 if d <  d2
 0 if d == d2
+1 if d >  d2

func (Decimal) Coefficient

func (d Decimal) Coefficient() *big.Int

optimized: Coefficient returns the coefficient of the decimal. It is scaled by 10^Exponent()

func (Decimal) CoefficientInt64

func (d Decimal) CoefficientInt64() int64

optimized: CoefficientInt64 returns the coefficient of the decimal as int64. It is scaled by 10^Exponent()

func (Decimal) Copy

func (d Decimal) Copy() Decimal

optimized: Copy returns a copy of decimal with the same value and exponent, but a different pointer to value.

func (Decimal) Cos

func (d Decimal) Cos() Decimal

fallback: Cos returns the cosine of the radian argument x.

func (Decimal) Div

func (d Decimal) Div(d2 Decimal) Decimal

optimized: Div returns d / d2. If it doesn't divide exactly, the result will have DivisionPrecision digits after the decimal point.

func (Decimal) DivRound

func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal

fallback: DivRound divides and rounds to a given precision

func (Decimal) Equal

func (d Decimal) Equal(d2 Decimal) bool

optimized: Equal returns whether the numbers represented by d and d2 are equal.

func (Decimal) Equals

func (d Decimal) Equals(d2 Decimal) bool

fallback: Equals is deprecated, please use Equal method instead

func (Decimal) ExpHullAbrham

func (d Decimal) ExpHullAbrham(overallPrecision uint32) (Decimal, error)

fallback: ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm. OverallPrecision argument specifies the overall precision of the result (integer part + decimal part).

func (Decimal) ExpTaylor

func (d Decimal) ExpTaylor(precision int32) (Decimal, error)

fallback: ExpTaylor calculates the natural exponent of decimal (e to the power of d) using Taylor series expansion. Precision argument specifies how precise the result must be (number of digits after decimal point). Negative precision is allowed.

func (Decimal) Exponent

func (d Decimal) Exponent() int32

optimized: Exponent returns the exponent, or scale component of the decimal.

func (Decimal) Float64

func (d Decimal) Float64() (f float64, exact bool)

fallback: Float64 returns the nearest float64 value for d and a bool indicating whether f represents d exactly.

func (Decimal) Floor

func (d Decimal) Floor() Decimal

optimized: Floor returns the nearest integer value less than or equal to d.

func (Decimal) GetFallback

func (d Decimal) GetFallback() *decimal.Decimal

func (Decimal) GetFixed

func (d Decimal) GetFixed() int64

Extra API to support get internal state. e.g. might be useful for flatbuffers encode / decode.

func (*Decimal) GobDecode

func (d *Decimal) GobDecode(data []byte) error

fallback: (can be optimized if needed)

func (Decimal) GobEncode

func (d Decimal) GobEncode() ([]byte, error)

fallback: (can be optimized if needed)

func (Decimal) GreaterThan

func (d Decimal) GreaterThan(d2 Decimal) bool

optimized: GreaterThan (GT) returns true when d is greater than d2.

func (Decimal) GreaterThanOrEqual

func (d Decimal) GreaterThanOrEqual(d2 Decimal) bool

optimized: GreaterThanOrEqual (GTE) returns true when d is greater than or equal to d2.

func (Decimal) InexactFloat64

func (d Decimal) InexactFloat64() float64

fallback: InexactFloat64 returns the nearest float64 value for d. It doesn't indicate if the returned value represents d exactly.

func (Decimal) IntPart

func (d Decimal) IntPart() int64

optimized: IntPart returns the integer component of the decimal.

func (Decimal) IsInteger

func (d Decimal) IsInteger() bool

optimized: IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false.

func (Decimal) IsNegative

func (d Decimal) IsNegative() bool

optimized: IsNegative return

true if d < 0
false if d == 0
false if d > 0

func (Decimal) IsOptimized

func (d Decimal) IsOptimized() bool

func (Decimal) IsPositive

func (d Decimal) IsPositive() bool

optimized: IsPositive return

true if d > 0
false if d == 0
false if d < 0

func (Decimal) IsZero

func (d Decimal) IsZero() bool

optimized: IsZero return

true if d == 0
false if d > 0
false if d < 0

func (Decimal) LessThan

func (d Decimal) LessThan(d2 Decimal) bool

optimized: LessThan (LT) returns true when d is less than d2.

func (Decimal) LessThanOrEqual

func (d Decimal) LessThanOrEqual(d2 Decimal) bool

optimized: LessThanOrEqual (LTE) returns true when d is less than or equal to d2.

func (Decimal) MarshalBinary

func (d Decimal) MarshalBinary() (data []byte, err error)

fallback: MarshalBinary implements the encoding.BinaryMarshaler interface.

func (Decimal) MarshalJSON

func (d Decimal) MarshalJSON() ([]byte, error)

optimized:

func (Decimal) MarshalText

func (d Decimal) MarshalText() (text []byte, err error)

optimized:

func (Decimal) Mod

func (d Decimal) Mod(d2 Decimal) Decimal

func (Decimal) Mul

func (d Decimal) Mul(d2 Decimal) Decimal

optimized: Mul returns d * d2

func (Decimal) Neg

func (d Decimal) Neg() Decimal

optimized: Neg returns -d

func (Decimal) NumDigits

func (d Decimal) NumDigits() int

fallback: NumDigits returns the number of digits of the decimal coefficient (d.Value)

func (Decimal) Pow

func (d Decimal) Pow(d2 Decimal) Decimal

fallback: Pow returns d to the power d2

func (Decimal) QuoRem

func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal)

fallback: QuoRem does divsion with remainder

func (Decimal) Rat

func (d Decimal) Rat() *big.Rat

fallback: Rat returns a rational number representation of the decimal.

func (Decimal) Round

func (d Decimal) Round(places int32) Decimal

optimized: Round rounds the decimal to places decimal places. If places < 0, it will round the integer part to the nearest 10^(-places).

func (Decimal) RoundBank

func (d Decimal) RoundBank(places int32) Decimal

fallback: RoundBank rounds the decimal to places decimal places. If the final digit to round is equidistant from the nearest two integers the rounded value is taken as the even number

If places < 0, it will round the integer part to the nearest 10^(-places).

func (Decimal) RoundCash

func (d Decimal) RoundCash(interval uint8) Decimal

fallback: RoundCash aka Cash/Penny/öre rounding rounds decimal to a specific interval. The amount payable for a cash transaction is rounded to the nearest multiple of the minimum currency unit available. The following intervals are available: 5, 10, 25, 50 and 100; any other number throws a panic.

  5:   5 cent rounding 3.43 => 3.45
 10:  10 cent rounding 3.45 => 3.50 (5 gets rounded up)
 25:  25 cent rounding 3.41 => 3.50
 50:  50 cent rounding 3.75 => 4.00
100: 100 cent rounding 3.50 => 4.00

For more details: https://en.wikipedia.org/wiki/Cash_rounding

func (Decimal) RoundCeil

func (d Decimal) RoundCeil(places int32) Decimal

fallback: RoundCeil rounds the decimal towards +infinity.

Example:

NewFromFloat(545).RoundCeil(-2).String()   // output: "600"
NewFromFloat(500).RoundCeil(-2).String()   // output: "500"
NewFromFloat(1.1001).RoundCeil(2).String() // output: "1.11"
NewFromFloat(-1.454).RoundCeil(1).String() // output: "-1.5"

func (Decimal) RoundDown

func (d Decimal) RoundDown(places int32) Decimal

fallback: RoundDown rounds the decimal towards zero.

Example:

NewFromFloat(545).RoundDown(-2).String()   // output: "500"
NewFromFloat(-500).RoundDown(-2).String()   // output: "-500"
NewFromFloat(1.1001).RoundDown(2).String() // output: "1.1"
NewFromFloat(-1.454).RoundDown(1).String() // output: "-1.5"

func (Decimal) RoundFloor

func (d Decimal) RoundFloor(places int32) Decimal

fallback: RoundFloor rounds the decimal towards -infinity.

Example:

NewFromFloat(545).RoundFloor(-2).String()   // output: "500"
NewFromFloat(-500).RoundFloor(-2).String()   // output: "-500"
NewFromFloat(1.1001).RoundFloor(2).String() // output: "1.1"
NewFromFloat(-1.454).RoundFloor(1).String() // output: "-1.4"

func (Decimal) RoundUp

func (d Decimal) RoundUp(places int32) Decimal

fallback: RoundUp rounds the decimal away from zero.

Example:

NewFromFloat(545).RoundUp(-2).String()   // output: "600"
NewFromFloat(500).RoundUp(-2).String()   // output: "500"
NewFromFloat(1.1001).RoundUp(2).String() // output: "1.11"
NewFromFloat(-1.454).RoundUp(1).String() // output: "-1.4"

func (*Decimal) Scan

func (d *Decimal) Scan(value interface{}) error

optimized: sql.Scanner interface

func (Decimal) Shift

func (d Decimal) Shift(shift int32) Decimal

fallback: Binary shift left (k > 0) or right (k < 0).

func (Decimal) Sign

func (d Decimal) Sign() int

optimized: Sign returns:

-1 if d <  0
 0 if d == 0
+1 if d >  0

func (Decimal) Sin

func (d Decimal) Sin() Decimal

fallback: Sin returns the sine of the radian argument x.

func (Decimal) String

func (d Decimal) String() string

optimized: String returns the string representation of the decimal with the fixed point.

func (Decimal) StringFixed

func (d Decimal) StringFixed(places int32) string

fallback: StringFixed returns a rounded fixed-point string with places digits after the decimal point.

func (Decimal) StringFixedBank

func (d Decimal) StringFixedBank(places int32) string

fallback: StringFixedBank returns a banker rounded fixed-point string with places digits after the decimal point.

func (Decimal) StringFixedCash

func (d Decimal) StringFixedCash(interval uint8) string

fallback: StringFixedCash returns a Swedish/Cash rounded fixed-point string. For more details see the documentation at function RoundCash.

func (Decimal) StringScaled

func (d Decimal) StringScaled(exp int32) string

fallback: DEPRECATED! Use StringFixed instead.

func (Decimal) Sub

func (d Decimal) Sub(d2 Decimal) Decimal

optimized: Sub returns d - d2.

func (Decimal) Tan

func (d Decimal) Tan() Decimal

fallback: Tan returns the tangent of the radian argument x.

func (Decimal) Truncate

func (d Decimal) Truncate(precision int32) Decimal

optimized: Truncate truncates off digits from the number, without rounding.

func (*Decimal) UnmarshalBinary

func (d *Decimal) UnmarshalBinary(data []byte) error

fallback: UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. As a string representation is already used when encoding to text, this method stores that string as []byte

func (*Decimal) UnmarshalJSON

func (d *Decimal) UnmarshalJSON(decimalBytes []byte) error

optimized: UnmarshalJSON implements the json.Unmarshaler interface.

func (*Decimal) UnmarshalText

func (d *Decimal) UnmarshalText(text []byte) error

optimized: UnmarshalText implements the encoding.TextUnmarshaler interface for XML deserialization.

func (Decimal) Value

func (d Decimal) Value() (driver.Value, error)

optimized: sql.Valuer interface

type NullDecimal

type NullDecimal struct {
	Decimal Decimal
	Valid   bool
}

NullDecimal support

func NewNullDecimal

func NewNullDecimal(d Decimal) NullDecimal

func (NullDecimal) MarshalJSON

func (d NullDecimal) MarshalJSON() ([]byte, error)

func (NullDecimal) MarshalText

func (d NullDecimal) MarshalText() (text []byte, err error)

func (*NullDecimal) Scan

func (d *NullDecimal) Scan(value interface{}) error

func (*NullDecimal) UnmarshalJSON

func (d *NullDecimal) UnmarshalJSON(decimalBytes []byte) error

func (*NullDecimal) UnmarshalText

func (d *NullDecimal) UnmarshalText(text []byte) error

func (NullDecimal) Value

func (d NullDecimal) Value() (driver.Value, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL