udecimal

package module
v1.7.0 Latest Latest
Warning

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

Go to latest
Published: Jan 4, 2025 License: BSD-3-Clause Imports: 13 Imported by: 4

README

udecimal

build Go Report Card codecov GoDoc Awesome Go

High performance, high precision, zero allocation fixed-point decimal number for financial applications.

Installation

go get github.com/quagmt/udecimal

Features

  • High Precision: Supports up to 19 decimal places with no precision loss during arithmetic operations.
  • Optimized for Speed: Designed for high performance with zero memory allocation in most cases (see Benchmarks and How it works).
  • Panic-Free: All errors are returned as values, ensuring no unexpected panics.
  • Immutable: All arithmetic operations return a new Decimal value, preserving the original value and safe for concurrent use.
  • Versatile Rounding Methods: Includes HALF AWAY FROM ZERO, HALF TOWARD ZERO, and Banker's rounding.
  • Correctness: All arithmetic operations are fuzz tested and cross-checked with shopspring/decimal library to ensure correctness.

NOTE: This library does not perform implicit rounding. If the result of an operation exceeds the maximum precision, extra digits are truncated. All rounding methods must be explicitly invoked. (see Rounding Methods for more details)

Documentation

Usage

package main

import (
	"fmt"

	"github.com/quagmt/udecimal"
)

func main() {
	// Create a new decimal number
	a, _ := udecimal.NewFromInt64(123456, 3)              // a = 123.456
	b, _ := udecimal.NewFromInt64(-123456, 4)             // b = -12.3456
	c, _ := udecimal.NewFromFloat64(1.2345)               // c = 1.2345
	d, _ := udecimal.Parse("4123547.1234567890123456789") // d = 4123547.1234567890123456789

	// Basic arithmetic operations
	fmt.Println(a.Add(b)) // 123.456 - 12.3456 = 111.1104
	fmt.Println(a.Sub(b)) // 123.456 + 12.3456 = 135.8016
	fmt.Println(a.Mul(b)) // 123.456 * -12.3456 = -1524.1383936
	fmt.Println(a.Div(b)) // 123.456 / -12.3456 = -10
	fmt.Println(a.Div(d)) // 123.456 / 4123547.1234567890123456789 = 0.0000299392722585176

	// Rounding
	fmt.Println(c.RoundBank(3))         // banker's rounding: 1.2345 -> 1.234
	fmt.Println(c.RoundAwayFromZero(2)) // round away from zero: 1.2345 -> 1.24
	fmt.Println(c.RoundHAZ(3))          // half away from zero: 1.2345 -> 1.235
	fmt.Println(c.RoundHTZ(3))          // half towards zero: 1.2345 -> 1.234
	fmt.Println(c.Trunc(2))             // truncate: 1.2345 -> 1.23
	fmt.Println(c.Floor())              // floor: 1.2345 -> 1
	fmt.Println(c.Ceil())               // ceil: 1.2345 -> 2

	// Display
	fmt.Println(a.String())         // 123.456
	fmt.Println(a.StringFixed(10))  // 123.4560000000
	fmt.Println(a.InexactFloat64()) // 123.456
}

Why another decimal library?

There are already a couple of decimal libraries available in Go, such as shopspring/decimal, cockroachdb/apd, govalues/decimal, etc. However, each of these libraries has its own limitations, for example:

  • shopspring/decimal is great for general-purpose decimal arithmetic because of arbitrary precision. However, it's slow and requires memory allocation for every arithmetic operation. Also in financial applications, arbitrary precision is not always necessary.
  • cockroachdb/apd is faster but still requires memory allocation. Also the API is not very intuitive.
  • govalues/decimal is fast, no memory allocation, easy to use but the data range is only limited to 19 digits (include both the integer and fractional parts). Some operations (especially Quo/QuoRem) usually overflow and fallback to use big.Int API, which hurts the performance. Another limitation is that it starts losing precision when the total number of digits exceeds 19.

This library is designed to address these limitations, providing both high performance and zero allocation while maintaining an acceptable range of precision, which is suitable for most financial applications.

Rounding Methods

Rounding numbers can often be challenging and confusing due to the variety of methods available. Each method serves specific purposes, and it's common for developers to make mistakes or incorrect assumptions about how rounding should be performed. For example, the result of round(1.45) could be either 1.4 or 1.5, depending on the rounding method used.

This issue is particularly critical in financial applications, where even minor rounding errors can accumulate and lead to significant financial losses. To mitigate such errors, this library intentionally avoids implicit rounding. If the result of an operation exceeds the maximum precision specified by developers beforehand, extra digits are truncated. Developers need to explicitly choose the rounding method they want to use. The supported rounding methods are:

Examples:
package main

import (
	"fmt"

	"github.com/quagmt/udecimal"
)

func main() {
	// Create a new decimal number
	a, _ := udecimal.NewFromFloat64(1.345) // a = 1.345

	// Rounding
	fmt.Println(a.RoundBank(2))         // banker's rounding: 1.345 -> 1.34
	fmt.Println(a.RoundAwayFromZero(2)) // round away from zero: 1.345 -> 1.35
	fmt.Println(a.RoundHAZ(2))          // half away from zero: 1.345 -> 1.35
	fmt.Println(a.RoundHTZ(2))          // half towards zero: 1.345 -> 1.34
}

