Money representation

3,858 views
Skip to first unread message

bsr

unread,
May 14, 2011, 4:28:58 PM5/14/11
to golang-nuts
Hello,

What is the best data type to represent underlying money value. Is the
following representation is ok?

type Money float32

func (m *Money)String() {
//Print money according to the Locale
}

thanks,
bsr.

eksp...@gmail.com

unread,
May 14, 2011, 4:43:28 PM5/14/11
to golan...@googlegroups.com
I would use either float64 or float32.

peterGo

unread,
May 14, 2011, 4:47:49 PM5/14/11
to golang-nuts
bsr,

Floating-point arithmetic is an approximation. People hate it when you
make approximations about their money.

What Every Computer Scientist Should Know About Floating-Point
Arithmetic
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html

Use int64 as cents to represent dollars and cents. Watch for
truncation and rounding, particularly in intermediate results.

Peter

Evan Shaw

unread,
May 14, 2011, 4:48:09 PM5/14/11
to bsr, golang-nuts
On Sun, May 15, 2011 at 8:28 AM, bsr <bsr...@gmail.com> wrote:
> type Money float32

If you're at all concerned about precision and rounding this is a bad
idea. It's often important to be exact when dealing with money and
floating point types aren't exact. There's a good description of the
details here: http://docs.python.org/tutorial/floatingpoint.html

Use an integer type for exact representation, but watch for overflows.
If you need bigger numbers, look at package big.

- Evan

bsr

unread,
May 14, 2011, 4:59:55 PM5/14/11
to golang-nuts
You guys are great!!.. thanks for the links, will go through them... I
did ask because of the same reason, rounding errors.. Java had
BigDecimal.. I never knew about package big :-) going though it..
thanks again...

bsr

unread,
May 14, 2011, 6:51:07 PM5/14/11
to golang-nuts
Please see a simple implementation on the line Peter and Evan
suggested.. Please let me know am I on the right track.. The program
outputs 5101, how can I display 51.01. %.2d as formatter did not work.
Also please give a hint on how can I detect overflow (or how to
prevent the user from doing an overflow operation).
thanks,
bsr.

=============== Program ============
package main

import (
"fmt"
)

//Money representation in 1⁄100 of the basic monetary unit (cent for
example).
type Money int64

func Add(x, y Money) Money {
return x + y
}

func Sub(x, y Money) Money {
return x - y
}

//Money string representation in basic monetary unit ($)
func (m Money) String() string {
return fmt.Sprintf("%d", int64(m))
}

type Mtest struct {
Number int
Oper func(Money, Money) Money
X, Y Money
}

func exec(f func(Money, Money) Money, x, y Money) Money {
return f(x, y)
}

func main() {

ma := []Mtest{
{1, Add, Money(2060), Money(3041)},
}
for _, val := range ma {
fmt.Println(exec(val.Oper, val.X, val.Y).String())
}
}

Evan Shaw

unread,
May 14, 2011, 7:10:34 PM5/14/11
to bsr, golang-nuts
On Sun, May 15, 2011 at 10:51 AM, bsr <bsr...@gmail.com> wrote:
> //Money string representation in basic monetary unit ($)
> func (m Money) String() string {
>        return fmt.Sprintf("%d", int64(m))
> }

Instead of using fmt, I'd look in the package strconv and use Uitob64
as inspiration. Alter it so it's always base 10 and inserts a decimal
in the right place and that's probably what you want.

- Evan

peterGo

unread,
May 14, 2011, 7:18:17 PM5/14/11
to golang-nuts
bsr,

> The program
> outputs 5101, how can I display 51.01. %.2d as formatter did not work.

//Money string representation in basic monetary unit ($.¢)
func (m Money) String() string {
return fmt.Sprintf("%d.%02d", int64(m)/100, int64(m)%100)
}

Peter

bsr

unread,
May 14, 2011, 7:24:31 PM5/14/11
to golang-nuts
Evan, Peter ... thanks again for your help... One quick qn... I read
about Overflow in the spec ...

"For signed integers, the operations +, -, *, and << may legally
overflow and the resulting value exists and is deterministically
defined by the signed integer representation, the operation, and its
operands. No exception is raised as a result of overflow. A compiler
may not optimize code under the assumption that overflow does not
occur. For instance, it may not assume that x < x + 1 is always true."

since overflow doesn't cause any error, what is the best way to detect
and prevent the user from entering values which cause overflow !

thanks,
bsr.

Steven

