What is a safe Haskell data type to store and manipulate Money values?

Hello all, What is a good data type for storing and manipulating monetary values in a typical web application with payment processing? What are the pros and cons of various options like Data.Scientific, Rational and Decimal?

On Mon, Apr 3, 2017 at 9:50 PM Sandeep Cr
What is a good data type for storing and manipulating monetary values in a typical web application with payment processing? What are the pros and cons of various options like Data.Scientific, Rational and Decimal?
It depends on what sort of computation you'll be doing upon those monetary values and on the expectations of your users. It's typical that payment processing applications sacrifice precision and instead prefer to deal in exact quantities, in which case you probably want some fixed-precision type with cents, and you'll want to be quite careful about your arithmetic. I insist that this is not necessarily what you want depending on the expectations of your users and I would recommend reviewing the usual go-to piece of literature on the hazards of numerical computing: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html I would say you ought to pick your semantics first, and then find the types that match, which is indeed secondary. Avoid any universal nuance-free advice like "never use floating point" or "use integers for cents" as conventional wisdom on this topic is often wrong for many applications. Check whether regulatory compliance affects your choice of semantics.

Hi Manuel, Thank you for your reply. Some clarifications below...
It depends on what sort of computation you'll be doing upon those monetary values and on the expectations of your users. It's typical that payment processing applications sacrifice precision and instead prefer to deal in exact quantities
Is there a document which explains common use-cases for monetary values and best-practices for dealing with them? Any guidelines for which **Haskell** data-types to use for what kind of monetary calculations? Here is what we are doing in our app: * Powering web-based e-commerce checkout flows * Allowing store owners to input tax rates * Allowing end-customers to see product prices in different currencies (so, currency conversion) * Various reports to see total sales, total receivables, and total payables (basically a **very** small subset of small-business accounting) * Exporting data to full-fledged accounting systems, like Quickbooks and Xero. Based on this use-case, which Haskell data-type would you suggest? -- Saurabh.

Hi Saurabh, a good place to start might be the "Amount" type provided by the hleder-lib package: http://hackage.haskell.org/package/hledger-lib-1.2/docs/Hledger-Data-Types.h... It is based on 'Decimal': http://hackage.haskell.org/package/Decimal Best regards, Peter