How it works

As mentioned above, this library is not always memory allocation free. However, those cases where we need to allocate memory are incredibly rare. To understand why, let's take a look at how the Decimal type is implemented.

The Decimal type represents a fixed-point decimal number. It consists of three components: sign, coefficient, and prec. The number is represented as:

// decimal value = (neg == true ? -1 : 1) * coef * 10^(-prec)
type Decimal struct {
	coef bint
	neg bool
	prec uint8 // 0 <= prec <= 19
}

// Example:
// 123.456 = 123456 * 10^-3
// -> neg = false, coef = 123456, prec = 3

// -123.456 = -123456 / 10^-3
// -> neg = true, coef = 123456, prec = 3

You can notice that coef data type is bint, which is a custom data type:

type bint struct {
	// For coefficients exceeding u128
	bigInt *big.Int

	// For coefficients less than 2^128
	u128 u128
}

The bint type can store coefficients up to 2^128 - 1 using u128. Arithmetic operations with u128 are fast and require no memory allocation. If result of an arithmetic operation exceeds u128 capacity, the whole operation will be performed using big.Int API. Such operations are slower and do involve memory allocation. However, those cases are rare in financial applications due to the extensive range provided by a 128-bit unsigned integer, for example:

  • If precision is 0, the decimal range it can store is: [-340282366920938463463374607431768211455, 340282366920938463463374607431768211455](approximately -340 to 340 undecillion)

  • If precision is 19, the decimal range becomes: [-34028236692093846346.3374607431768211455, 34028236692093846346.3374607431768211455] (approximately -34 to 34 quintillion)

Therefore, in most cases you can expect high performance and no memory allocation when using this library.

Credits

This library is inspired by these repositories:

Documentation

Overview

Package udecimal provides a high-performance, high-precision (up to 19 digits after the decimal point) decimal arithmetic library. It includes functions for parsing and performing arithmetic operations such as addition, subtraction, multiplication, and division on decimal numbers. The package is designed to handle decimal numbers with a high degree of precision and efficiency, making it suitable for high-traffic financial applications where both precision and performance are critical.

Maximum and default precision is 19 digits after the decimal point. The default precision can be changed globally to any value between 1 and 19 to suit your use case and make sure that the precision is consistent across the entire application. See SetDefaultPrecision for more details.

Codec

The udecimal package supports various encoding and decoding mechanisms to facilitate easy integration with different data storage and transmission systems.

  • Marshal/UnmarshalJSON
  • Marshal/UnmarshalBinary: gob, protobuf
  • SQL: The Decimal type implements the sql.Scanner interface, enabling seamless integration with SQL databases.

For more details, see the documentation for each method.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (

	// ErrPrecOutOfRange is returned when the decimal precision is greater than the default precision
	// default precision can be configured using SetDefaultPrecision, and its value is up to 19
	ErrPrecOutOfRange = fmt.Errorf("precision out of range. Only support maximum %d digits after the decimal point", defaultPrec)

	// ErrEmptyString is returned when the input string is empty
	ErrEmptyString = fmt.Errorf("can't parse empty string")

	// ErrMaxStrLen is returned when the input string exceeds the maximum length
	// Maximum length is arbitrarily set to 200 so string length value can fit in 1 byte (for MarshalBinary).
	// Also such that big number (more than 200 digits) is unrealistic in financial system
	// which this library is mainly designed for.
	ErrMaxStrLen = fmt.Errorf("string input exceeds maximum length %d", maxStrLen)

	// ErrInvalidFormat is returned when the input string is not in the correct format
	// It doesn't support scientific notation, such as 1e-2, 1.23e4, etc.
	ErrInvalidFormat = fmt.Errorf("invalid format")

	// ErrDivideByZero is returned when dividing by zero
	ErrDivideByZero = fmt.Errorf("can't divide by zero")

	// ErrSqrtNegative is returned when calculating square root of negative number
	ErrSqrtNegative = fmt.Errorf("can't calculate square root of negative number")

	// ErrInvalidBinaryData is returned when unmarshalling invalid binary data
	// The binary data should follow the format as described in MarshalBinary
	ErrInvalidBinaryData = fmt.Errorf("invalid binary data")

	// ErrZeroPowNegative is returned when raising zero to a negative power
	ErrZeroPowNegative = fmt.Errorf("can't raise zero to a negative power")

	// ErrExponentTooLarge is returned when the exponent is too large and becomes impractical.
	ErrExponentTooLarge = fmt.Errorf("exponent is too large. Must be less than or equal math.MaxInt32")

	// ErrIntPartOverflow is returned when the integer part of the decimal is too large to fit in int64
	ErrIntPartOverflow = fmt.Errorf("integer part is too large to fit in int64")
)
View Source
var (
	Zero = Decimal{}
	One  = MustFromInt64(1, 0)
)

Functions

func SetDefaultPrecision

func SetDefaultPrecision(prec uint8)

SetDefaultPrecision changes the default precision for decimal numbers in the package. Max precision is 19 and is also default.

