Use float or decimal for accounting application dollar amount?
Asked Answered
D

24

77

We are rewriting our legacy accounting system in VB.NET and SQL Server. We brought in a new team of .NET/ SQL Programmers to do the rewrite. Most of the system is already completed with the dollar amounts using floats. The legacy system language, I programmed in, did not have a float, so I probably would have used a decimal.

What is your recommendation?

Should the float or decimal data type be used for dollar amounts?

What are some of the pros and cons for either?

One con mentioned in our daily scrum was you have to be careful when you calculate an amount that returns a result that is over two decimal positions. It sounds like you will have to round the amount to two decimal positions.

Another con is all displays and printed amounts have to have a format statement that shows two decimal positions. I noticed a few times where this was not done and the amounts did not look correct. (i.e. 10.2 or 10.2546)

A pro is the float-only approach takes up eight bytes on disk where the decimal would take up nine bytes (decimal 12,2).

Deucalion answered 15/9, 2008 at 4:55 Comment(2)
Go back and get rid of your floats.Backandforth
Actual banking and settlement systems deployed today often use binary floating point with a built in scale. So 1 dollar might be represented as 100000 float double. The idea there is that the systems gain performance from native float support on the cpu, and that more math operations are available than with decimal. The con is the devs have to know what they are doing .Millen
V
112

Should Float or Decimal data type be used for dollar amounts?

The answer is easy. Never floats. NEVER!

Floats were according to IEEE 754 always binary, only the new standard IEEE 754R defined decimal formats. Many of the fractional binary parts can never equal the exact decimal representation.

Any binary number can be written as m/2^n (m, n positive integers), any decimal number as m/(2^n*5^n). As binaries lack the prime factor 5, all binary numbers can be exactly represented by decimals, but not vice versa.

0.3 = 3/(2^1 * 5^1) = 0.3

0.3 = [0.25/0.5] [0.25/0.375] [0.25/3.125] [0.2825/3.125]

          1/4         1/8         1/16          1/32

So you end up with a number either higher or lower than the given decimal number. Always.

Why does that matter? Rounding.

Normal rounding means 0..4 down, 5..9 up. So it does matter if the result is either 0.049999999999.... or 0.0500000000... You may know that it means 5 cent, but the the computer does not know that and rounds 0.4999... down (wrong) and 0.5000... up (right).

Given that the result of floating point computations always contain small error terms, the decision is pure luck. It gets hopeless if you want decimal round-to-even handling with binary numbers.

Unconvinced? You insist that in your account system everything is perfectly ok? Assets and liabilities equal? Ok, then take each of the given formatted numbers of each entry, parse them and sum them with an independent decimal system!

Compare that with the formatted sum. Oops, there is something wrong, isn't it?

