Arbitrary-precision fixed-point decimal numbers in go

decimal

Build Status GoDoc Go Report Card

Arbitrary-precision fixed-point decimal numbers in go.

Note: Decimal library 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

Requirements

Decimal library requires Go version >=1.7

Usage

package main

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

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

	quantity := decimal.NewFromInt(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 float64 (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.

Owner
Spring Engineering
Spring's Engineering open source home
Spring Engineering
Comments
  • Make Value take a pointer receiver

    Make Value take a pointer receiver

    The Value method needs to take a pointer receiver so that null values can be inserted into the database with ease.

    This PR shouldn't affect any existing behavior since the arguments to Exec get converted into an interface{}.

  • Zeros trimmed off when parsing strings, leading to inability to use string to store a decimal value

    Zeros trimmed off when parsing strings, leading to inability to use string to store a decimal value

    Looks like merge #46 (Remove insignificant digits during string parsing) made it impossible to initialize decimals from string with a fixed precision. What was the motivation for that change, and would a change be accepted to undo it, or move it off into a NewFromStringTrimmed() function? [Or, less preferably, introduce a NewFromStringPrecise variant.]

    The whole point of decimals is to be precise, and there is a semantic difference between 1.0 and 1.00.

  • Bug in NewFromFloat

    Bug in NewFromFloat

    Hi, after commit 78e9b82f68c54fcfaf34d71d751e594d20d2e242 NewFromFloat works not as it should

    fmt.Println(decimal.NewFromFloat(0.1).String()) fmt.Println(decimal.NewFromFloat(0.01).String()) fmt.Println(decimal.NewFromFloat(0.02).String()) fmt.Println(decimal.NewFromFloat(0.0023).String()) fmt.Println(decimal.NewFromFloat(0.69).String()) fmt.Println(decimal.NewFromFloat(0.98).String())

    return

    0.1000000000000000055511151231257827021181583404541015625 0.01000000000000000020816681711721685132943093776702880859375 0.0200000000000000004163336342344337026588618755340576171875 0.0022999999999999999611421941381195210851728916168212890625 0.689999999999999946709294817992486059665679931640625 0.979999999999999982236431605997495353221893310546875

    Reverting back to commit bf9a39e28bc9aea136acaf2c3628e68f65e3c3df works as it should: 0.1 0.01 0.02 0.0023 0.69 0.98

    Tested on go version go1.9.2 linux/amd64 Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Model name: Intel(R) Core(TM) i3-4170 CPU @ 3.70GHz

  • please add get original value funcion

    please add get original value funcion

    in current decimal,IntPart Function to get decimal IntPart value

    // IntPart returns the integer component of the decimal.
    func (d Decimal) IntPart() int64 {
    	scaledD := d.rescale(0)
    	return scaledD.value.Int64()
    }
    

    BUT, IN SOM TIME, NEED TO GET ORIGINAL VALUE

    eg: PROTOBUF , you need define Decimal message, and you need convert between proto.Decimal and decimal.Decimal

    so, it's really neet to to get original value function,

    To get this purpose, I copied decimal code, and Add Int64Value function

    PLEASE ADD

    PLEASE ADD

    PLEASE ADD

    // Int64Value returns the original  value.
    func (d Decimal) Int64Value() int64 {
    	return d.value.Int64()
    }
    

    the follw code is my proto Decimal define:

    // Example: 12345.6789 -> { units = 12345, nanos = 678900000 }
    message Decimal {
    
      // Whole units part of the amount
      int64 val = 1;
    
      // Nano units of the amount (10^-9)
      // Must be same sign as units
      int32 exp = 2;
    }
    

    convert between decimal.Decimal and proto.Decimal

    1、decimal.Decimal -> proto.Decimal

    func FromDecimal(d decimal.Decimal) *proto.Decimal {
    	return &Decimal{
    		Val: d.Int64Value(),
    		Exp: d.Exponent(),
    	}
    }
    

    2、proto.Decimal -> decimal.Decimal

    func (x proto.Decimal) ToDecimal() decimal.Decimal {
    	return decimal.New(x.Val, x.Exp)
    }
    
  • Performance issue preventing production use

    Performance issue preventing production use

    I'm currently working on an application that would benefit greatly from this lib, due to requirements with floating point precision. However benchmark tests of that application are worrying.

    The scenario involves creating Decimal values, and then calling the various comparison methods on them. This sequence of events happens millions of times. Immediately after converting my code to use Decimal (instead of big.float, which was showing unacceptable precision loss after 20 significant digits), the performance time of our benchmark test went from 3-4 minutes for the test, to 22, on the same processor. pprof helped narrow the problem to the fact that your implementation of CMP involves the creation of an additional 2 decimal objects whenever the 'exp' values were different (which was pretty much every time), which is in line with the fact that after the change, 60% of the cpu time was spent allocating memory.

    Is there a particular reason this approach was taken? Would a PR attempting to optimize this be accepted?

  • SQL Value marshall/unmarshall is broken

    SQL Value marshall/unmarshall is broken

    Get error when used with gorm:

    pq: encode: unknown type for decimal.Decimal
    

    All nice on commit: cd690d0c9e2447b1ef2a129a6b7b49077da89b8e but broken for the last one: a36b5d85f337bcbff4008ffb786d0e4ec2a345a4

    Also, it would be nice if you will add go mod support.

  • Need two API: get precision from decimal, get how many digits from decimal

    Need two API: get precision from decimal, get how many digits from decimal

    1. get precision from decimal

    how many digital number after dot

    1. get how many digits from decimal

    how many digital numbers, for example, 1.632, there are 4 numbers

  • Revert remove insignificant digits

    Revert remove insignificant digits

    When I went to revert #46, TestDecimal_RoundCash started failing because the RoundCash function (#66) relies on a Decimal with insignificant digits removed. To get around this problem I made a new function to remove insignificant digits

  • Inconsistent behaviour for different types of input values

    Inconsistent behaviour for different types of input values

    decimal.NewFromFloat(0.357).Div(decimal.NewFromFloat(0.001)).Floor()

    gives 356 and

    x,_ := decimal.NewFromString("0.357")
    y,_ := decimal.NewFromString("0.001")
    x.Div(y).Floor()
    

    gives 357 but the result should be consistent imho

  • improve performance of Abs() and Round()

    improve performance of Abs() and Round()

    This PR improves the performance of Abs() and Round() and is related to #208 . The goal is to return the original value instead of creating a new one when:

    • value is not negative in Abs()
    • value already has exact precision in Round()
  • Modify Rounding methods

    Modify Rounding methods

    Why

    • The current RoundUp, RoundDown methods seem Ceiling and Floor in Java and many others https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/RoundingMode.html image

    What I did

    • Change the name of the current methods
      • RoundUp -> RoundCeil
      • RoundDown -> RoundFloor
    • Add the new method with the names RoundUp and RoundDown.
    • Add unit test for the new methods
  • The panic caused by NewFromFloat function makes trouble

    The panic caused by NewFromFloat function makes trouble

    func NewFromFloat(value float64) Decimal {
    	if value == 0 {
    		return New(0, 0)
    	}
    	return newFromFloat(value, math.Float64bits(value), &float64info)
    }
    
    
    func newFromFloat(val float64, bits uint64, flt *floatInfo) Decimal {
    	if math.IsNaN(val) || math.IsInf(val, 0) {
    		panic(fmt.Sprintf("Cannot create a Decimal from %v", val))
    	}
    	// ...
    }
    

    the panic caused by newFromFloat function makes trouble. How about returning error instead of panic, likes NewFromString function?

  • Support for the three latest Go version

    Support for the three latest Go version

    I use this module on production a long time. I happened to find the Go versions are old. This PR update Go version on CI and go.mod. And, I also update README.md.

  • Support for other decimal separators

    Support for other decimal separators

    This library currently supports only the dot . as a decimal separator. Unfortunately there is not a standard for this (see here for example). The comma , for example, is also heavily used.

    It would be nice to have some support from the library in regard to this. This issue falls under the umbrella of localization, which is quite tedious to implement, but some global variables could be enough.

    At the moment the dot is hard-coded here https://github.com/shopspring/decimal/blob/f55dd564545cec84cf84f7a53fb3025cdbec1c4f/decimal.go#L159

    It would be really easy to support others decimal separators by introducing a global variable

    var DecimalSeparator string = "."
    

    that users can change from outside, and use it there. Of course the code for formatting the number should also be modified.

    Sometimes one wants to support different formats. At this point would be possible to also allow for several separators. For example

    var DecimalSeparators string = ".," //Support for both dot and comma
    

    and

    	if strings.Contains(DecimalSeparators, value[i]) {
    

    For the formatting, the first separator DecimalSeparators[0] could be used.

    What do you think? I would like to hear some opinion before (eventually) submitting a PR.

  • Fix NumDigits with ensureInitialized

    Fix NumDigits with ensureInitialized

    Fix #292

    The library promises

    The zero-value is 0, and is safe to use without initialization

    Simply add ensureInitialized at the beginning of NumDigits to ensure zero-value act as NewFromInt(0).

  • use  to determine whether it equals 0 or not.

    use to determine whether it equals 0 or not.

    r.Cmp(zeroInt) != 0 => r.Sign() != 0

    func BenchmarkCmpZeroInt(b *testing.B) { positiveVal := big.NewInt(314) negtiveVal := big.NewInt(-314) b.Run("cmpzero", func(b *testing.B) { for i := 0; i < b.N; i++ { zeroInt.Cmp(zeroInt) } })

    b.Run("cmppositive", func(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		positiveVal.Cmp(zeroInt)
    	}
    })
    
    b.Run("cmpnegtive", func(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		negtiveVal.Cmp(zeroInt)
    	}
    })
    
    b.Run("signzero", func(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		zeroInt.Sign()
    	}
    })
    
    b.Run("signpositive", func(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		positiveVal.Sign()
    	}
    })
    
    b.Run("signnegtive", func(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		negtiveVal.Sign()
    	}
    })
    

    }

    goos: darwin goarch: amd64 pkg: github.com/shopspring/decimal cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz BenchmarkCmpZeroInt BenchmarkCmpZeroInt/cmpzero BenchmarkCmpZeroInt/cmpzero-8 729437467 1.714 ns/op 0 B/op 0 allocs/op BenchmarkCmpZeroInt/cmppositive BenchmarkCmpZeroInt/cmppositive-8 240192255 5.657 ns/op 0 B/op 0 allocs/op BenchmarkCmpZeroInt/cmpnegtive BenchmarkCmpZeroInt/cmpnegtive-8 463588371 2.386 ns/op 0 B/op 0 allocs/op BenchmarkCmpZeroInt/signzero BenchmarkCmpZeroInt/signzero-8 1000000000 0.4847 ns/op 0 B/op 0 allocs/op BenchmarkCmpZeroInt/signpositive BenchmarkCmpZeroInt/signpositive-8 1000000000 0.5291 ns/op 0 B/op 0 allocs/op BenchmarkCmpZeroInt/signnegtive BenchmarkCmpZeroInt/signnegtive-8 1000000000 0.5308 ns/op 0 B/op 0 allocs/op