This function is particularly useful when you want to have your precision of the deicmal smaller than 19 across the whole application. It should be called only once at the beginning of your application

Panics if the new precision is greater than 19 (maxPrec) or new precision is 0

Example
SetDefaultPrecision(10)
defer SetDefaultPrecision(19)

a := MustParse("1.23")
b := MustParse("4.12475")
c, _ := a.Div(b)
fmt.Println(c)
Output:

0.2981998909

Types

type Decimal

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

Decimal represents a fixed-point decimal number. The number is represented as a struct with three fields: coef, neg, and prec.

  • coef: the coefficient of the decimal number
  • neg: true if the number is negative
  • prec: the number of digits after the decimal point (0 to 19)

Decimal numbers are immutable and can be used in arithmetic operations such as addition, subtraction, multiplication, and division.

func Max added in v1.7.0

func Max(a Decimal, b ...Decimal) Decimal

Max returns the maximum decimal from the list of decimals.

Example
fmt.Println(Max(MustParse("1.23"), MustParse("4.12475")))
Output:

4.12475

func Min added in v1.7.0

func Min(a Decimal, b ...Decimal) Decimal
Example
fmt.Println(Min(MustParse("1.23"), MustParse("4.12475")))
Output:

1.23

func MustFromFloat64

func MustFromFloat64(f float64) Decimal

MustFromFloat64 similars to NewFromFloat64, but panics instead of returning error

Example
fmt.Println(MustFromFloat64(1.234))

// cautious: result will lose some precision when converting to decimal
fmt.Println(MustFromFloat64(123456789.1234567890123456789))
Output:

1.234
123456789.12345679

func MustFromInt64

func MustFromInt64(coef int64, prec uint8) Decimal

MustFromInt64 similars to NewFromInt64, but panics instead of returning error

Example
fmt.Println(MustFromInt64(123, 3))
fmt.Println(MustFromInt64(-12345, 2))
Output:

0.123
-123.45

func MustFromUint64

func MustFromUint64(coef uint64, prec uint8) Decimal

MustFromUint64 similars to NewFromUint64, but panics instead of returning error

Example
fmt.Println(MustFromUint64(123, 3))
fmt.Println(MustFromUint64(12345, 2))
Output:

0.123
123.45

func MustParse

func MustParse(s string) Decimal

MustParse similars to Parse, but pacnis instead of returning error.

Example
fmt.Println(MustParse("1234567890123456789.1234567890123456789"))
fmt.Println(MustParse("-1234567890123456789.1234567890123456789"))
fmt.Println(MustParse("-0.00007890123456789"))
Output:

1234567890123456789.1234567890123456789
-1234567890123456789.1234567890123456789
-0.00007890123456789

func NewFromFloat64

func NewFromFloat64(f float64) (Decimal, error)

NewFromFloat64 returns a decimal from float64.

**NOTE**: you'll expect to lose some precision for this method due to FormatFloat. See: https://github.com/golang/go/issues/29491

This method is only suitable for small numbers with low precision. e.g. 1.0001, 0.0001, -123.456, -1000000.123456. You should avoid using this method if your input number has high precision.

Returns error when:

  1. f is NaN or Inf
  2. error when parsing float to string and then to decimal
Example
fmt.Println(NewFromFloat64(1.234))

// cautious: result will lose some precision when converting to decimal
fmt.Println(NewFromFloat64(123456789.1234567890123456789))
Output:

1.234 <nil>
123456789.12345679 <nil>

func NewFromHiLo

func NewFromHiLo(neg bool, hi uint64, lo uint64, prec uint8) (Decimal, error)

NewFromHiLo returns a decimal from 128-bit unsigned integer (hi,lo)

Example
fmt.Println(NewFromHiLo(false, 1, 1, 10))
fmt.Println(NewFromHiLo(true, 0, 123456, 4))
Output:

1844674407.3709551617 <nil>
-12.3456 <nil>

func NewFromInt64

func NewFromInt64(coef int64, prec uint8) (Decimal, error)

NewFromInt64 returns a decimal which equals to coef / 10^prec and coef is an int64. Trailing zeros wll be removed and the prec will also be adjusted

Example
fmt.Println(NewFromInt64(123, 3))
fmt.Println(NewFromInt64(12345, 20))
Output:

0.123 <nil>
0 precision out of range. Only support maximum 19 digits after the decimal point

func NewFromUint64

func NewFromUint64(coef uint64, prec uint8) (Decimal, error)

NewFromUint64 returns a decimal which equals to coef / 10^prec and coef is an uint64 Trailing zeros wll be removed and the prec will also be adjusted

Example
fmt.Println(NewFromUint64(123, 3))
fmt.Println(NewFromUint64(12345, 2))
fmt.Println(NewFromUint64(12345, 20))
Output:

0.123 <nil>
123.45 <nil>
0 precision out of range. Only support maximum 19 digits after the decimal point

func Parse

func Parse(s string) (Decimal, error)

Parse parses a number in string to a decimal. The string must be in the format of: [+-]d{1,19}[.d{1,19}]

Returns error if:

  1. empty/invalid string
  2. the number has more than 19 digits after the decimal point
  3. string length exceeds maxStrLen (which is 200 characters. See ErrMaxStrLen for more details)