unread,
May 14, 2011, 8:02:29 PM5/14/11
to bsr, golang-nuts
On Saturday, May 14, 2011, bsr <bsr...@gmail.com> wrote:
> Evan, Peter ... thanks again for your help... One quick qn... I read
> about Overflow in the spec ...
>
> "For signed integers, the operations +, -, *, and << may legally
> overflow and the resulting value exists and is deterministically
> defined by the signed integer representation, the operation, and its
> operands. No exception is raised as a result of overflow. A compiler
> may not optimize code under the assumption that overflow does not
> occur. For instance, it may not assume that x < x + 1 is always true."
>
> since overflow doesn't cause any error, what is the best way to detect
> and prevent the user from entering values which cause overflow !
>
> thanks,
> bsr

Only accept clients with low incomes.

But seriously, are you panning on handling amounts anywhere near
$92,233,720,368,547,758.07? Picture how big you think, practically,
you might be going, and just be sure (you probably won't have to worry
about it) that your arithmetic always fits in the headroom.

peterGo

unread,
May 14, 2011, 8:35:53 PM5/14/11
to golang-nuts
bsr,

> Also please give a hint on how can I detect overflow (or how to
> prevent the user from doing an overflow operation).

For addition, do you mean something like this.

func Add(x, y Money) Money {
r := x + y
if y >= 0 {
if r < x {
panic("Money Add: overflow")
}
} else {
if r > x {
panic("Money Add: overflow")
}
}
return r
}

Peter

On May 14, 6:51 pm, bsr <bsr...@gmail.com> wrote:

peterGo

unread,
May 14, 2011, 8:47:01 PM5/14/11
to golang-nuts
Steven,

Calculate the U.S national debt in Zimbabwe cents.

What do you think of the design of the Reserve Bank of Zimbabwe 100
trillion dollar note?

Peter

Brad Fitzpatrick

unread,
May 14, 2011, 8:47:26 PM5/14/11
to peterGo, golang-nuts
On Sat, May 14, 2011 at 5:35 PM, peterGo <go.pe...@gmail.com> wrote:
bsr,

> Also please give a hint on how can I detect overflow (or how to
> prevent the user from doing an overflow operation).

For addition, do you mean something like this.