Arbitrary-precision fixed-point decimal numbers in go

decimal Arbitrary-precision fixed-point decimal numbers in go. Note: Decimal library can "only" represent numbers with a maximum of 2^31 digits after

Jan 10, 2022
An arbitrary-precision decimal floating-point arithmetic package for Go

decimal Package decimal implements arbitrary-precision decimal floating-point arithmetic for Go. Rationale How computers represent numbers internally

Sep 27, 2022
An arbitrary-precision decimal floating-point arithmetic package for Go

decimal Package decimal implements arbitrary-precision decimal floating-point arithmetic for Go. Rationale How computers represent numbers internally

Sep 27, 2022
high performance fixed decimal place math library for Go

Summary A fixed place numeric library designed for performance. All numbers have a fixed 7 decimal places, and the maximum permitted value is +- 99999

Dec 6, 2022
Arbitrary-precision decimals for Go

apd apd is an arbitrary-precision decimal package for Go. apd implements much of the decimal specification from the General Decimal Arithmetic descrip

Jan 8, 2023
Converts a number to its English counterpart. Uses arbitrary precision; so a number of any size can be converted.

Converts a number to its English counterpart. Uses arbitrary precision; so a number of any size can be converted.

Dec 14, 2021
Bigint - Immutable arbitrary-precision integer for Go