Example
fmt.Println(Parse("1234567890123456789.1234567890123456789"))
fmt.Println(Parse("-1234567890123456789.1234567890123456789"))
fmt.Println(Parse("-0.00007890123456789"))

// error cases
fmt.Println(Parse("0.12345678901234567890123"))
fmt.Println(Parse(""))
fmt.Println(Parse("1.123.123"))
Output:

1234567890123456789.1234567890123456789 <nil>
-1234567890123456789.1234567890123456789 <nil>
-0.00007890123456789 <nil>
0 precision out of range. Only support maximum 19 digits after the decimal point
0 can't parse empty string
0 invalid format: can't parse '1.123.123'

func (Decimal) Abs

func (d Decimal) Abs() Decimal

Abs returns |d|

Example
fmt.Println(MustParse("-123.45").Abs())
Output:

123.45

func (Decimal) Add

func (d Decimal) Add(e Decimal) Decimal

Add returns d + e

Example
a := MustParse("1.23")
b := MustParse("4.12475")
c := a.Add(b)
fmt.Println(c)
Output:

5.35475

func (Decimal) Add64

func (d Decimal) Add64(e uint64) Decimal

Add64 returns d + e where e is a uint64

Example
a := MustParse("1.23")
c := a.Add64(4)
fmt.Println(c)
Output:

5.23

func (Decimal) Ceil

func (d Decimal) Ceil() Decimal

Ceil returns the smallest integer value greater than or equal to d.

Example
fmt.Println(MustParse("1.23").Ceil())
Output:

2

func (Decimal) Cmp

func (d Decimal) Cmp(e Decimal) int

Cmp compares two decimals d,e and returns:

-1 if d < e
 0 if d == e
+1 if d > e
Example
fmt.Println(MustParse("1.23").Cmp(MustParse("4.12475")))
fmt.Println(MustParse("1.23").Cmp(MustParse("1.23")))
fmt.Println(MustParse("1.23").Cmp(MustParse("0.12475")))
Output:

-1
0
1

func (Decimal) Div

func (d Decimal) Div(e Decimal) (Decimal, error)

Div returns d / e. If the result has more than defaultPrec fraction digits, it will be truncated to defaultPrec digits.

Returns divide by zero error when e is zero

Example
fmt.Println(MustParse("1.23").Div(MustParse("4.12475")))
fmt.Println(MustParse("1.23").Div(MustParse("0")))
Output:

0.2981998909024789381 <nil>
0 can't divide by zero

func (Decimal) Div64

func (d Decimal) Div64(v uint64) (Decimal, error)

Div64 returns d / e where e is a uint64. If the result has more than defaultPrec fraction digits, it will be truncated to defaultPrec digits.

Returns divide by zero error when e is zero

Example
fmt.Println(MustParse("1.23").Div64(4))
fmt.Println(MustParse("1.23").Div64(0))
Output:

0.3075 <nil>
0 can't divide by zero

func (Decimal) Equal added in v1.2.0

func (d Decimal) Equal(e Decimal) bool

Equal reports whether the two decimals d and e are equal.

Example
fmt.Println(MustParse("1.123").Equal(MustParse("-1.123")))
fmt.Println(MustParse("1.123").Equal(MustParse("1.1230000")))
Output:

false
true

func (Decimal) Floor

func (d Decimal) Floor() Decimal

Floor returns the largest integer value less than or equal to d.

Example
fmt.Println(MustParse("1.23").Floor())
fmt.Println(MustParse("-1.23").Floor())
Output:

1
-2

func (Decimal) GreaterThan added in v1.7.0

func (d Decimal) GreaterThan(e Decimal) bool

GreaterThan reports whether d > e.

Example
fmt.Println(MustParse("1.23").GreaterThan(MustParse("4.12475")))
fmt.Println(MustParse("1.23").GreaterThan(MustParse("1.23")))
fmt.Println(MustParse("1.23").GreaterThan(MustParse("0.12475")))
Output:

false
false
true

func (Decimal) GreaterThanOrEqual added in v1.7.0

func (d Decimal) GreaterThanOrEqual(e Decimal) bool

GreaterThanOrEqual reports whether d >= e.

Example
fmt.Println(MustParse("1.23").GreaterThanOrEqual(MustParse("4.12475")))
fmt.Println(MustParse("1.23").GreaterThanOrEqual(MustParse("1.23")))
fmt.Println(MustParse("1.23").GreaterThanOrEqual(MustParse("0.12475")))
Output:

false
true
true

func (Decimal) InexactFloat64

func (d Decimal) InexactFloat64() float64

InexactFloat64 returns the float64 representation of the decimal. The result may not be 100% accurate due to the limitation of float64 (less decimal precision).

Caution: this method will not return the exact number if the decimal is too large.

e.g. 123456789012345678901234567890123456789.9999999999999999999 -> 123456789012345680000000000000000000000
Example
fmt.Println(MustParse("1.23").InexactFloat64())
fmt.Println(MustParse("123456789.123456789").InexactFloat64())
Output:

1.23
1.2345678912345679e+08

func (Decimal) Int64 added in v1.6.0

