Arbitrary-precision fixed-point decimal numbers in go

Overview

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.

Issues
  • 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{}.

    opened by tomlinford 18
  • 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.

    opened by imirkin 16
  • 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

    opened by wedancedalot 16
  • 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)
    }
    
    opened by piyongcai 12
  • 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?

    opened by technicalviking 12
  • 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.

    opened by risentveber 10
  • 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

    opened by guotie 9
  • 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

    opened by njason 9
  • 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

    opened by breathbath 8
  • 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()
    opened by DoubleDi 7
  • 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
    opened by lovung 7
  • Method NewFromString and NewFromFloat works differently

    Method NewFromString and NewFromFloat works differently

    Example:

    a, _ := decimal.NewFromString("166.6666666666666667")
    b := decimal.NewFromFloat(166.6666666666666667)
    fmt.Println(a.Equal(b)) // false
    
    println(a.String()) // 166.6666666666666667
    println(b.String()) // 166.66666666666666
    

    Play with it: https://go.dev/play/p/isV9ODqEy5u

    The same for:

    decimal.NewFromFloatWithExponent(166.6666666666666667, -16)
    

    Question is: why NewFromString and NewFromFloat returns different values?

    opened by DarkDrim 0
  • Add NewFromBigRat function

    Add NewFromBigRat function

    This adds a convenience helper function that converts a big.Rat to a Decimal. Since it is impossible to guarantee an exact accuracy, a precision attribute is used.

    Just as the Div method has a DivRounded method counterpart, perhaps this convenience function should have one as well? It could use the DivisionPrecision variable and wouldn't accept a precision parameter.

    opened by davseby 0
  • roundfloor

    roundfloor

    num := decimal.NewFromFloat(1.111111).Sub(decimal.NewFromFloat(1.222222)).RoundFloor(4)
    fmt.Println("1.111111 - 1.222222 =", num)
    
         1.111111 - 1.222222 = -0.1112
    
        but, the RoundFloor example was -0.1111
    
    opened by richardJiang 0
  • Round RoundBank

    Round RoundBank

    package main
    
    import (
    	"fmt"
    
    	"github.com/shopspring/decimal"
    )
    
    func main() {
    	fmt.Println(decimal.NewFromFloat(float64(10) / float64(53)))
    	fmt.Println(decimal.NewFromFloat(float64(10) / float64(53)).Round(4))
    	fmt.Println(decimal.NewFromFloat(float64(10) / float64(53)).RoundBank(4).String())
    	fmt.Println(decimal.NewFromFloat(float64(10) / float64(53)).RoundBank(4).Float64())
    	fmt.Println(decimal.NewFromFloat(5.45).Round(1).String())
    }
    
    
    
    0.18867924528301888
    0.1887
    0.1887
    0.1887 false
    5.5
    
    opened by fideism 0
  • Add Format()

    Add Format()

    This adds a Format() function to decimal, allowing for setting of a thousands separator (as requested in #267) and decimal separator, as well as the existing functionality for trimming trailing zeros. This allows for easier use in localized environments.

    This replaces the prior internal string() function, which could now be removed entirely but that would result in a larger PR so has been left for now.

    Worth taking a close look at lines 1032-1034 to ensure they behave as expected, as they alter the flow compared to before this PR.

    opened by mcdee 0
Releases(v1.3.1)
  • v1.3.1(Oct 20, 2021)

    Full Changelog can be found in CHANGELOG.md

    New Contributors

    • @Gilthoniel made their first contribution in https://github.com/shopspring/decimal/pull/253
    • @lmittmann made their first contribution in https://github.com/shopspring/decimal/pull/252
    Source code(tar.gz)
    Source code(zip)
  • v1.3.0(Oct 14, 2021)

    Full Changelog can be found in CHANGELOG.md

    New Contributors

    • @dreamerjackson made their first contribution in https://github.com/shopspring/decimal/pull/161
    • @sreekanth370 made their first contribution in https://github.com/shopspring/decimal/pull/188
    • @nknordeen made their first contribution in https://github.com/shopspring/decimal/pull/198
    • @lovung made their first contribution in https://github.com/shopspring/decimal/pull/202
    • @bharath23 made their first contribution in https://github.com/shopspring/decimal/pull/192
    • @felipeneuwald made their first contribution in https://github.com/shopspring/decimal/pull/205
    • @jmalloc made their first contribution in https://github.com/shopspring/decimal/pull/232
    • @DoubleDi made their first contribution in https://github.com/shopspring/decimal/pull/240
    • @habuvo made their first contribution in https://github.com/shopspring/decimal/pull/123
    • @rubensayshi made their first contribution in https://github.com/shopspring/decimal/pull/234
    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Apr 28, 2020)

Owner
Spring Engineering
Spring's Engineering open source home
Spring Engineering
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

robert engels 285 Jun 21, 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

CockroachDB 453 Jul 1, 2022
This is a go implementation of Segmented Sieve and non Segmented sieve to produce prime numbers concurrently.

Prime This is a Go library to produce prime numbers using all available cpu cores. Installation $ go get github.com/kavehmz/prime Usage package main

Kaveh Mousavi Zamani 23 Jan 16, 2022
Implements a simple floating point arithmetic expression evaluator in Go (golang).

evaler https://github.com/soniah/evaler Package evaler implements a simple floating point arithmetic expression evaluator. Evaler uses Dijkstra's Shun

Sonia Hamilton 49 Jun 4, 2022
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

Spring Engineering 4.1k Jun 23, 2022
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

null 0 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

Denis Bernard 27 Jun 19, 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

Denis Bernard 27 Jun 19, 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

robert engels 285 Jun 21, 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

CockroachDB 453 Jul 1, 2022
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.

null 0 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

Minswap 5 Mar 3, 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

mooff 1 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)

Konnor Klashinsky 0 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

Nathan Hedge 2 Apr 4, 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

HAH! Sun 7 Apr 6, 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

Caleb L. Power 1 Mar 29, 2022
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

bsag 21 Apr 10, 2022
Transfer decimal ipv4 to binary ipv4

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

yifan 1 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

Zack 4.3k Jun 28, 2022
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

Oleg Lobanov 20 Mar 4, 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

Ian Lopshire 63 May 8, 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.

Sherif Abdel-Naby 57 Mar 28, 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

Saurabh Pujari 70 Jun 15, 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

hulk 12 Dec 16, 2021
Fixed column file to avro/kafka

shredder shredds Fixed column file to avro/kafka . Implementation uses Avro schema and multicore Speed around 220mb/sec per Core using 4 core on a 1Gb

Rickard 3 Dec 20, 2021
A lightweight, thread-safe FIFO queue with fixed capacity.

FIFO Queue Thread-safe, lightweight, tested FIFO queue with fixed size, which is built upon list for performance ;-) This implement is inspired by htt

jhin 0 Dec 16, 2021
Simple docker container to publish a fixed message to a specified queue. Created to be used with k8s CRON scheduling.

RabbitMQ Publish CRON Simple docker container to publish a fixed message to a specified rabbitmq exchange. Created to be used as part of a Kubernetes

Daniel Emery 0 Dec 20, 2021
This provides the lru package which implements a fixed-size thread safe LRU cache

golang-lru This provides the lru package which implements a fixed-size thread sa

byx 0 Dec 22, 2021