diff options
author | Vadim Graboys <dimva13@gmail.com> | 2015-06-15 01:01:56 +0800 |
---|---|---|
committer | Vadim Graboys <dimva13@gmail.com> | 2015-06-15 01:06:00 +0800 |
commit | 1a6d69cb903665e888e944460babcd4898ee97f4 (patch) | |
tree | ab24aa0cfeb5ae94852362ea564530ffbc5efd20 | |
parent | 354b07a0fbb0fc371742de5a4e8f7efa3019d8bd (diff) | |
download | dexon-decimal-1a6d69cb903665e888e944460babcd4898ee97f4.tar.gz dexon-decimal-1a6d69cb903665e888e944460babcd4898ee97f4.tar.zst dexon-decimal-1a6d69cb903665e888e944460babcd4898ee97f4.zip |
add FAQ to README
-rw-r--r-- | README.md | 59 |
1 files changed, 58 insertions, 1 deletions
@@ -42,7 +42,7 @@ func main() { 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 @@ -60,6 +60,63 @@ http://godoc.org/github.com/shopspring/decimal * [Spring](https://shopspring.com/), 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) |