func (d Decimal) Int64() (int64, error)

Int64 returns the integer part of the decimal. Return error if the decimal is too large to fit in int64.

Example
fmt.Println(MustParse("1.23").Int64())
fmt.Println(MustParse("1234567890123456789.1234567890123456789").Int64())
fmt.Println(MustParse("-1234567890123456789.1234567890123456789").Int64())
fmt.Println(MustParse("0.123").Int64())
fmt.Println(MustParse("-0.123").Int64())
fmt.Println(MustParse("12345678901234567890123456.123456").Int64())
Output:

1 <nil>
1234567890123456789 <nil>
-1234567890123456789 <nil>
0 <nil>
0 <nil>
0 integer part is too large to fit in int64

func (Decimal) IsNeg

func (d Decimal) IsNeg() bool

IsNeg returns

true if d < 0
false if d >= 0
Example
fmt.Println(MustParse("1.23").IsNeg())
fmt.Println(MustParse("-1.23").IsNeg())
fmt.Println(MustParse("0").IsNeg())
Output:

false
true
false

func (Decimal) IsPos

func (d Decimal) IsPos() bool

IsPos returns

true if d > 0
false if d <= 0
Example
fmt.Println(MustParse("1.23").IsPos())
fmt.Println(MustParse("-1.23").IsPos())
fmt.Println(MustParse("0").IsPos())
Output:

true
false
false

func (Decimal) IsZero

func (d Decimal) IsZero() bool

IsZero returns

true if d == 0
false if d != 0
Example
fmt.Println(MustParse("1.23").IsZero())
fmt.Println(MustParse("0").IsZero())
Output:

false
true

func (Decimal) LessThan added in v1.7.0

func (d Decimal) LessThan(e Decimal) bool

LessThan reports whether d < e.

Example
fmt.Println(MustParse("1.23").LessThan(MustParse("4.12475")))
fmt.Println(MustParse("1.23").LessThan(MustParse("1.23")))
fmt.Println(MustParse("1.23").LessThan(MustParse("0.12475")))
Output:

true
false
false

func (Decimal) LessThanOrEqual added in v1.7.0

func (d Decimal) LessThanOrEqual(e Decimal) bool

LessThanOrEqual reports whether d <= e.

Example
fmt.Println(MustParse("1.23").LessThanOrEqual(MustParse("4.12475")))
fmt.Println(MustParse("1.23").LessThanOrEqual(MustParse("1.23")))
fmt.Println(MustParse("1.23").LessThanOrEqual(MustParse("0.12475")))
Output:

true
true
false

func (Decimal) MarshalBinary

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

MarshalBinary implements encoding.BinaryMarshaler interface with custom binary format.

Binary format: [overflow + neg] [prec] [total bytes] [coef]

 example 1: -1.2345
 1st byte: 0b0001_0000 (overflow = true, neg = false)
 2nd byte: 0b0000_0100 (prec = 4)
 3rd byte: 0b0000_1101 (total bytes = 11)
 4th-11th bytes: 0x0000_0000_0000_3039 (coef = 12345, only stores the coef.lo part)

 example 2: 1234567890123456789.1234567890123456789
 1st byte: 0b0000_0000 (overflow = false, neg = false)
 2nd byte: 0b0001_0011 (prec = 19)
 3rd byte: 0b0001_0011 (total bytes = 19)
 4th-11th bytes: 0x0949_b0f6_f002_3313 (coef.hi)
 12th-19th bytes: 0xd3b5_05f9_b5f1_8115 (coef.lo)
Example
fmt.Println(MustParse("1.23").MarshalBinary())
fmt.Println(MustParse("-1.2345").MarshalBinary())
fmt.Println(MustParse("1234567890123456789.1234567890123456789").MarshalBinary())
Output:

[0 2 11 0 0 0 0 0 0 0 123] <nil>
[1 4 11 0 0 0 0 0 0 48 57] <nil>
[0 19 19 9 73 176 246 240 2 51 19 211 181 5 249 181 241 129 21] <nil>

func (Decimal) MarshalJSON

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

MarshalJSON implements the json.Marshaler interface.

Example
a, _ := MustParse("1.23").MarshalJSON()
b, _ := MustParse("-1.2345").MarshalJSON()
c, _ := MustParse("1234567890123456789.1234567890123456789").MarshalJSON()
fmt.Println(string(a))
fmt.Println(string(b))
fmt.Println(string(c))
Output:

"1.23"
"-1.2345"
"1234567890123456789.1234567890123456789"

func (Decimal) MarshalText added in v1.2.0

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

MarshalText implements the encoding.TextMarshaler interface.

Example
a, _ := MustParse("1.23").MarshalText()
b, _ := MustParse("-1.2345").MarshalText()
c, _ := MustParse("1234567890123456789.1234567890123456789").MarshalText()
fmt.Println(string(a))
fmt.Println(string(b))
fmt.Println(string(c))
Output:

1.23
-1.2345
1234567890123456789.1234567890123456789

func (Decimal) Mod added in v1.6.0

func (d Decimal) Mod(e Decimal) (Decimal, error)

Mod is similar to Decimal.QuoRem but only returns the remainder

