DOUBLE vs DECIMAL in MySQL

153,467

Solution 1

Actually it's quite different. DOUBLE causes rounding issues. And if you do something like 0.1 + 0.2 it gives you something like 0.30000000000000004. I personally would not trust financial data that uses floating point math. The impact may be small, but who knows. I would rather have what I know is reliable data than data that were approximated, especially when you are dealing with money values.

Solution 2

The example from MySQL documentation http://dev.mysql.com/doc/refman/5.1/en/problems-with-float.html (i shrink it, documentation for this section is the same for 5.5)

mysql> create table t1 (i int, d1 double, d2 double);

mysql> insert into t1 values (2, 0.00  , 0.00),
                             (2, -13.20, 0.00),
                             (2, 59.60 , 46.40),
                             (2, 30.40 , 30.40);

mysql> select
         i,
         sum(d1) as a,
         sum(d2) as b
       from
         t1
       group by
         i
       having a <> b; -- a != b

+------+-------------------+------+
| i    | a                 | b    |
+------+-------------------+------+
|    2 | 76.80000000000001 | 76.8 |
+------+-------------------+------+
1 row in set (0.00 sec)

Basically if you sum a you get 0-13.2+59.6+30.4 = 76.8. If we sum up b we get 0+0+46.4+30.4=76.8. The sum of a and b is the same but MySQL documentation says:

A floating-point value as written in an SQL statement may not be the same as the value represented internally.

If we repeat the same with decimal:

mysql> create table t2 (i int, d1 decimal(60,30), d2 decimal(60,30));
Query OK, 0 rows  affected (0.09 sec)

mysql> insert into t2 values (2, 0.00  , 0.00),
                             (2, -13.20, 0.00),
                             (2, 59.60 , 46.40),
                             (2, 30.40 , 30.40);
