alpacadecimal

package module
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2024 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
	ExpMaxIterations         = decimal.ExpMaxIterations
	MarshalJSONWithoutQuotes = decimal.MarshalJSONWithoutQuotes
	Zero                     = Decimal{/* contains filtered or unexported fields */}
)

Variables

Functions

func RescalePair added in v1.3.2

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 added in v1.1.0

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 added in v1.3.2

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 added in v1.3.2

func NewFromInt(x int64) Decimal

optimized: NewFromInt converts a int64 to Decimal.

func NewFromInt32 added in v1.3.2

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 added in v1.0.1

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 added in v1.1.0

func (d Decimal) Atan() Decimal

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

func (Decimal) BigFloat added in v1.3.2

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

fallback: BigFloat returns decimal as BigFloat.

func (Decimal) BigInt added in v1.3.2

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 added in v1.3.2

func (d Decimal) CoefficientInt64() int64

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

func (Decimal) Copy added in v1.3.2

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 added in v1.1.0

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 added in v1.3.2

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 added in v1.3.2

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 added in v1.4.0

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

func (Decimal) GetFixed added in v1.4.0

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 added in v1.3.2

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 added in v1.3.2

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 added in v1.1.0

func (d Decimal) IsNegative() bool

optimized: IsNegative return

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

func (Decimal) IsOptimized added in v1.4.0

func (d Decimal) IsOptimized() bool

func (Decimal) IsPositive added in v1.1.0

func (d Decimal) IsPositive() bool

optimized: IsPositive return

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

func (Decimal) IsZero added in v1.1.0

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 added in v1.3.2

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 added in v1.3.2

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 added in v1.3.2

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 added in v1.3.2

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 added in v1.3.2

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 added in v1.1.0

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 added in v1.1.0

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 added in v1.1.0

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 added in v1.3.2

func NewNullDecimal(d Decimal) NullDecimal

func (NullDecimal) MarshalJSON

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

func (NullDecimal) MarshalText added in v1.3.2

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 added in v1.3.2

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