Example
fmt.Println(MustParse("1.23").Mod(MustParse("0.5")))
fmt.Println(MustParse("1.23").Mod(MustParse("0")))
Output:

0.23 <nil>
0 can't divide by zero

func (Decimal) Mul

func (d Decimal) Mul(e Decimal) Decimal

Mul returns d * e. The result will have at most defaultPrec digits after the decimal point.

Example
a := MustParse("1.23")
b := MustParse("4.12475")
c := a.Mul(b)
fmt.Println(c)
Output:

5.0734425

func (Decimal) Mul64

func (d Decimal) Mul64(v uint64) Decimal

Mul64 returns d * e where e is a uint64. The result will have at most defaultPrec digits after the decimal point.

Example
a := MustParse("1.23")
c := a.Mul64(4)
fmt.Println(c)
Output:

4.92

func (Decimal) Neg

func (d Decimal) Neg() Decimal

Neg returns -d

Example
fmt.Println(MustParse("1.23").Neg())
fmt.Println(MustParse("-1.23").Neg())
Output:

-1.23
1.23

func (Decimal) PowInt deprecated

func (d Decimal) PowInt(e int) Decimal

Deprecated: Use [PowInt32] instead for correct handling of 0^0 and negative exponents. This function treats 0 raised to any power as 0, which may not align with mathematical conventions but is practical in certain cases. See: https://github.com/quagmt/udecimal/issues/25.

PowInt raises the decimal d to the integer power e (d^e).

Special cases:

  • 0^e = 0 for any integer e
  • d^0 = 1 for any decimal d ≠ 0

Examples:

PowInt(0, 0)    = 0
PowInt(0, 1)    = 0
PowInt(0, -1)   = 0
PowInt(2, 0)    = 1
PowInt(2.5, 2)  = 6.25
PowInt(2.5, -2) = 0.16
Example
fmt.Println(MustParse("1.23").PowInt(2))
fmt.Println(MustParse("1.23").PowInt(0))
fmt.Println(MustParse("1.23").PowInt(-2))
Output:

1.5129
1
0.6609822195782933439

func (Decimal) PowInt32 added in v1.6.0

func (d Decimal) PowInt32(e int32) (Decimal, error)

PowInt32 returns d raised to the power of e, where e is an int32.

Returns:

  • The result of d raised to the power of e.
  • An error if d is zero and e is a negative integer.

Special cases:

Examples:

PowInt32(0, 0) = 1
PowInt32(2, 0) = 1
PowInt32(0, 1) = 0
PowInt32(0, -1) results in an error
PowInt32(2.5, 2) = 6.25
PowInt32(2.5, -2) = 0.16
Example
fmt.Println(MustParse("1.23").PowInt32(2))
fmt.Println(MustParse("1.23").PowInt32(0))
fmt.Println(MustParse("1.23").PowInt32(-2))
fmt.Println(MustParse("0").PowInt32(0))
fmt.Println(MustParse("0").PowInt32(-2))
Output:

1.5129 <nil>
1 <nil>
0.6609822195782933439 <nil>
1 <nil>
0 can't raise zero to a negative power

func (Decimal) PowToIntPart added in v1.6.0

func (d Decimal) PowToIntPart(e Decimal) (Decimal, error)

PowToIntPart raises the decimal d to the power of integer part of e (d^int(e)). This is useful when the exponent is an integer but stored in Decimal.

Returns error if:

  • d is zero and e is a negative integer.
  • |int(e)| > math.MaxInt32 (because MaxInt32 is already ~2 billion, supporting more than that value is not practical and unnecessary).

Special cases:

Examples:

PowInt32(0, 0) = 1
PowInt32(2, 0) = 1
PowInt32(0, 1) = 0
PowInt32(0, -1) results in an error
PowInt32(2.5, 2.6) = 2.5^2 = 6.25
PowInt32(2.5, -2.123) = 2.5^(-2) = 0.16
Example
fmt.Println(MustParse("1.23").PowToIntPart(MustParse("2.123")))
fmt.Println(MustParse("1.23").PowToIntPart(MustParse("0.123")))
fmt.Println(MustParse("1.23").PowToIntPart(MustParse("-2.123")))
fmt.Println(MustParse("0").PowToIntPart(MustParse("0.123")))
fmt.Println(MustParse("0").PowToIntPart(MustParse("-2.123")))
Output:

1.5129 <nil>
1 <nil>
0.6609822195782933439 <nil>
1 <nil>
0 can't raise zero to a negative power

func (Decimal) Prec

func (d Decimal) Prec() int

Prec returns decimal precision as an integer

Example
fmt.Println(MustParse("1.23").Prec())
Output:

2

func (Decimal) PrecUint added in v1.3.0

func (d Decimal) PrecUint() uint8

PrecUint returns decimal precision as uint8 Useful when you want to use the precision in other functions like Decimal.RoundBank or Decimal.Trunc because they accept uint8

Example:

u := MustParse("0.000001")
d := MustParse("123.4567891") // 123.456, prec = 3
d = d.Trunc(u.PrecUint()) // 123.456789
Example
fmt.Println(MustParse("1.23456").PrecUint())
Output:

5