Query OK, 4 rows affected (0.07 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select
         i,
         sum(d1) as a,
         sum(d2) as b
       from
         t2
       group by
         i
       having a <> b;

Empty set (0.00 sec)

The result as expected is empty set.

So as long you do not perform any SQL arithemetic operations you can use DOUBLE, but I would still prefer DECIMAL.

Another thing to note about DECIMAL is rounding if fractional part is too large. Example:

mysql> create table t3 (d decimal(5,2));
Query OK, 0 rows affected (0.07 sec)

mysql> insert into t3 (d) values(34.432);
Query OK, 1 row affected, 1 warning (0.10 sec)

mysql> show warnings;
+-------+------+----------------------------------------+
| Level | Code | Message                                |
+-------+------+----------------------------------------+
| Note  | 1265 | Data truncated for column 'd' at row 1 |
+-------+------+----------------------------------------+
1 row in set (0.00 sec)

mysql> select * from t3;
+-------+
| d     |
+-------+
| 34.43 |
+-------+
1 row in set (0.00 sec)

Solution 3

We have just been going through this same issue, but the other way around. That is, we store dollar amounts as DECIMAL, but now we're finding that, for example, MySQL was calculating a value of 4.389999999993, but when storing this into the DECIMAL field, it was storing it as 4.38 instead of 4.39 like we wanted it to. So, though DOUBLE may cause rounding issues, it seems that DECIMAL can cause some truncating issues as well.

Share:
153,467

Related videos on Youtube

user327961
Author by

user327961

Updated on June 29, 2020

Comments

  • user327961
    user327961 almost 4 years

    OK, so I know there are tons of articles stating I shouldn't use DOUBLE to store money on a MySQL database, or I'll end up with tricky precision bugs. The point is I am not designing a new database, I am ask to find way to optimise an existing system. The newer version contains 783 DOUBLE typed columns, most of them used to store money or formula to compute money amount.

    So my first opinion on the subject was I should highly recommend a conversion from DOUBLE to DECIMAL in the next version, because the MySQL doc and everybody say so. But then I couldn't find any good argument to justify this recommandation, for three reasons :

    • We do not perform any calculation on the database. All operations are done in Java using BigDecimal, and MySQL is just used as a plain storage for results.
    • The 15 digits precision a DOUBLE offers is plenty enough since we store mainly amounts with 2 decimal digits, and occasionaly small numbers wit 8 decimal digits for formula arguments.
    • We have a 6 years record in production with no known issue of bug due to a loss of precision on the MySQL side.

    Even by performing operations on a 18 millons rows table, like SUM and complex multiplications, I couldn't perform a bug of lack of precision. And we don't actually do this sort of things in production. I can show the precision lost by doing something like

    SELECT columnName * 1.000000000000000 FROM tableName;

    But I can't figure out a way to turn it into a bug at the 2nd decimal digit. Most of the real issues I found on the internet are 2005 and older forum entries, and I couldn't reproduce any of them on a 5.0.51 MySQL server.

    So as long as we do not perform any SQL arithmetic operations, which we do not plan to do, are there any issue we should expect from only storing and retreiving a money amount in a DOUBLE column ?

    • rajah9
      rajah9 almost 13 years
      Do you calculate taxable amounts in Java and then round them according to contract before storing them? For example, if you sell a $1.47 item and have 8.25% local sales tax, you might need to record $0.121275 in tax. I am wondering in what form you are storing this kind of field in the DB, and whether you are rounding to $0.12 before you store (or rounding up to $0.13, depending on your locale).
    • user327961
      user327961 almost 13 years
      Yes, we calculate taxes in Java, and we store the item's price, the tax amount rounded to the 4th decimal and the total price rounded to the 2nd decimal. So in your example a row would contain 1.47, 0.1213 and 1.59. The 8.25% is stored somewhere else as 0.08250000 and is not repeated for each sales.
  • user327961
    user327961 almost 13 years
    Well this is not a technical answer to the case, but the point that makes me think the most is I personally would not trust financial data that uses floating point math. Most certainly many others would not 100% trust those data even if I spent a week trying to proove it safe in our usecases, and they would be right to doubt. Not having the client's trust in an audit is indeed an issue and is a good argument to recommend the switch from DOUBLE to DECIMAL.
  • Halil Özgür
    Halil Özgür almost 12 years
    "Your big question is: does anybody care if certain reports are off by a penny?" And if no: head to your box for writing a virus :P (re Office Space) Sorry, I just couldn't help it.
  • broadband
    broadband over 11 years
    I just tried this: CREATE TABLE IF NOT EXISTS exact ( n decimal(5,2) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; I inserted data like this: insert into exact (n) values(4.389999999993); there was a warning ofcourse because of the rounding. But it stored 4.39 not 4.38 like you wrote. I'm using mySQL 5.5.28-log
  • RandomSeed
    RandomSeed about 11 years
    Without the use of exact(), the inserted value is truncated, not rounded.
  • Stephen
    Stephen almost 11 years
    The truncation behavior is documented in the MySQL manual: "When such a column is assigned a value with more digits following the decimal point than are permitted by the specified scale, the value is converted to that scale. (The precise behavior is operating system-specific, but generally the effect is truncation to the permissible number of digits.)" I'd recommend explicitly specifying the rounding behavior using ROUND()
  • Roberto
    Roberto about 10 years
    "Basically if you sum a you get 0-13.2+59.6+30.4 = 76.8. If we sum up b we get 0+0+46.4+30.4=78.8." This has an error, the result is "a = 76.80000000000001, b = 76.8", as shown in the SQL result. The error is the 1 on the very end, but this is so small and is just a result of double encoding using binary not decimal.
  • broadband
    broadband about 9 years
    @markwatson it was a typo, you are correct. The sum of b is of course 76.8, I corrected it now.
  • Vincent Decaux
    Vincent Decaux over 5 years
    If you want to store 8 decimal precision, use DECIMAL(10, 8) field so. It will be better to handle rounding in your application if you have real value in your DB.
  • Sam Hughes
    Sam Hughes over 3 years
    I know this is a super-old answer, but I just did the "Oh *%&" dance about my heavy use of type "double" in the database I've been running for a medium-sized company. The issues DO appear. It drives accountants insane. Suddenly, I'm having flashbacks to a handful of completely disconnected formatting, consistency, and accuracy issues. I dealt with it, confusedly, each time it occurred. One time I observed that sometimes a total would end up as a cent off, but if I captured the different sub-factors, rounded, and then multiplied, the totals would remain accurate. Such a rookie mistake
  • ToolmakerSteve
    ToolmakerSteve about 3 years
    @VincentDecaux - the point of this answer is that, for financial data in dollars and cents, one doesn't want to store 8 decimal precision. There is a correct accountant-specified behavior (usually, to round to nearest cent), and this is what should be performed, and saved as 2 decimal places.
  • ToolmakerSteve
    ToolmakerSteve about 3 years
    "... if there were, they would be truncated by the conversion to BigDecimal." No, thats the lurking problem. A rounding error can cause a number to be a hair smaller than the actual value. Truncation would drop this down a penny. At minimum, it is essential to explicitly Round at any step that might convert from double to a decimal format.
  • ToolmakerSteve
    ToolmakerSteve about 3 years
    @SamHughes - "One time I observed that sometimes a total would end up as a cent off, but if I captured the different sub-factors, rounded, and then multiplied, the totals would remain accurate." FWIW, this can equally be an issue when using decimal; just in different circumstances. Either way, Its necessary to have verification by an accountant as to how sub-factors are to be treated (if multiplication is also involved, as in a tax rate); are they to be summed at a higher precision, and only the total is rounded?
  • ToolmakerSteve
    ToolmakerSteve about 3 years
    "DOUBLE causes rounding issues." And Decimal causes truncation issues, if you fail to use a high enough number of digits for sub-factors, when applying multiplication to each (e.g. a tax rate). In this situation, there is no innately correct answer; the "correct" answer is whatever the accountant (or tax agency) says is the appropriate way to accumulate fractional values. Nevertheless, it is safer to use decimal; just be aware that number of needed decimal digits may be higher in intermediate values.