On 4/04/2017, at 11:21 PM, Saurabh Nanda
wrote: Is there a document which explains common use-cases for monetary values and best-practices for dealing with them? Any guidelines for which **Haskell** data-types to use for what kind of monetary calculations?
I think you may need to talk to an accountant to find out what rounding rules are required for your application. As long as you are just adding and subtracting amounts of money, integer numbers of cents work fine. As soon as you start multiplying by percentages (to compute discounts, penalties, or taxes -- hello VAT, GST, &c -- it gets trickier.
Here is what we are doing in our app:
* Powering web-based e-commerce checkout flows * Allowing store owners to input tax rates
Ah. There you go. A 15% GST on $1.37 is 20.55 cents. We used to have a 12.5% GST, and 12.5% of $1.36 is 17.125 cents.
* Allowing end-customers to see product prices in different currencies (so, currency conversion) * Various reports to see total sales, total receivables, and total payables (basically a **very** small subset of small-business accounting)
There are Haskell data types that will let you compute sums and differences of money times percentages exactly. For final reporting, you will need to round. The rounding rule is *probably* the one you learned in school, but you really should check with a friendly accountant.
* Exporting data to full-fledged accounting systems, like Quickbooks and Xero.
Based on this use-case, which Haskell data-type would you suggest?
Data.Decimal doesn't do a lot, but it probably does almost everything you need. What it *doesn't* offer is operations with controlled rounding or much in the way of rounding methods (if I recall correctly IEEE decimal floats offer seven different rounding modes and the extra three, compared with binary floats, were added to support commercial calculations). It's not going to be terribly hard to program whatever rounding you need.

On 5 Apr 2017 04:15, "Richard A. O'Keefe"
On 4/04/2017, at 11:21 PM, Saurabh Nanda
wrote:
* Allowing end-customers to see product prices in different currencies (so, currency conversion) * Various reports to see total sales, total receivables, and total payables (basically a **very** small subset of small-business accounting)
There are Haskell data types that will let you compute sums and differences of money times percentages exactly. For final reporting, you will need to round. Careful here. I've worked on systems where one of the fundamental requirements is that any sums of columns of numbers representing amounts of money _must_ add up precisely. In a situation where you apply different tax rates to different items you could either calculate the tax on each one (and round it to £0.01) or group the items together into subtotals that all had the same tax rate, then calculate the tax on the subtotal (and round it to £0.01). What you couldn't do was keep track of the precise amount of tax on each item and round it at the very end for reporting purposes only, because you had to show the tax breakdown (using numbers rounded to £0.01) and if your working calculation was more precise then the numbers in the report wouldn't always quite add up, which would upset the auditors. This is why it's frequently recommended to use an integral type representing multiples of your smallest reporting unit for representing money. The rounding rule is *probably* the one you learned in school, but you really should check with a friendly accountant. The actual rounding rule in question seemed relatively unimportant compared with the requirement that numbers in reports must add up precisely, although I also agree that you should check with an accountant. They may not even mention the columns-of-numbers-must-add-up thing because that's so fundamental it almost goes without saying. Cheers, David

Am 06.04.2017 um 08:32 schrieb David Turner:
They may not even mention the columns-of-numbers-must-add-up thing because that's so fundamental it almost goes without saying.
I have seen very different policies on that. From "it must be exact" to "don't worry if the difference is less than 0.5*number-of-summands because then it could be a round-off error". It all depends on whether there's somebody who wants to double-check. For taxes calculations (any percentages actually), round-off errors are unavoidable. People tend to shift them around to minimize that error - that's why taxes are typically applied to sums, not to individual summands; the per-summand tax breakdowns are then taken to be purely informative and need not add up. And then there's "creative accounting" where these differences are larger than just a round-off error - that's what auditors are trying to find, and they don't want your numbers to add up because that in itself is relevant, they want your numbers to add up because it makes their jobs easier.

On 6/04/2017, at 6:32 PM, David Turner
wrote: Careful here. I've worked on systems where one of the fundamental requirements is that any sums of columns of numbers representing amounts of money _must_ add up precisely. In a situation where you apply different tax rates to different items you could either calculate the tax on each one (and round it to £0.01) or group the items together into subtotals that all had the same tax rate, then calculate the tax on the subtotal (and round it to £0.01). What you couldn't do was keep track of the precise amount of tax on each item and round it at the very end for reporting purposes only, because you had to show the tax breakdown (using numbers rounded to £0.01) and if your working calculation was more precise then the numbers in the report wouldn't always quite add up, which would upset the auditors.
For what it's worth, there are rounding algorithms that work on a whole bunch of numbers at once, ensuring that the total of the rounded numbers is equal to the total of the unrounded numbers.

On 2017-04-07 00:35, Richard A. O'Keefe wrote:
On 6/04/2017, at 6:32 PM, David Turner
wrote: Careful here. I've worked on systems where one of the fundamental requirements is that any sums of columns of numbers representing amounts of money _must_ add up precisely. In a situation where you apply different tax rates to different items you could either calculate the tax on each one (and round it to £0.01) or group the items together into subtotals that all had the same tax rate, then calculate the tax on the subtotal (and round it to £0.01). What you couldn't do was keep track of the precise amount of tax on each item and round it at the very end for reporting purposes only, because you had to show the tax breakdown (using numbers rounded to £0.01) and if your working calculation was more precise then the numbers in the report wouldn't always quite add up, which would upset the auditors. For what it's worth, there are rounding algorithms that work on a whole bunch of numbers at once, ensuring that the total of the rounded numbers is equal to the total of the unrounded numbers.
REFERENCES TO LITERATURE, PLEASE! (I'm not being sarcastic -- this would be very useful to almost anyone dealing with monetary amounts... anywhere.) Cheers,

On 7/04/2017, at 10:42 AM, Bardur Arantsson
wrote: On 2017-04-07 00:35, Richard A. O'Keefe wrote:
For what it's worth, there are rounding algorithms that work on a whole bunch of numbers at once, ensuring that the total of the rounded numbers is equal to the total of the unrounded numbers.
REFERENCES TO LITERATURE, PLEASE!
(I'm not being sarcastic -- this would be very useful to almost anyone dealing with monetary amounts... anywhere.)
I first saw mention of such an algorithm in a journal about 40 years ago. I have a more recent memory of a paper by Knuth in a volume of his collected papers that I cannot at the moment locate, but I believe this is the article: Two-Way Rounding Author: Donald E. Knuth Published in: · Journal SIAM Journal on Discrete Mathematics archive Volume 8 Issue 2, May 1995 Pages 281 - 290 Society for Industrial and Applied Mathematics Philadelphia, PA, USA table of contents doi>10.1137/S0895480194264757 Ah, FOUND IT. Selected Papers on Design of Algorithms Donald E. Knuth CSL Lecture Notes Number 191 CSLI Publications, Stanford, California. ISBN-13: 978-1-57586-582-9 ISBN-10: 1-57686-582-3 Chapter 16, pp 219-234 "Two-Way Rounding" "Given n real numbers 0 <= x1 < 1, ..., 0 <= xn < 1, and a permutation \sigma of {1,...,n}, we can always find integers x'1 \in {0,1}, ..., x'n \in {0,1} so that the partial sums x'1+...+x'k and x'\sigma[1]+...+x'\sigma[k] differ from the unrounded values x1+...+xk and x\sigma[1...+x\sigma[k] by at most n/(n+1) for 1 <= k <= n. The latter bound is the best possible." Section 1, An Application "Sometimes it is desirable to round "spreadsheet" data to larger units while preserving row and column totals and the grand total." This application to matrices has been studied by others. http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.421.2051&rep=rep1&type=pdf Rounding of Sequences and Matrices, with Applications "We show that any real matrix can be rounded to an integer matrix in such a way that the rounding errors of all row sums are less than one, and the rounding errors of all column sums as well as all sums of consecutive row entries are less than two. Such roundings can be computed in linear time." http://people.mpi-inf.mpg.de/~doerr/papers/unbimatround.pdf Unbiased Matrix Rounding "We show several ways to round a real matrix to an integer one such that the rounding errors in all rows and columns as well as the whole matrix are less than one. ... our roundings also have a rounding error of less than one in all initial intervals of rows and columns. Consequently, arbitrary intervals have an error of at most two. ... The same result can be obtained via (dependent) randomized rounding. This has the additional advantage that the rounding is unbiased." Here's a bunch of less formal discussions of the 1D case. http://stackoverflow.com/questions/32544646/round-vector-of-numerics-to-inte... http://stackoverflow.com/questions/792460/how-to-round-floats-to-integers-wh... https://explainextended.com/2009/09/21/rounding-numbers-preserving-their-sum... https://biostatmatt.com/archives/2902

2017-04-04 7:05 GMT+02:00 Manuel Gómez
[...] Check whether regulatory compliance affects your choice of semantics.
This is the most important advice: You'll probably run into legal trouble if you choose e.g. the wrong rounding mode, and for a good reason: If you handle millions of transactions in your system and sneakily always round cents "into your own pocket", you'll be a rich man. That's the reason behind the tons of rounding modes in e.g. http://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html. Ask your local lawyer which is the right one. ;-) I have serious doubts that you can handle such things with a floating point representation.

This is the most important advice: You'll probably run into legal trouble if you choose e.g. the wrong rounding mode, and for a good reason: If you handle millions of transactions in your system and sneakily always round cents "into your own pocket", you'll be a rich man. That's the reason behind the tons of rounding modes in e.g. http://docs.oracle.com/javase/ 8/docs/api/java/math/BigDecimal.html. Ask your local lawyer which is the right one. ;-) I have serious doubts that you can handle such things with a floating point representation.
That's an important point you raise. Leaving aside the legal aspects of this, what would be Haskell's equivalent of the BigDecimal format in Java (or Ruby)? Decimal (as used by hledger)? -- Saurabh.

`Data.Decimal.Decimal` is quite close to Java's `BigDecimal` in intent but
not quite the same: Java's `BigDecimal` has a 32-bit signed exponent
whereas Haskell's `Decimal`'s exponent is 8-bit and unsigned. The
difference shouldn't matter for the purposes of tracking money since
accountancy rules are generally designed to prefer (carefully specified)
rounding over keeping track of numbers with hundreds of decimal places.
On 4 April 2017 at 15:13, ALeX Kazik
Leaving aside the legal aspects of this, what would be Haskell's equivalent of the BigDecimal format in Java (or Ruby)? Decimal (as used by hledger)?
Rational?
ALeX. _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Am 04.04.2017 um 16:13 schrieb ALeX Kazik:
Leaving aside the legal aspects of this, what would be Haskell's equivalent of the BigDecimal format in Java (or Ruby)? Decimal (as used by hledger)?
Rational?
You'd be constantly be doing explicit calculations to get back to a denominator of 100. Plus you'd have to deal with those cases where the denominator is a divisor of 100, and you'd need explicit nonstandard conversion to strings.

On 4 April 2017 at 15:50, Joachim Durchholz
Am 04.04.2017 um 16:13 schrieb ALeX Kazik:
Leaving aside the legal aspects of this, what would be Haskell's
equivalent of the BigDecimal format in Java (or Ruby)? Decimal (as used by hledger)?
Rational?
You'd be constantly be doing explicit calculations to get back to a denominator of 100. Plus you'd have to deal with those cases where the denominator is a divisor of 100, and you'd need explicit nonstandard conversion to strings.
The same sort of fiddling-the-denominator problems also occur if you use `Decimal` - it's not normally useful to be able to represent things smaller than £0.01 (or whatever the smallest unit of your local currency is); I'd even go as far as to say it's useful to be able to be sure that you _can't_ represent such things (in the domain of basic accounting at least - more complex things may deal with smaller fractions of currency, so YMMV etc). Cheers,
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

On Tue, Apr 4, 2017 at 11:10 AM, David Turner wrote: it's not normally useful to be able to represent things smaller than £0.01
(or whatever the smallest unit of your local currency is) Currency conversion was specifically mentioned, and from what I have seen
the convention there is to go to tenths of the local smallest unit of
currency. Going to the smallest unit itself in the presence of currency
conversions is problematic.
--
brandon s allbery kf8nh sine nomine associates
allbery.b@gmail.com ballbery@sinenomine.net
unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Am 04.04.2017 um 17:10 schrieb David Turner:
On 4 April 2017 at 15:50, Joachim Durchholz
mailto:jo@durchholz.org> wrote: Am 04.04.2017 um 16:13 schrieb ALeX Kazik:
Rational?
You'd be constantly be doing explicit calculations to get back to a denominator of 100. Plus you'd have to deal with those cases where the denominator is a divisor of 100, and you'd need explicit nonstandard conversion to strings.
The same sort of fiddling-the-denominator problems also occur if you use `Decimal` - it's not normally useful to be able to represent things smaller than £0.01 (or whatever the smallest unit of your local currency is); I'd even go as far as to say it's useful to be able to be sure that you _can't_ represent such things (in the domain of basic accounting at least - more complex things may deal with smaller fractions of currency, so YMMV etc).
Decimal has a roundTo function, Rational does not.

Curious to know what StanChart is using to represent monetary values in its
Haskell code.
-- Saurabh.
On Tue, Apr 4, 2017 at 8:20 PM, Joachim Durchholz
Am 04.04.2017 um 16:13 schrieb ALeX Kazik:
Leaving aside the legal aspects of this, what would be Haskell's
equivalent of the BigDecimal format in Java (or Ruby)? Decimal (as used by hledger)?
Rational?
You'd be constantly be doing explicit calculations to get back to a denominator of 100. Plus you'd have to deal with those cases where the denominator is a divisor of 100, and you'd need explicit nonstandard conversion to strings.
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

From memory: At StanChart the Haskell library is mostly used in Financial Market for derivatives pricing, the accounting and actual payment processing is done by other back-office softwares. So Double was used to represent "simulated" values (the price of a derivative being the NPV of expected cash flows) and for computing actual payments rounding rules are used with the relevant decimal (based on ISO 4217 tables).
On Tue, Apr 4, 2017 at 5:16 PM Saurabh Nanda
Curious to know what StanChart is using to represent monetary values in its Haskell code.
-- Saurabh.
On Tue, Apr 4, 2017 at 8:20 PM, Joachim Durchholz
wrote: Am 04.04.2017 um 16:13 schrieb ALeX Kazik:
Leaving aside the legal aspects of this, what would be Haskell's equivalent of the BigDecimal format in Java (or Ruby)? Decimal (as used by hledger)?
Rational?
You'd be constantly be doing explicit calculations to get back to a denominator of 100. Plus you'd have to deal with those cases where the denominator is a divisor of 100, and you'd need explicit nonstandard conversion to strings.
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
-- http://www.saurabhnanda.com _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
-- Frederic Cogny +33 7 83 12 61 69

Am 04.04.2017 um 15:46 schrieb Sven Panne:
2017-04-04 7:05 GMT+02:00 Manuel Gómez
mailto:targen@gmail.com>: [...] Check whether regulatory compliance affects your choice of semantics.
This is the most important advice: You'll probably run into legal trouble if you choose e.g. the wrong rounding mode,
I have yet to hear about *any* case where that led to legal issues. Most cases would be laughed out of court anyway - nobody litigates for 0.5 cents. The PR backlash could be much worse.
and for a good reason: If you handle millions of transactions in your system and sneakily always round cents "into your own pocket", you'll be a rich man.
Actually they're insignificant unless you're doing very specialized stuff (microtransactions, long-running debt repayment plans). E.g. for currency conversion, you set a spread which is typically way above any round-off profit you could make. I.e. from a business perspective, it's pointless to try that under most circumstances.
That's the reason behind the tons of rounding modes in e.g. http://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html.
I think these rounding modes have two origins, one of them in the IEEE standard for floating-point numbers, the other is financial calculations. Note that it doesn't matter much which of the modes you use as long as it's reasonable (that's why some countries have "banker's rounding"). You *might* still run into regulations, of course, but I'm pretty sure that if you stick with a specific rounding mode it's essentially okay. Otherwise it would be pretty difficult to run a business with branches in Europe and America, after all.
I have serious doubts that you can handle such things with a floating point representation.
Yeah, floating point is a no-go for monetary values. They can be the right tool when combining percentages, or actualy any factor that is supposed to be multiplied with a monetary value.

On 5/04/2017, at 3:53 AM, Joachim Durchholz
wrote: Yeah, floating point is a no-go for monetary values. They can be the right tool when combining percentages, or actualy any factor that is supposed to be multiplied with a monetary value.
*Binary* floats, yes. But IEEE *decimal* floats were specifically designed to support commercial calculations, and the current COBOL standard offers decimal floats as one of three interpretations of COBOL fixed-point arithmetic. It helps to have hardware that supports decimal floats, of course. Probably no surprise that current z/Series and Power machines do, most others don't (yet).

Am 05.04.2017 um 05:20 schrieb Richard A. O'Keefe:
On 5/04/2017, at 3:53 AM, Joachim Durchholz
wrote: Yeah, floating point is a no-go for monetary values. They can be the right tool when combining percentages, or actualy any factor that is supposed to be multiplied with a monetary value.
*Binary* floats, yes. But IEEE *decimal* floats
Ah, right, I forgot about these.
were specifically designed to support commercial calculations, and the current COBOL standard offers decimal floats as one of three interpretations of COBOL fixed-point arithmetic.
What languages besides Cobol actually make use of them?
It helps to have hardware that supports decimal floats, of course. Probably no surprise that current z/Series and Power machines do, most others don't (yet).
Are there any plans by Intel or AMD to support them? I'm not aware of any, but you never know.

On 5/04/2017, at 5:08 PM, Joachim Durchholz
wrote: What languages besides Cobol actually make use of [decimal floats]?
Aside from obvious things like PL/I and ABAP, there is an official way to use decimal floats in C, if you have them. gcc is *part* of the way there. https://gcc.gnu.org/onlinedocs/gcc/Decimal-Float.html Having used gcc on a machine with no hardware binary floats, where binary floats were emulated, I'd rather like to use decimal floats on my Macs. However gcc 5.3.0 on my laptop *recognises* decimal floats but just says that it's not supported on this target. The decNumber library's licences are perfectly compatible with gcc. Solaris Studio supports decimal floats on SPARCX[+] processors, for some value of "support" strictly between 0 and 1. (It knows the _Decimal64 type but you have to call special intrinsics to get the arithmetic instructions, though you _do_ get them.)
Are there any plans by Intel or AMD to support them? I'm not aware of any, but you never know.
Perhaps if Microsoft realised that _Decimal128 is exactly what they want for Excel, and put pressure on Intel? Intel do provide a decimal floating point *library* https://software.intel.com/en-us/articles/intel-decimal-floating-point-math-... http://www.netlib.org/misc/intel/ so in *some* sense they already support decimal floats. The date in the README file is "August 15, 2011", so we've had an "official" decimal floating point library for Intel systems for nearly 8 years (first release in 2009). [Amusingly, the only difference between LIBRARY/RUNOSX and LIBRARY/RUNLINUX is that the OSX version has a space after "./linuxbuild".] The code is in C and has support for big-endian machines, and eula.txt is very permissive, so it looks very much as though nobody needs to go without.

I once implemented a decimal library based around this PEP,
https://www.python.org/dev/peps/pep-0327/
Not sure if that is a good idea or not -- but it seemed like a good idea at
the time.
- jeremy
On Mon, Apr 3, 2017 at 7:48 PM, Sandeep Cr
Hello all,
What is a good data type for storing and manipulating monetary values in a typical web application with payment processing? What are the pros and cons of various options like Data.Scientific, Rational and Decimal?
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
participants (13)
-
ALeX Kazik
-
Bardur Arantsson
-
Brandon Allbery
-
David Turner
-
Frederic Cogny
-
Jeremy Shaw
-
Joachim Durchholz
-
Manuel Gómez
-
Peter Simons
-
Richard A. O'Keefe
-
Sandeep Cr
-
Saurabh Nanda
-
Sven Panne