func (Decimal) QuoRem added in v1.6.0

func (d Decimal) QuoRem(e Decimal) (Decimal, Decimal, error)

QuoRem returns q and r where - q = d / e and q is an integer - r = d - q * e (r < e and r has the same sign as d)

The implementation is similar to C's fmod function. Returns divide by zero error when e is zero

Example
fmt.Println(MustParse("1.23").QuoRem(MustParse("0.5")))
fmt.Println(MustParse("1.23").QuoRem(MustParse("0")))
Output:

2 0.23 <nil>
0 0 can't divide by zero

func (Decimal) RoundAwayFromZero added in v1.6.0

func (d Decimal) RoundAwayFromZero(prec uint8) Decimal

RoundAwayFromZero rounds the decimal to the specified prec using AWAY FROM ZERO method (https://en.wikipedia.org/wiki/Rounding#Rounding_away_from_zero). If differs from HALF AWAY FROM ZERO in a way that the number is always rounded away from zero (or to infinity) no matter if is 0.5 or not. In other libraries or languages, this method is also known as ROUND_UP.

Examples:

Round(1.12, 1) = 1.2
Round(1.15, 1) = 1.2
Round(-1.12, 1) = -1.12
Round(-1.15, 1) = -1.12
Example
fmt.Println(MustParse("1.12").RoundAwayFromZero(1))
fmt.Println(MustParse("1.15").RoundAwayFromZero(1))
fmt.Println(MustParse("-1.12").RoundAwayFromZero(1))
fmt.Println(MustParse("-1.15").RoundAwayFromZero(1))
Output:

1.2
1.2
-1.2
-1.2

func (Decimal) RoundBank

func (d Decimal) RoundBank(prec uint8) Decimal

RoundBank uses half up to even (banker's rounding) to round the decimal to the specified prec.

Examples:

RoundBank(1.12345, 4) = 1.1234
RoundBank(1.12335, 4) = 1.1234
RoundBank(1.5, 0) = 2
RoundBank(-1.5, 0) = -2
Example
fmt.Println(MustParse("1.12345").RoundBank(4))
fmt.Println(MustParse("1.12335").RoundBank(4))
fmt.Println(MustParse("1.5").RoundBank(0))
fmt.Println(MustParse("-1.5").RoundBank(0))
Output:

1.1234
1.1234
2
-2

func (Decimal) RoundHAZ

func (d Decimal) RoundHAZ(prec uint8) Decimal

RoundHAZ rounds the decimal to the specified prec using HALF AWAY FROM ZERO method (https://en.wikipedia.org/wiki/Rounding#Rounding_half_away_from_zero).

Examples:

Round(1.12345, 4) = 1.1235
Round(1.12335, 4) = 1.1234
Round(1.5, 0) = 2
Round(-1.5, 0) = -2
Example
fmt.Println(MustParse("1.12345").RoundHAZ(4))
fmt.Println(MustParse("1.12335").RoundHAZ(4))
fmt.Println(MustParse("1.5").RoundHAZ(0))
fmt.Println(MustParse("-1.5").RoundHAZ(0))
Output:

1.1235
1.1234
2
-2

func (Decimal) RoundHTZ

func (d Decimal) RoundHTZ(prec uint8) Decimal

RoundHTZ rounds the decimal to the specified prec using HALF TOWARD ZERO method (https://en.wikipedia.org/wiki/Rounding#Rounding_half_toward_zero).

Examples:

Round(1.12345, 4) = 1.1234
Round(1.12335, 4) = 1.1233
Round(1.5, 0) = 1
Round(-1.5, 0) = -1
Example
fmt.Println(MustParse("1.12345").RoundHTZ(4))
fmt.Println(MustParse("1.12335").RoundHTZ(4))
fmt.Println(MustParse("1.5").RoundHTZ(0))
fmt.Println(MustParse("-1.5").RoundHTZ(0))
Output:

1.1234
1.1233
1
-1

func (*Decimal) Scan

func (d *Decimal) Scan(src any) error

Scan implements sql.Scanner interface.

Example
var a Decimal
_ = a.Scan("1.23")
fmt.Println(a)
Output:

1.23

func (Decimal) Sign

func (d Decimal) Sign() int

Sign returns:

-1 if d < 0
 0 if d == 0
+1 if d > 0
Example
fmt.Println(MustParse("1.23").Sign())
fmt.Println(MustParse("-1.23").Sign())
fmt.Println(MustParse("0").Sign())
Output:

1
-1
0

func (Decimal) Sqrt

func (d Decimal) Sqrt() (Decimal, error)

Sqrt returns the square root of d using Newton-Raphson method. (https://en.wikipedia.org/wiki/Newton%27s_method) The result will have at most defaultPrec digits after the decimal point. Returns error if d < 0

Examples:

Sqrt(4) = 2
Sqrt(2) = 1.4142135623730950488
Example
fmt.Println(MustParse("1.21").Sqrt())
fmt.Println(MustParse("0").Sqrt())
fmt.Println(MustParse("-1.21").Sqrt())
Output:

1.1 <nil>
0 <nil>
0 can't calculate square root of negative number

func (Decimal) String

func (d Decimal) String() string

String returns the string representation of the decimal. Trailing zeros will be removed.

Example
fmt.Println(MustParse("1.23").String())
fmt.Println(MustParse("-1.230000").String())
Output:

1.23
-1.23

func (Decimal) StringFixed

func (d Decimal) StringFixed(prec uint8) string

StringFixed returns the string representation of the decimal with fixed prec. Trailing zeros will not be removed. If the decimal is integer, the fractional part will be padded with zeros. If prec is smaller then d.prec, the number will stay the same as the original.

Example:

1.23.StringFixed(4) -> 1.2300
-1.23.StringFixed(4) -> -1.2300
5.StringFixed(2) -> 5.00
5.123.StringFixed(2) -> 5.123
Example
fmt.Println(MustParse("1").StringFixed(2))
fmt.Println(MustParse("1.23").StringFixed(4))
fmt.Println(MustParse("-1.230000").StringFixed(5))
Output:

1.00
1.2300
-1.23000

func (Decimal) Sub

func (d Decimal) Sub(e Decimal) Decimal

Sub returns d - e

Example
a := MustParse("1.23")
b := MustParse("4.12475")
c := a.Sub(b)
fmt.Println(c)
Output:

-2.89475

func (Decimal) Sub64

func (d Decimal) Sub64(e uint64) Decimal

Sub64 returns d - e where e is a uint64

Example
a := MustParse("1.23")
c := a.Sub64(4)
fmt.Println(c)
Output:

-2.77

func (Decimal) ToHiLo added in v1.7.0

func (d Decimal) ToHiLo() (neg bool, hi uint64, lo uint64, prec uint8, ok bool)

ToHiLo returns the internal representation of the decimal.

Example
fmt.Println(MustParse("1.23").ToHiLo())
fmt.Println(MustParse("1234567890123456789.1234567890123456789").ToHiLo())
fmt.Println(MustParse("-1234567890123456789.1234567890123456789").ToHiLo())
fmt.Println(MustParse("0.123").ToHiLo())
fmt.Println(MustParse("-0.123").ToHiLo())
fmt.Println(MustParse("12345678901234567890123456.123456").ToHiLo())
fmt.Println(MustParse("0").ToHiLo())
fmt.Println(MustParse("18446744073709551615").ToHiLo())
fmt.Println(MustParse("18446744073709551617").ToHiLo())
fmt.Println(MustParse("184467440737095516.15").ToHiLo())
fmt.Println(MustParse("184467440737095516.16").ToHiLo())
fmt.Println(MustParse("184467440737095516160.1844674407370955161").ToHiLo())
Output:

false 0 123 2 true
false 669260594276348691 15255105882844922133 19 true
true 669260594276348691 15255105882844922133 19 true
false 0 123 3 true
true 0 123 3 true
false 669260594276 6432227781799973440 6 true
false 0 0 0 true
false 0 18446744073709551615 0 true
false 1 1 0 true
false 0 18446744073709551615 2 true
false 1 0 2 true
false 0 0 0 false

func (Decimal) Trunc

func (d Decimal) Trunc(prec uint8) Decimal

Trunc returns d after truncating the decimal to the specified prec.

Examples:

Trunc(1.12345, 4) = 1.1234
Trunc(1.12335, 4) = 1.1233
Example
fmt.Println(MustParse("1.23").Trunc(1))
fmt.Println(MustParse("-1.23").Trunc(5))
Output:

1.2
-1.23

func (*Decimal) UnmarshalBinary

func (d *Decimal) UnmarshalBinary(data []byte) error
Example
var a Decimal
_ = a.UnmarshalBinary([]byte{0, 2, 11, 0, 0, 0, 0, 0, 0, 0, 123})
fmt.Println(a)
Output:

1.23

func (*Decimal) UnmarshalJSON

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

UnmarshalJSON implements the json.Unmarshaler interface.

Example
var a Decimal
_ = a.UnmarshalJSON([]byte("1.23"))
fmt.Println(a)
Output:

1.23

func (*Decimal) UnmarshalText added in v1.2.0

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

UnmarshalText implements the encoding.TextUnmarshaler interface.

Example
var a Decimal
_ = a.UnmarshalText([]byte("1.23"))
fmt.Println(a)
Output:

1.23

func (Decimal) Value

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

Value implements driver.Valuer interface.

Example
fmt.Println(MustParse("1.2345").Value())
Output:

1.2345 <nil>

type NullDecimal

type NullDecimal struct {
	Decimal Decimal
	Valid   bool
}

NullDecimal is a nullable Decimal.

func (*NullDecimal) Scan

func (d *NullDecimal) Scan(src any) error

Scan implements sql.Scanner interface.

Example
var a, b NullDecimal
_ = a.Scan("1.23")
_ = b.Scan(nil)

fmt.Println(a)
fmt.Println(b)
Output:

{1.23 true}
{0 false}

func (NullDecimal) Value

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

Value implements the driver.Valuer interface.

Example
fmt.Println(NullDecimal{Decimal: MustParse("1.2345"), Valid: true}.Value())
fmt.Println(NullDecimal{}.Value())
Output:

1.2345 <nil>
<nil> <nil>

Jump to

Keyboard shortcuts

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