For that calculation, extreme accuracy and fidelity was required (we used Oracle's FLOAT) so we could record the "billionth's of a penny" being accured.

It doesn't help against this error. Because all people automatically assume that the computer sums right, and practically no one checks independently.

Vinegary answered 15/9, 2008 at 20:35 Comment(4)
But do make sure to use at least 4 decimal places in the decimal field if you want to do calculations on it especially division.Pehlevi
And make sure that you know that (by default) $0.045 rounds to $0.04 and $0.055 rounds to $0.06Allisan
For those unsure by what Keith means, Decimal types use a different kind of rounding. It seems to be commonly called "bankers' rounding" but Wikipedia has a number of alternative names: round half to even, unbiased rounding, convergent rounding, statistician's rounding, Dutch rounding, Gaussian rounding, or bankers' rounding (en.wikipedia.org/wiki/…).Wheatworm
Another thing to keep in mind is that Decimal.Round and String.Format give different results: Decimal.Round(0.045M,2) = 0.04 and String.Format("{0:0.00}",0.045M) = 0.05Rana
F
47

This photo answers:

photo1,C.O.

This is another situation: man from Northampton got a letter stating his home would be seized if he didn't pay up zero dollars and zero cents!

photo2,C.O.

Forwarder answered 23/10, 2010 at 0:4 Comment(3)
This made me laugh. Way to go, Best Buy.Wakerobin
I got a bill for $0.01 from a phone company every month for year. So I paid them $0.02 online, then got a bill for -$0.01 for six months, then it stopped.Samiel
Well, there will be plenty of maintenance jobs to clean up this mess.Function
P
23

First you should read What Every Computer Scientist Should Know About Floating Point Arithmetic. Then you should really consider using some type of fixed point / arbitrary-precision number package (e.g., Java BigNum or Python decimal module). Otherwise, you'll be in for a world of hurt. Then figure out if using the native SQL decimal type is enough.

Floats and doubles exist(ed) to expose the fast x87 floating-point coprocessor that is now pretty much obsolete. Don't use them if you care about the accuracy of the computations and/or don't fully compensate for their limitations.

Pinstripe answered 15/9, 2008 at 8:4 Comment(2)
While learning more about floating point is useful, using the decimal type in C# is akin to using a fixed point / arbitrary-precision number package as you suggest, built-in to the language. See msdn.microsoft.com/en-us/library/system.decimal.aspx for an explanation of how decimal stores precise powers of 10 with decimals instead of powers of 2 for the decimal component (it's basically an int with a decimal placement component).Cressida
"to expose the fast x87 fp that is now pretty much obsolete", thats simply not true floating point numbers are still one of the most used datatypes on computers, e.g. simulations, games, signals processing...Oldfashioned
S
10

Just as an additional warning, SQL Server and the .NET framework use a different default algorithm for rounding. Make sure you check out the MidPointRounding parameter in Math.Round(). .NET framework uses bankers' rounding by default and SQL Server uses Symmetric Algorithmic Rounding. Check out the Wikipedia article here.

Stylite answered 15/9, 2008 at 13:21 Comment(1)
What name does "Symmetric Algorithmic Rounding" have in the Wikipedia article? Or is it not covered there? What is "Symmetric Algorithmic Rounding"? Can you add a reference?Function
W
7

Ask your accountants! They will frown upon you for using float. Like David Singer said, use float only if you don't care for accuracy. Although I would always be against it when it comes to money.

In accounting software is not acceptable a float. Use decimal with four decimal points.

Wileywilfong answered 15/9, 2008 at 5:22 Comment(0)
A
6

Floating points have unexpected irrational numbers.

For instance you can't store 1/3 as a decimal, it would be 0.3333333333... (and so on)

Floats are actually stored as a binary value and a power of 2 exponent.

So 1.5 is stored as 3 x 2 to the -1 (or 3/2)

Using these base-2 exponents create some odd irrational numbers, for instance:

Convert 1.1 to a float and then convert it back again, your result will be something like: 1.0999999999989

This is because the binary representation of 1.1 is actually 154811237190861 x 2^-47, more than a double can handle.

More about this issue on my blog, but basically, for storage, you're better off with decimals.

On Microsoft SQL server you have the money data type - this is usually best for financial storage. It is accurate to 4 decimal positions.

For calculations you have more of a problem - the inaccuracy is a tiny fraction, but put it into a power function and it quickly becomes significant.

However decimals aren't very good for any sort of maths - there's no native support for decimal powers, for instance.

Allisan answered 15/9, 2008 at 9:59 Comment(5)
"irrational" isn't the word you're looking for. 1/3 is still rational, but it doesn't have a finite binary representation...Lima
Yeah, I know - I'm just not sure what else to call it: a number that can't be represented is a bit too wordy.Allisan
They are approximations, but then numbers that could be represented could be approximated too. An actual irrational number is one that cannot be represented by any integer fraction, regardless of base. These are numbers that can be represented in base 10, but can't in base 2.Allisan
A number with a non-terminating decimal representation – that is too wordy!Mimas
Perhaps you could say that floating point numbers typically store unexpected, and irrelevant, fractional values.Mimas
D
6

A bit of background here....

No number system can handle all real numbers accurately. All have their limitations, and this includes both the standard IEEE floating point and signed decimal. The IEEE floating point is more accurate per bit used, but that doesn't matter here.

Financial numbers are based on centuries of paper-and-pen practice, with associated conventions. They are reasonably accurate, but, more importantly, they're reproducible. Two accountants working with various numbers and rates should come up with the same number. Any room for discrepancy is room for fraud.

Therefore, for financial calculations, the right answer is whatever gives the same answer as a CPA who's good at arithmetic. This is decimal arithmetic, not IEEE floating point.

Defoliant answered 9/2, 2009 at 16:42 Comment(1)
I feel this answer makes more sense in general. I read several similar questions and answers, talking about accuracy, rounding etc. However, I still feel weird about those answers and something is missing. The word “reproducible” seems to be the key hereMcfarlane
S
5

Use SQL Server's decimal type.

Do not use money or float.

money uses four decimal places and is faster than using decimal, but suffers from some obvious and some not so obvious problems with rounding (see this connect issue).

Spendthrift answered 3/11, 2008 at 1:0 Comment(1)
See @David Thornley's answer. It may be that the money type most closely reproduces accounting conventions, however (in)approximate they are.Mccaleb
S
5

I'd recommend using 64-bit integers that store the whole thing in cents.

Surfactant answered 3/11, 2008 at 1:8 Comment(5)
With the obvious caveat that partial-cent values (ie. $0.015) cannot be represented at all. A reasonable limitation for most apps.Dilettantism
Simple solution: Store it in thousands of cents.. I store the stuff in millionths of the currency in question..Road
Check your overflow. Millionths of cents overflows at just over 20 billion dollars. Thousanths of cents at 20 trillion (which may or may not be acceptable), while cents is 20 quadrillion (which I deem safe).Surfactant
@Marenz: At any given stage of calculation, it should often be possible to define a minimum-sized unit upon which the calculation will be performed, and have no round-off errors of any magnitude occur at any points other than when things are explicitly rounded. If one buys five thousand of something at 3 for $1, the total price should typically be $1666.67 (5000/3, rounded to the penny), rather than $1666.66667 (5000/3, rounded to 1/1000 penny) the or $1666.65 (0.33333 times 5000).Yeorgi
Cents? No pennies, then?Function
F
4

Floats are not exact representations, precision issues are possible, for example when adding very large and very small values. That's why decimal types are recommended for currency, even though the precision issue may be sufficiently rare.

To clarify, the decimal 12,2 type will store those 14 digits exactly, whereas the float will not as it uses a binary representation internally. For example, 0.01 cannot be represented exactly by a floating point number - the closest representation is actually 0.0099999998

Fuld answered 15/9, 2008 at 4:58 Comment(2)
Decimals are not exact either, unless they are infinite precision.Shockey
0.1 can be stored exactly in a Decimal field. Decimals are not exact for every number, but are exact for most (some?) common monetary amounts. Sometimes.Dilettantism
B
4

The only reason to use Float for money is if you don't care about accurate answers.

Barilla answered 15/9, 2008 at 5:0 Comment(0)
I
4

For a banking system I helped develop, I was responsible for the "interest accrual" part of the system. Each day, my code calculated how much interest had been accrued (earnt) on the balance that day.

For that calculation, extreme accuracy and fidelity was required (we used Oracle's FLOAT) so we could record the "billionth's of a penny" being accrued.

When it came to "capitalising" the interest (ie. paying the interest back into your account) the amount was rounded to the penny. The data type for the account balances was two decimal places. (In fact it was more complicated as it was a multi-currency system that could work in many decimal places - but we always rounded to the "penny" of that currency). Yes - there where "fractions" of loss and gain, but when the computers figures were actualised (money paid out or paid in) it was always REAL money values.

This satisfied the accountants, auditors and testers.

So, check with your customers. They will tell you their banking/accounting rules and practices.

Irishman answered 15/9, 2008 at 10:42 Comment(3)
Billionths of a penny is 0.01^e-9 - there is absolutely no reason to use Oracle's FLOAT here for "extreme accuracy and fidelity", since it is a floating-point representation, which is an approximate number rather than an exact number. TSQL's DECIMAL(38,18) would be more accurate. Without you explaining how you handled multi-currency, I am skeptical you are error-free. If the testers were converting from the Euro to the Zimbabwe dollar, they might see a real rounding problem.Couchant
Just to clarify, I used floats for the interest accrual process. Decimals were used for the actual transactions (when the accrued interest was paid out). At the time the system was single currency. If I had my time again, I probably would have not used floats. :)Irishman
Bankers' rounding?Function
A
3

Even better than using decimals is using just plain old integers (or maybe some kind of bigint). This way you always have the highest accuracy possible, but the precision can be specified. For example the number 100 could mean 1.00, which is formatted like this:

int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);

