I\'m facing a really weird ¿bug? on mysql+php right now. Is a simple select, in the following example i\'m using multiple fields to try to explain my problem:
The problem is how the DOUBLE and FLOAT values are stored.
It is possible (and probable) that values like 11.5 or 22.475 coul be stored in approximated values like 11.499999999999~ or 22.475000000000000001 thus some calculations or roundings could lead to incorrect results.
It is always better to store float values into a DECIMAL coulmn type where the value is stored exactly with all the decimal digits and is not approximated.
Exact numeric literals (such as the 1.15
in all four of your examples and the 11.5
in your latter two) are encoded using MySQL's DECIMAL
type.
In your first two examples, the result of multiplying field
(of type DOUBLE
) with such literals is another DOUBLE
, which is stored using 8-byte IEEE Standard floating-point representation (i.e. binary64). However, in this representation 13.225
encodes as 0x402A733333333333
, whose bits signify:
Sign : 0b0 Biased exponent: 0b10000000010 = 1026 (representation includes bias of +1023, therefore exp = 3) Significand : 0b[1.]1010011100110011001100110011001100110011001100110011 = [1.]6531249999999999555910790149937383830547332763671875 ^ hidden bit, not stored in binary representation
This equates to:
(-1)^0 * 1.6531249999999999555910790149937383830547332763671875 * 2^3 = 13.2249999999999996447286321199499070644378662109375000
Therefore, rounding this result to two decimal places yields 13.22
not 13.23
.
Performing the multiplication with two DECIMAL
types, such as two literals as used in your latter two examples, results in another DECIMAL
. In this representation, 13.225
encodes in a binary-coded decimal format with exact precision and therefore the rounding operation results in 13.23
as expected.
As mentioned in the MySQL manual:
Floating-point numbers sometimes cause confusion because they are approximate and not stored as exact values. A floating-point value as written in an SQL statement may not be the same as the value represented internally. Attempts to treat floating-point values as exact in comparisons may lead to problems. They are also subject to platform or implementation dependencies. The FLOAT and DOUBLE data types are subject to these issues. For DECIMAL columns, MySQL performs operations with a precision of 65 decimal digits, which should solve most common inaccuracy problems.
If you are after exact precision, DECIMAL
may be more suited to your requirements. If you need the range/performance of DOUBLE
but still wish to have the desired rounding outcome, you could CONVERT the result of the multiplication to DECIMAL
prior to rounding.