decimal

package module
v0.0.0-...-9d980d3 Latest Latest
Warning

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

Go to latest
Published: May 13, 2016 License: MIT Imports: 6 Imported by: 0

README

decimal Build Status BADGINATOR

Arbitrary-precision fixed-point decimal numbers in go.

NOTE: can "only" represent numbers with a maximum of 2^31 digits after the decimal point.

Features

  • the zero-value is 0, and is safe to use without initialization
  • addition, subtraction, multiplication with no loss of precision
  • division with specified precision
  • database/sql serialization/deserialization
  • json and xml serialization/deserialization

Install

Run go get github.com/shopspring/decimal

Usage

package main

import (
    "fmt"
    "github.com/shopspring/decimal"
)

func main() {
	price, err := decimal.NewFromString("136.02")
    if err != nil {
        panic(err)
    }

	quantity := decimal.NewFromFloat(3)

	fee, _ := decimal.NewFromString(".035")
	taxRate, _ := decimal.NewFromString(".08875")

    subtotal := price.Mul(quantity)

    preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1)))

    total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1)))

	fmt.Println("Subtotal:", subtotal)                      // Subtotal: 408.06
	fmt.Println("Pre-tax:", preTax)                         // Pre-tax: 422.3421
    fmt.Println("Taxes:", total.Sub(preTax))                // Taxes: 37.482861375
	fmt.Println("Total:", total)                            // Total: 459.824961375
	fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875
}

Documentation

http://godoc.org/github.com/shopspring/decimal

Production Usage

  • Spring, since August 14, 2014.
  • If you are using this in production, please let us know!

FAQ

Why don't you just use float64?

Because float64s (or any binary floating point type, actually) can't represent numbers such as 0.1 exactly.

Consider this code: http://play.golang.org/p/TQBd4yJe6B You might expect that it prints out 10, but it actually prints 9.999999999999831. Over time, these small errors can really add up!

Why don't you just use big.Rat?

big.Rat is fine for representing rational numbers, but Decimal is better for representing money. Why? Here's a (contrived) example:

Let's say you use big.Rat, and you have two numbers, x and y, both representing 1/3, and you have z = 1 - x - y = 1/3. If you print each one out, the string output has to stop somewhere (let's say it stops at 3 decimal digits, for simplicity), so you'll get 0.333, 0.333, and 0.333. But where did the other 0.001 go?

Here's the above example as code: http://play.golang.org/p/lCZZs0w9KE

With Decimal, the strings being printed out represent the number exactly. So, if you have x = y = 1/3 (with precision 3), they will actually be equal to 0.333, and when you do z = 1 - x - y, z will be equal to .334. No money is unaccounted for!

You still have to be careful. If you want to split a number N 3 ways, you can't just send N/3 to three different people. You have to pick one to send N - (2/3*N) to. That person will receive the fraction of a penny remainder.

But, it is much easier to be careful with Decimal than with big.Rat.

Why isn't the API similar to big.Int's?

big.Int's API is built to reduce the number of memory allocations for maximal performance. This makes sense for its use-case, but the trade-off is that the API is awkward and easy to misuse.

For example, to add two big.Ints, you do: z := new(big.Int).Add(x, y). A developer unfamiliar with this API might try to do z := a.Add(a, b). This modifies a and sets z as an alias for a, which they might not expect. It also modifies any other aliases to a.

Here's an example of the subtle bugs you can introduce with big.Int's API: https://play.golang.org/p/x2R_78pa8r

In contrast, it's difficult to make such mistakes with decimal. Decimals behave like other go numbers types: even though a = b will not deep copy b into a, it is impossible to modify a Decimal, since all Decimal methods return new Decimals and do not modify the originals. The downside is that this causes extra allocations, so Decimal is less performant. My assumption is that if you're using Decimals, you probably care more about correctness than performance.

License

The MIT License (MIT)

This is a heavily modified fork of fpd.Decimal, which was also released under the MIT License.

Documentation

Overview

Package decimal implements an arbitrary precision fixed-point decimal.

To use as part of a struct:

type Struct struct {
    Number Decimal
}

The zero-value of a Decimal is 0, as you would expect.

The best way to create a new Decimal is to use decimal.NewFromString, ex:

n, err := decimal.NewFromString("-123.4567")
n.String() // output: "-123.4567"

NOTE: This can "only" represent numbers with a maximum of 2^31 digits after the decimal point.

Index

Constants

This section is empty.

Variables

View Source
var DivisionPrecision = 16

DivisionPrecision is the number of decimal places in the result when it doesn't divide exactly.

Example:

d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)
d1.String() // output: "0.6666666666666667"
d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000)
d2.String() // output: "0.0000666666666667"
d3 := decimal.NewFromFloat(20000).Div(decimal.NewFromFloat(3)
d3.String() // output: "6666.6666666666666667"
decimal.DivisionPrecision = 3
d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)
d4.String() // output: "0.667"
View Source
var Zero = New(0, 1)