If you like to have more precision, you can change the 100 to a bigger value, like: 10 ^ n, where n is the number of decimals.

Astro answered 16/9, 2008 at 9:58 Comment(2)
You should do this if you don't have a good fixed point type. The upside is that you get to determine where the decimal is, the downside is that you're going to screw it up. If you can get a fixed-point type, you won't have to worry about it.Desorb
That is already two magic numbers, presumably the same.Function
P
3

Another thing you should be aware of in accounting systems is that no one should have direct access to the tables. This means all access to the accounting system must be through stored procedures.

This is to prevent fraud, not just SQL injection attacks. An internal user who wants to commit fraud should not have the ability to directly change data in the database tables, ever. This is a critical internal control on your system.

Do you really want some disgruntled employee to go to the backend of your database and have it start writing them checks? Or hide that they approved an expense to an unauthorized vendor when they don't have approval authority? Only two people in your whole organization should be able to directly access data in your financial database, your database administrator (DBA) and his backup. If you have many DBAs, only two of them should have this access.

I mention this because if your programmers used float in an accounting system, likely they are completely unfamiliar with the idea of internal controls and did not consider them in their programming effort.

Pehlevi answered 9/2, 2009 at 16:55 Comment(0)
V
2

You can always write something like a Money type for .NET.

Take a look at this article: A Money type for the CLR. The author did an excellent work in my opinion.

Venom answered 21/9, 2008 at 6:12 Comment(0)
H
2

