I am being specific about handling large number of money values. Each value is precise only upto 2 decimal places. But the values will be passed around by a database and one or more web frameworks and there will be arithemetic operations.
Should I insist on decimal datatypes for numbers that need only 2 places of precision? Or are modern floating point implementations robust and standardized to avoid it?
Hell no, absolutely, and the issues are orthogonal, in that order. :-)
Floating point numbers, especially in binary, are never the right choice for fixed-point quantities, least of all those that expect precise fractions, like money values. First of all, they don't express all values of cents (or whatever fractional component) accurately, just like fixed-length decimal numbers can't express 1/3 correctly. Secondly, adding or subtracting small and very large floating point numbers doesn't always produce the result you expect, because of differences in "significance".
Decimal numbers are the way to go for currency calculations. If you absolutely must use binary numbers, use scaled fixed-point binary numbers - for example, compute everything in 1/100ths of your currency unit, and use binary integers to do it.
Lastly, this has nothing to do with "robustness" or "standardization" - it's got everything to do with picking a datatype that matches your data.
No, they are not precise enough. See the floating point guide for details.
Related
What is the adequate MySQL field type for weight (in kilograms) and height (meters) data?
That all depends. There are advantages and disadvantages to the different numeric types:
If you absolutely need the height/weight to be exactly a certain number of decimal places and not subject to the issues that floating point numbers can cause, use decimal() (decimal takes two parameters, the amount number of digits > 1 and the number of digits < 1, so decimal(5,2) would be able to store numbers as low as 0.01 and as high as 999.99). Fixed precision math is a bit slower, and it's more costly to store them because of the algorithms involved in doing it. It's absolutely mandatory to store currency values or any value you intend to do math with involving currency as a numeric(). If you're, for instance, paying people per pound or per meter for a baby, it would need to be numeric().
If you're willing to accept the possibility that some numeric values will be less precise than others use either FLOAT or DOUBLE depending on the amount of precision and the size of the numbers you're storing. Floating points are approximations based on the way computers do math, we use decimal points to mean out of powers of 10, computers do math in base2, the two are not directly comparable, so to get around that algorithms were developed for cases in which 100% precision is not required, but more speed is necessary that cannot be achieved by modeling it using collections of integers.
For more info:
http://dev.mysql.com/doc/refman/5.0/en/numeric-types.html
http://dev.mysql.com/doc/refman/5.0/en/fixed-point-types.html
http://dev.mysql.com/doc/refman/5.0/en/floating-point-types.html
This question has been asked many times before, but I've found conflicting opinions on the topic so I thought I would bring it up again in hopes of a more unified conclusion.
I would like to store a currency value in my database. Let's assume all entries are the same type of currency (USD for example) and that both positive and negative values are allowed.
My initial thought would be to store the value as a signed integer in terms of the smallest unit of the associated currency. For example, if I want to store the value $1.25, I would insert 125 into the database, since the smallest unit of USD is $0.01. The nice thing about this method is that MySQL will automatically round to the nearest integer. For example, if the dollar value is $1.259, I could insert 125.9, which would automatically be rounded and stored as 126 or $1.26.
So, what do you think? Is this a sound approach or is there a better way?
Financial data can be stored as DECIMAL(p,s) where p is the number of significant digits, and s is the scale (number of places to the right of the decimal point). See The MySQL documentation.
Also from O'Reilly's High Performance MySQL, chapter 3:
"...you should use DECIMAL only when you need exact results for
fractional numbers--for example, when storing financial data."
From O'Reilly's Learning MySQL:
DECIMAL is, "...A commonly used numeric type. Stores a fixed-point
number such as a salary or distance..."
And MySQL Pocket Reference:
DECIMAL "...Stores floating-point numbers where precision is
critical, such as for monetary values."
There is nothing wrong with the approach you describe. There is no right or wrong when it comes to this question.
You have to keep in mind that to display a $ value to the user, you would need to always do a division operation or some kind of formatting.
Will it be easier to debug your formulas/calculations if everything was in cents or dollars? Will you be displaying the values in cents or dollars? etc.
Keep it simple, but either way you'll have to use a decimal.
I am trying to figure out how to store 1/3, or any fraction which results in an infinitely repeating decimal value in MySQL. I cannot just use 3.333333 because it obviously does not total to 100. I have been reading about the float datatype but i'm not sure if this will work. Any help would be appreciated.
Thank you
You could potentially represent all rational numbers (including integers) as "numerator" and "denominator". So in your table you'd have a numerator and denominator columns, and your app would have logic to store numbers using that form.
You would still be unable to store irrational numbers precisely with this technique (i.e. if you want to store Pi, you'd need a fractional approximation anyway).
See here for what rational numbers are, so you can understand the limitations of this technique.
http://en.wikipedia.org/wiki/Rational_number
Store the numerator and the denominator in separate columns. It's really that simple. The real problem comes later when you want to add up all the fractions. Some languages have built-in facilities to do so, but I don't think MySQL does.
The problem is this, when I add two or more doubles from a table to a view, instead of giving me the right results, it adds a about ten or so more digits. For example 0.5+1.5=1.99999999998 or 5.5+8.5=14.0000000001. Any ideas?
(I know this is sort of n00b question and I remember having to deal with stuff like that in the exams at 9th grade, but I just cannot remember how I did it back then :P)
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_format
You can format numbers this way if thats what your after?
Adding 1 and 1 as floats or doubles should not result in anything but 2.
I find it hard to believe that 0.5 + 1.5 comes out to anything but 2.
All these numbers can be represented correctly in binary floating point.
I hate to say I don't believe your examples, but I don't. :-)
However, I do believe that you might have trouble with a number such as 1.1.
Why? Because 1/10 turns out to be a repeating decimal in binary.
The problem comes when trying to convert floating point numbers between decimal and binary representations. Some numbers make the trip fine, but others are only approximated.
However, if your examples really work like that, I have no idea what is happening, and I'd love to know.
floating point numbers are ALWAYS mere approximations. :-) If precision matters, figure out a way to use integers.
In my experience, when I used decimal datatype instead of float/double, I did always got the precise results.
This article explains the issue pretty well.
In a nutshell, very large, or very small floating point numbers can result in a loss of precision during calculations.
Don't expect integers when you work with floating point types.
Not sure if you're looking for an explanation or a solution, but since you've already gotten pointers to good explanations....
If you need your numbers to behave like integers, use integers. If you need a finite degree of precision (ie, two decimal places, for fields that represent money), store it as an integer anyway, but multiply it by whatever factor of 10 you need to get rid of the decimal when you put it into the database and divide by the same when you pull it back out.
So if you're representing a widget that costs $3.99, you put 399 into the WIDGET.cost field.
Dunno if that applies to your situation or not. But the general rule of thumb is that floating point numbers are ALWAYS mere approximations. :-) If precision matters, figure out a way to use integers.
In a previous project, I noticed that the price field was being stored as an int, rather than as a float. This is done by multiplying the actual value by 100, the reason being was to avoid running into floating point problems.
Is this a good practice that I should follow or is it unnecessary and only makes the data less transparent?
Interesting question.
I wouldn't actually choose float in the mysql environment. Too many problems in the past with precision with that datatype.
To me, the choice would be between int and decimal(18,4).
I've seen real world examples integers used to represent floating point values. The internals of JD Edwards datatables all do this. Quantities are typically divided by 10000. While I'm sure it's faster and smaller in-table, it just means that we're always having to CAST the ints to a decimal value if we want to do anything with them, especially division.
From a programming perspective, I'd always prefer to work with decimal for price ( or money in RDBMSs that support it ).
Floating point errors could cause you problems if you are multiplying large numbers. In general, financial calculations should never be done with floating point numbers where possible.
I think Decimal is good for this use.
While it would save you float-related issues, having prices saved as integers might lead to a problem where you end up charging 100 times the price to a customer. It could also confuse other programmers.
I have seen both solution used successfully on medium-size ecommerce websites, but my preference goes to using floats.