Zero constant, to make computations faster.

Functions

This section is empty.

Types

type Decimal

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

Decimal represents a fixed-point decimal. It is immutable. number = value * 10 ^ exp

func Max

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

Returns the largest Decimal that was passed in the arguments.

To call this function with an array, you must do:

Max(arr[0], arr[1:]...)

This makes it harder to accidentally call Max with 0 arguments.

func Min

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

Returns the smallest Decimal that was passed in the arguments.

To call this function with an array, you must do:

Min(arr[0], arr[1:]...)

This makes it harder to accidentally call Min with 0 arguments.

func New

func New(value int64, exp int32) Decimal

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

func NewFromFloat

func NewFromFloat(value float64) Decimal

NewFromFloat converts a float64 to Decimal.

Example:

NewFromFloat(123.45678901234567).String() // output: "123.4567890123456"
NewFromFloat(.00000000000000001).String() // output: "0.00000000000000001"

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

func NewFromFloatWithExponent

func NewFromFloatWithExponent(value float64, exp int32) Decimal

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

Example:

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

func NewFromString

func NewFromString(value string) (Decimal, error)

NewFromString returns a new Decimal from a string representation.

Example:

d, err := NewFromString("-123.45")
d2, err := NewFromString(".0001")

func (Decimal) Abs

func (d Decimal) Abs() Decimal

Abs returns the absolute value of the decimal.

func (Decimal) Add

func (d Decimal) Add(d2 Decimal) Decimal

Add returns d + d2.

func (Decimal) Ceil

func (d Decimal) Ceil() Decimal

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

func (Decimal) Cmp

func (d Decimal) Cmp(d2 Decimal) int

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) Div

func (d Decimal) Div(d2 Decimal) Decimal

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

func (Decimal) Equals

func (d Decimal) Equals(d2 Decimal) bool

Equals returns whether the numbers represented by d and d2 are equal.

func (Decimal) Exponent

func (d Decimal) Exponent() int32

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

func (Decimal) Float64

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

Float64 returns the nearest float64 value for d and a bool indicating whether f represents d exactly. For more details, see the documentation for big.Rat.Float64

func (Decimal) Floor

func (d Decimal) Floor() Decimal

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

func (Decimal) IntPart

func (d Decimal) IntPart() int64

IntPart returns the integer component of the decimal.

func (Decimal) MarshalJSON

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

MarshalJSON implements the json.Marshaler interface.

func (Decimal) MarshalText

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

MarshalText implements the encoding.TextMarshaler interface for XML serialization.

func (Decimal) Mod

func (d Decimal) Mod(d2 Decimal) Decimal

Mod returns d % d2.

func (Decimal) Mul

func (d Decimal) Mul(d2 Decimal) Decimal

Mul returns d * d2.

func (Decimal) Rat

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

Rat returns a rational number representation of the decimal.

func (Decimal) Round

func (d Decimal) Round(places int32) Decimal

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

Example:

NewFromFloat(5.45).Round(1).String() // output: "5.5"
NewFromFloat(545).Round(-1).String() // output: "550"

func (*Decimal) Scan

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

Scan implements the sql.Scanner interface for database deserialization.

func (Decimal) String

func (d Decimal) String() string

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

Example:

d := New(-12345, -3)
println(d.String())

Output:

-12.345

func (Decimal) StringFixed

func (d Decimal) StringFixed(places int32) string

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

Example:

NewFromFloat(0).StringFixed(2) // output: "0.00"
NewFromFloat(0).StringFixed(0) // output: "0"
NewFromFloat(5.45).StringFixed(0) // output: "5"
NewFromFloat(5.45).StringFixed(1) // output: "5.5"
NewFromFloat(5.45).StringFixed(2) // output: "5.45"
NewFromFloat(5.45).StringFixed(3) // output: "5.450"
NewFromFloat(545).StringFixed(-1) // output: "550"

func (Decimal) StringScaled

func (d Decimal) StringScaled(exp int32) string

NOTE: buggy, unintuitive, and DEPRECATED! Use StringFixed instead. StringScaled first scales the decimal then calls .String() on it.

func (Decimal) Sub

func (d Decimal) Sub(d2 Decimal) Decimal

Sub returns d - d2.

func (Decimal) Truncate

func (d Decimal) Truncate(precision int32) Decimal

Truncate truncates off digits from the number, without rounding.

NOTE: precision is the last digit that will not be truncated (must be >= 0).

Example:

decimal.NewFromString("123.456").Truncate(2).String() // "123.45"

func (*Decimal) UnmarshalJSON

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

UnmarshalJSON implements the json.Unmarshaler interface.

func (*Decimal) UnmarshalText

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

UnmarshalText implements the encoding.TextUnmarshaler interface for XML deserialization.

func (Decimal) Value

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

Value implements the driver.Valuer interface for database serialization.

Jump to

Keyboard shortcuts

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