I had been using SQL's money type for storing monetary values. Recently, I've had to work with a number of online payment systems and have noticed that some of them use integers for storing monetary values. In my current and new projects I've started using integers and I'm pretty content with this solution.

Hamal answered 9/2, 2009 at 14:9 Comment(3)
I am assuming you are using the ROUND verb in your procedures?Deucalion
If you mean on the SQL side then NO. I prefer the DAL to return the integer as is in the DB. It's in the Business Logic Layer that I do the transformation. int cents = value % 100; int dollars = (value - cents) / 100; With .NET 3.5 I have an extension method for that.Hamal
@Gerhard Weiss: It sounds like a rhetorical question. Is it?Function
G
2

Out of the 100 fractions n/100, where n is a natural number such that 0 <= n and n < 100, only four can be represented as floating point numbers. Take a look at the output of this C program:

#include <stdio.h>

int main()
{
    printf("Mapping 100 numbers between 0 and 1 ");
    printf("to their hexadecimal exponential form (HEF).\n");
    printf("Most of them do not equal their HEFs. That means ");
    printf("that their representations as floats ");
    printf("differ from their actual values.\n");
    double f = 0.01;
    int i;
    for (i = 0; i < 100; i++) {
        printf("%1.2f -> %a\n",f*i,f*i);
    }
    printf("Printing 128 'float-compatible' numbers ");
    printf("together with their HEFs for comparison.\n");
    f = 0x1p-7; // ==0.0071825
    for (i = 0; i < 0x80; i++) {
        printf("%1.7f -> %a\n",f*i,f*i);
    }
    return 0;
}
Goraud answered 21/10, 2010 at 20:19 Comment(1)
For the sake of it, I copied the above code and ran it in codepad. codepad.org/03hAQZwq This includes the output.Mauricio
C
1

Have you considered using the money-data type to store dollar-amounts?

Regarding the con that decimal takes up one more byte, I would say don't care about it. In 1 million rows you will only use 1 more MB and storage is very cheap these days.

Cartesian answered 15/9, 2008 at 4:59 Comment(1)
Do not use the money datatype. (It's a hangover from SyBase.)Spendthrift
S
1

Whatever you do, you need to be careful of rounding errors. Calculate using a greater degree of precision than you display in.

Shockey answered 15/9, 2008 at 4:59 Comment(0)
W
1

You will probably want to use some form of fixed point representation for currency values. You will also want to investigate banker's rounding (also known as "round half to even"). It avoids bias that exist in the usual "round half up" method.

Wagoner answered 15/9, 2008 at 13:19 Comment(0)
L
0

Your accountants will want to control how you round. Using float means that you'll be constantly rounding, usually with a FORMAT() type statement, which isn't the way you want to do it (use floor / ceiling instead).

You have currency datatypes (money, smallmoney), which should be used instead of float or real. Storing decimal (12,2) will eliminate your roundings, but will also eliminate them during intermediate steps - which really isn't what you'll want at all in a financial application.

Louella answered 15/9, 2008 at 12:48 Comment(0)
M
0

Always use Decimal. Float will give you inaccurate values due to rounding issues.

Marquis answered 15/9, 2008 at 12:50 Comment(0)
A
0

Floating point numbers can only represent numbers that are a sum of negative multiples of the base - for binary floating point, of course, that's two.

There are only four decimal fractions representable precisely in binary floating point: 0, 0.25, 0.5 and 0.75. Everything else is an approximation, in the same way that 0.3333... is an approximation for 1/3 in decimal arithmetic.

Floating point is a good choice for computations where the scale of the result is what is important. It's a bad choice where you're trying to be accurate to some number of decimal places.

Ahmad answered 15/9, 2008 at 12:55 Comment(0)
B
0

This is an excellent article describing when to use float and decimal. Float stores an approximate value and decimal stores an exact value.

In summary, exact values like money should use decimal, and approximate values like scientific measurements should use float.

Here is an interesting example that shows that both float and decimal are capable of losing precision. When adding a number that is not an integer and then subtracting that same number float results in losing precision while decimal does not:

    DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float; 
    SET @Float1 = 54; 
    SET @Float2 = 3.1; 
    SET @Float3 = 0 + @Float1 + @Float2; 
    SELECT @Float3 - @Float1 - @Float2 AS "Should be 0";

Should be 0 
---------------------- 
1.13797860024079E-15

When multiplying a non integer and dividing by that same number, decimals lose precision while floats do not.

DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4); 
SET @Fixed1 = 54; 
SET @Fixed2 = 0.03; 
SET @Fixed3 = 1 * @Fixed1 / @Fixed2; 
SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1";

Should be 1 
--------------------------------------- 
0.99999999999999900
Benkley answered 23/10, 2010 at 1:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.