func Add(x, y Money) Money {
       r := x + y
       if y >= 0 {
               if r < x {
                       panic("Money Add: overflow")
               }

... mo problems

Message has been deleted

Steven

unread,
May 15, 2011, 12:25:53 AM5/15/11
to peterGo, golang-nuts
On Saturday, May 14, 2011, peterGo <go.pe...@gmail.com> wrote:
> Steven,
>
> Calculate the U.S national debt in Zimbabwe cents.
>
> What do you think of the design of the Reserve Bank of Zimbabwe 100
> trillion dollar note?
>
> Peter
>


Thanks Peter, in that case, we should account for the case where
people are using individual atoms of precious metals as currency. It
might take more than the worlds total computational capacity to
perform a single transaction, but we'll work it out eventually.

I'm sure if bsr wants to use Zimbabwean currency with his software,
he'll answer my question accordingly and design his software around
it. As I said, set parameters on your system, make sure you have room
to operate within them, and enforce them to the degree required by
your use case.

Florian Weimer

unread,
May 22, 2011, 9:36:11 AM5/22/11
to peterGo, golang-nuts
* peterGo:

> bsr,
>
>> Also please give a hint on how can I detect overflow (or how to
>> prevent the user from doing an overflow operation).
>
> For addition, do you mean something like this.
>
> func Add(x, y Money) Money {
> r := x + y
> if y >= 0 {
> if r < x {
> panic("Money Add: overflow")
> }
> } else {
> if r > x {
> panic("Money Add: overflow")
> }
> }
> return r
> }

Or this:

func Add(x, y Money) Money {
r := x + y

if (r ^ x) & (r ^ y) < 0 {


panic("Money Add: overflow")
}
return r
}

"Hacker's Delight" by Henry S. Warren contains lots of such goodies.

Francis

unread,
Mar 27, 2015, 7:05:47 AM3/27/15
to golan...@googlegroups.com
I don't know your requirements or your applications, but...

If you are writing an application that will handle other people's money and take any level of responsibility for it do not use float32/64.

When I worked on financial software in London we used java's BigDecimal with a fixed rounding. The golang standard library does not have a BigDecimal equivalent, the closest is big.Rat (a perfect precision rational number, i.e. a fraction).

I have used big.Rat to represent money and it has the significant drawback that it is perfect precision. The numbers representing the fractions quickly became truly enormous, more than 500 digits in both the numerator and denominator, when testing the system by hand. This caused performance to nose-dive and gave us significant storage problems (we were using google's app engine)

There is a package inf developed by a gentleman by the name of Peter. Docs can be found at


I believe 


will get the code for you.

This package is working well for us right now. If you are representing money in any serious application I would expect that a package like this is mandatory.

P.S. I realise this post is pretty old, but it does come up near the top if you google "golang currency"

Manlio Perillo

unread,
Mar 27, 2015, 9:45:59 AM3/27/15
to golan...@googlegroups.com
Il giorno sabato 14 maggio 2011 22:47:49 UTC+2, peterGo ha scritto:
bsr,

Floating-point arithmetic is an approximation. People hate it when you
make approximations about their money.

What Every Computer Scientist Should Know About Floating-Point
Arithmetic
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html

Use int64 as cents to represent dollars and cents. Watch for
truncation and rounding, particularly in intermediate results.

Using int64 as cents is not that simple. 
1 cents / 2 = 0 cents, as an example, and I'm not sure banking software works this way.


> [...]

Regards  Manlio

Eduard Castany

unread,
Mar 27, 2015, 9:49:13 AM3/27/15
to golan...@googlegroups.com
I'm sure it works this way. But then the author sends that lost cent to his bank account.

El divendres, 27 març de 2015 14:45:59 UTC+1, Manlio Perillo va escriure:

chris dollin

unread,
Mar 27, 2015, 10:15:47 AM3/27/15
to Manlio Perillo, golang-nuts
"Watch for truncation and rounding".

Chris

--
Chris "allusive" Dollin

Manlio Perillo

unread,
Mar 27, 2015, 11:22:29 AM3/27/15
to chris dollin, golang-nuts
If you use int64 to represents 1 cent, what can you do about this operation?
Note that I'm not talking about intermediate results.

Another problem with this representation is that:
1) not all currency have a two digit precision 

2) the precision used to format money is not the same as the one used during
    computations


Regards  Manlio

Eduard Castany

unread,
Mar 27, 2015, 11:55:45 AM3/27/15
to golan...@googlegroups.com, ehog....@googlemail.com
Maybe use big.Rat for intermediate results and then store only the legally rounded number.

El divendres, 27 març de 2015 16:22:29 UTC+1, Manlio Perillo va escriure:

Wojciech S. Czarnecki

unread,
Mar 27, 2015, 1:27:34 PM3/27/15
to Manlio Perillo, golan...@googlegroups.com
Dnia 2015-03-27, o godz. 06:45:59
Manlio Perillo <manlio....@gmail.com> napisał(a):


> Using int64 as cents is not that simple.
> 1 cents / 2 = 0 cents, as an example, and I'm not sure banking software
> works this way.

It does. Just computational unit is scaled down. E.g. 1cu = 1/10**4 MU.
Then int64 can represent amount of +/- 922'337'203'685'477 currency units.

Rounding remainders must be dealt with according to local (and
international) accounting laws.

>
> Regards Manlio
>

--
Wojciech S. Czarnecki
^oo^ OHIR-RIPE

Manlio Perillo

unread,
Mar 27, 2015, 1:34:57 PM3/27/15
to golan...@googlegroups.com, ehog....@googlemail.com

Il giorno venerdì 27 marzo 2015 16:55:45 UTC+1, Eduard Castany ha scritto:
Maybe use big.Rat for intermediate results and then store only the legally rounded number.


Using infinite precision will probably cause different results compared to other accounting software.
So the answer, IMHO, it to use an implementation that conforms to the banking requirements.

On http://en.wikipedia.org/wiki/Rounding#History, when speaking of the "banker's rounding", there is a link to
"The Introduction of the Euro and the Rounding of Currency Amounts"
from the European Commission
but I suspect there is no international standard.

> [...]

Regards  Manlio 

Manlio Perillo

unread,
Mar 27, 2015, 1:43:32 PM3/27/15
to Wojciech S. Czarnecki, golan...@googlegroups.com
On Fri, Mar 27, 2015 at 6:27 PM, Wojciech S. Czarnecki <oh...@fairbe.org> wrote:
Dnia 2015-03-27, o godz. 06:45:59
Manlio Perillo <manlio....@gmail.com> napisał(a):


> Using int64 as cents is not that simple.
> 1 cents / 2 = 0 cents, as an example, and I'm not sure banking software
> works this way.

It does. Just computational unit is scaled down. E.g. 1cu = 1/10**4 MU.
Then int64 can represent amount of +/- 922'337'203'685'477 currency units.


The O.P. wrote that `type Monetary uint64` represents 1 cents.
Moreover, what do you mean with MU? Is it a well defined, universal, concept?
With dollar and euro, what is the MU? 1 dollar or 1 cents?
Will the rounding be the same with dollars (2 decimal digits of precision), yen (0 decimal digits of precision) and Iraqi dinar (3 decimal digits of precision)?

> [...]

Regards  Manlio
Reply all
Reply to author
Forward
0 new messages