bigint Go's big.Int is mutable to enable flexibility in performance tuning but s

Sep 13, 2022
A little websocket TCP proxy to let browsers talk to a fixed port on arbitrary hosts. Built for Gemini (gemini://, port 1965)

Kepler A little websocket TCP proxy built to let Amfora talk to Gemini servers when running in a browser. Usage $ git clone https://github.com/awfulco

May 27, 2022
Utilities for rounding and truncating floating point numbers.

Rounders Provides utilities for rounding and truncating floating point numbers. Example: rounders.RoundToDecimals(12.48881, 2)

Jan 6, 2022
It's a basic newtonian gravity simulation written in Go using floating point numbers with 1024 bits of accuracy

Float 1K Gravity What is it? It's a basic newtonian gravity simulation written in Go using floating point numbers with 1024 bits of accuracy. It is cu

Dec 13, 2022
An anonymous, encrypted Point-to-Point (Layer 3) tunnel between two peers.

NKN-Link An anonymous, encrypted Point-to-Point (Layer 3) tunnel between two peers. NKN-Link Table of Contents Preface Description Install Setup Run P

Dec 20, 2022
Wg-configurator - This project makes it easier to configure a fleet of servers interconnected via the WireGuard point-to-point virtual private network.

WireGuard Configurator This project makes it easier to configure a fleet of servers interconnected via the WireGuard point-to-point virtual private ne

Mar 29, 2022
An Alfred Workflow for the Johnny.Decimal filing system
An Alfred Workflow for the Johnny.Decimal filing system

alfred-jd An Alfred Workflow for the Johnny.Decimal filing system This workflow provides tools for working with the brilliant Johnny.Decimal filing sy

Dec 26, 2022
Transfer decimal ipv4 to binary ipv4

transfer decimal ipv4 to binary ipv4. Download: git clone https://github.com/Bet

Jun 8, 2022
High-precision indoor positioning framework, version 3.

The Framework for Internal Navigation and Discovery (FIND) is like indoor GPS for your house or business, using only a simple smartphone or laptop. Th

Jan 1, 2023
Fixed width file parser (encoder/decoder) in GO (golang)

Fixed width file parser (encoder/decoder) for GO (golang) This library is using to parse fixed-width table data like: Name Address

Sep 27, 2022
Encoding and decoding for fixed-width formatted data

fixedwidth Package fixedwidth provides encoding and decoding for fixed-width formatted Data. go get github.com/ianlopshire/go-fixedwidth Usage Struct

Dec 16, 2022
Run Jobs on a schedule, supports fixed interval, timely, and cron-expression timers; Instrument your processes and expose metrics for each job.

A simple process manager that allows you to specify a Schedule that execute a Job based on a Timer. Schedule manage the state of this job allowing you to start/stop/restart in concurrent safe way. Schedule also instrument this Job and gather metrics and optionally expose them via uber-go/tally scope.

Dec 8, 2022
A simple thread-safe, fixed size LRU written in Go. Based on dominictarr's Hashlru Algorithm. 🔃

go-hashlru A simple thread-safe, fixed size LRU written in Go. Based on dominictarr's Hashlru Algorithm. ?? Uses map[interface{}]interface{} to allow

Dec 5, 2022
Routines was a fixed number thread pool to process the user task, and it would respawn a corresponding new thread when panic

Routines Routines was a fixed number thread pool to process the user task, and it would respawn a corresponding new thread when panic. It supports the

Dec 16, 2021