问题
MySQL documentation says :
The DECIMAL and NUMERIC types store exact numeric data values. These types are used when it is important to preserve exact precision, for example with monetary data.
I did this test on that column decimal_column DECIMAL(31,30)
.
insert into tests (decimal_column) values(1/3);
then inspecting what has been stored gives this
select * from tests ;
> result : 0.333333333000000000000000000000
then reversing the math operation with this query gives this
select decimal_column*3 from test;
> result: 0.999999999000000000000000000000
I was expecting to get integer "1" as we do it on our calculators and in an excel sheet ! like this
#calculator or excel sheet
>input: 1 / 3
>result: 0.3333333333333333333333333333333333
>input: * 3
>result: 1
1- why MySQL didn't store the exact binary representation of (1 / 3) so I can use that result again in my calculations as they are in memory like a calculator or an excel sheet.
2- How to store in mysql the result of (1/3)
as they are in the memory during calculation time, So I can retrieve the exact value back and do something like 3 * $storedValue
to result in 1 as integer as we do in a calculator or excel sheet.
回答1:
The problem is not in storage. In your example, the value was broken before storing it in the table.
Unfortunately, if you write 1/3, that will be calculated (and inserted) using the default representation:
SELECT 1/3
0.333333333
which, as you can see, has insufficient precision.
An additional problem is that when you send a constant (1, or 3) to the server, yo do so using a library or a connector, which can take liberties with the value. For example, it might believe that "1" and "3" are integers and their result is to be treated as an integer. So you get "1/3 = 0", but "1./3 = 0.333333", because the dot in "1." makes the connector realize that it needs to use its default floating point. And you get only six 3's because the "default floating point" of the connector has 6 digits. Then you store it into the database, but it is too late. You're storing with high precision a value that has been truncated to low precision.
You can try casting constants from the beginning. Instead of "1", you use the casting of 1 as a sufficiently large decimal. I'm using your 31,30 here, but check that you don't need to store larger numbers. Possibly, "31,20" would be better.
mysql> SELECT 1/3 UNION SELECT CAST(1 AS DECIMAL(31,30))/CAST(3 AS DECIMAL(31,30));
+----------------------------------+
| 1/3 |
+----------------------------------+
| 0.333333333000000000000000000000 |
| 0.333333333333333333333333333333 |
+----------------------------------+
2 rows in set (0.00 sec)
It is very awkward, but results should be better. Also, I think that it's only necessary to cast one value in an expression; MySQL will then promote all involved quantities as necessary. So, adding CAST(0 AS DECIMAL(x,y)) to sums and CAST(1 AS DECIMAL(x,y)) to multiplications might be enough.
mysql> SELECT 3*CAST(1 AS DECIMAL(31,30))/CAST(3 AS DECIMAL(31,30));
+-------------------------------------------------------+
| 3*CAST(1 AS DECIMAL(31,30))/CAST(3 AS DECIMAL(31,30)) |
+-------------------------------------------------------+
| 1.000000000000000000000000000000 |
+-------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT CAST(1 AS DECIMAL(31,30))*1/3;
+----------------------------------+
| CAST(1 AS DECIMAL(31,30))*1/3 |
+----------------------------------+
| 0.333333333333333333333333333333 |
+----------------------------------+
1 row in set (0.00 sec)
Note that this doesn't work because multiplication has higher precedence:
mysql> SELECT CAST(0 AS DECIMAL(31,30))+1/3;
+----------------------------------+
| CAST(0 AS DECIMAL(31,30))+1/3 |
+----------------------------------+
| 0.333333333000000000000000000000 |
+----------------------------------+
1 row in set (0.00 sec)
回答2:
It depends on what you need the information for.
If it is stored for calculation and storage, calculate the result (0.33 instead of 1/3) and store that as decimal(1,5). But you can't easily calculate it backwards if you want to display it.
If it is stored to be displayed at a later time but never to be modified again (or at least not fast) you can store it as varchar but that will break sorting.
Or you could store the different elements (positives, negatives, totals, ... whatever) as decimal(5,0) and display / calculate it while using it.
And of course you can combine the above if you want to get the edge out of the computing time while selecting.
回答3:
MySQL does its internal fractional arithmetic using 64-bit IEEE 754 floating point numbers.
They are generally approximations. It's entirely unreasonable to expect IEEE floating point, or decimal arithmetic for that matter, to achieve exact equality when doing
3*(1/3) == 1
That's just not the way computer arithmetic works.
There's also no way to store an exact representation of the value 1/3, unless you happen to be using an exotic computing system that stores rational (fractional) numbers in the form of (numerator,denominator) pairs. MySQL isn't such a system.
Most calculators implicitly round their results to the number of digits they can display. Excel, too, contains a formatting module. You can choose the format for a cell by pressing -1. The formatting module rounds these floating point numbers. You can achieve the same effect in MySQL using the ROUND() function. That function doesn't change the stored value, but it does render it in a way that conceals the tiny errors inherent in IEEE 754 floating arithmetic.
SELECT ROUND(decimal_column, 2) AS decimal_column
(Don't accounting people have to learn this stuff in school?)
来源:https://stackoverflow.com/questions/42833474/how-to-store-a-decimal-calcaulation-result-in-mysql-and-retrive-it-back-as-they