Відповіді:
Це те, що я виявив, коли у мене виникли ці сумніви.
mysql> create table numbers (a decimal(10,2), b float);
mysql> insert into numbers values (100, 100);
mysql> select @a := (a/3), @b := (b/3), @a * 3, @b * 3 from numbers \G
*************************** 1. row ***************************
@a := (a/3): 33.333333333
@b := (b/3): 33.333333333333
@a + @a + @a: 99.999999999000000000000000000000
@b + @b + @b: 100
Десятковий робив саме те, що повинен був робити в цих випадках, він усікав решту, тим самим втрачаючи 1/3 частини.
Так що для сум десяткова краща, але для поділів поплавок краще, звичайно, до певного моменту. Я маю на увазі, використання DECIMAL не дасть вам арифметики "не довести до ладу" жодними засобами.
Сподіваюся, це допомагає.
@aдає 99,999999999000000000000000000000 ДЕКІМАЛЬНО? Що технічно правильно.
"Поплавок" у більшості середовищ - це двійковий тип з плаваючою комою. Він може точно зберігати значення base-2 (до певної точки), але не може точно зберігати багато базових-10 (десяткових) значень. Поплавці найбільш підходять для наукових розрахунків. Вони не підходять для більшості бізнес-орієнтованої математики, і неналежне використання поплавців укусить вас. Багато десяткових значень не можуть бути точно представлені в base-2. 0.1Наприклад, не може, і тому ви бачите дивні результати на кшталт 1.0 - 0.1 = 0.8999999.
Десяткові знаки зберігають номери-10. Десяткові знаки - це хороший тип для більшості математики бізнесу (але будь-який вбудований тип "гроші" є більш підходящим для фінансових розрахунків), коли діапазон значень перевищує той, який надається цілими типами, і потрібні дробові значення. Десяткові знаки, як випливає з назви, розраховані на базові числа 10 - вони можуть точно зберігати десяткові значення (знову ж таки, до певної точки).
MySQL нещодавно змінив спосіб зберігання типу DECIMAL . Раніше вони зберігали символи (або нюбіли) для кожної цифри, що містять ASCII (або nybble) подання числа - vs - ціле число доповнення двох або деякої його похідної.
Поточний формат зберігання для DECIMAL - це серія 1,2,3 або 4-байтових цілих чисел, біти яких об'єднані, щоб створити номер доповнення двох з десятковою комою, що визначається вами, і зберігається у схемі БД під час декларації стовпця та вкажіть його розмір ДЕКІМАЛЬНО та десяткове місце.
Наприклад, якщо ви взяли 32-бітний int, ви можете зберігати будь-яке число від 0 - 4 294 967 295. Це надійно охопить 999,999,999, тож якби викинули 2 біти і використали (1 << 30 -1), ви нічого б не відмовилися. Покриття всіх 9-значних чисел лише 4 байтами є більш ефективним, ніж охоплення 4-х цифр у 32 бітах за допомогою 4 символів ASCII або 8 цифр. (nybble - це 4-бітне значення, дозволяючи значення 0-15, більше, ніж потрібно для 0-9, але ви не можете усунути ці відходи, перейшовши на 3 біти, тому що вони охоплюють лише значення 0-7)
Для прикладу, який використовується в Інтернет-документах MySQL, використовується DECIMAL (18,9) як приклад. Це на 9 цифр попереду і на 9 цифр за мається на увазі десяткової крапки, яка, як пояснено вище, потребує наступного зберігання.
Як 18 8-бітних символів: 144 біт
Як 18 4-розрядних ніблів: 72 біт
Як 2 32-бітні цілі числа: 64 біт
В даний час DECIMAL підтримує максимум 65 цифр, як DECIMAL (M, D), де найбільше значення для M дозволене - 65, а найбільше значення D дозволене - 30.
Щоб не вимагати відрізків з 9 цифр одночасно, цілі числа, менші за 32 біти, використовуються для додавання цифр, використовуючи 1,2 та 3 байтові цілі числа. З якоїсь причини, яка не відповідає логіці, використовувались підписані замість непідписані вставки, і при цьому 1 біт викидається, що призводить до наступних можливостей зберігання. Для 1,2 та 4 байтових int втрачений біт не має значення, але для 3-байтового int це катастрофа, оскільки ціла цифра втрачається через втрату цього єдиного біта.
Із 7-бітовою вкладкою: 0 - 99
Із 15-бітовим int: 0 - 9,999
З 23-розрядним int: 0 - 999,999 (0 - 9,999,999 з 24-бітовим int)
1,2,3 і 4-байтові цілі числа об'єднуються разом, щоб утворювати "бітовий пул", який DECIMAL використовує для представлення числа точно як ціле число комплементу двох. Десяткова крапка НЕ зберігається, вона мається на увазі.
Це означає, що не потрібно перетворювати ASCII в int перетворення двигуна БД, щоб перетворити "число" в те, що ЦП визнає числом. Ні округлення, ні помилок конверсії, це справжнє число, яким може управляти процесор.
Розрахунки на це довільно велике ціле число потрібно робити в програмному забезпеченні, оскільки немає апаратної підтримки для такого типу чисел, але ці бібліотеки дуже старі та високооптимізовані, написані 50 років тому для підтримки даних IBM 370 Fortran довільної точності з плаваючою точкою . Вони все ще набагато повільніше, ніж ціла ціла алгебра фіксованого розміру, зроблена з апаратним забезпеченням цілого числа процесора, або обчислення з плаваючою комою, зроблені на FPU.
З точки зору ефективності зберігання, оскільки показник поплавця приєднаний до кожного поплавця, неявно вказуючи, де знаходиться десяткова крапка, він є масовим надмірним, а отже, неефективним для роботи з БД. У БД ви вже знаєте, де десяткова крапка повинна переходити попереду, і кожен рядок таблиці, який має значення для стовпця ДЕКІМАЛЬНО, повинен лише переглянути 1 та єдину специфікацію, де потрібно розміщувати та зберігати десяткову точку. у схемі як аргументи до DECIMAL (M, D) як імплікації значень M та D.
Тут знайдено безліч зауважень щодо того, який формат слід використовувати для різних програм, тому я не буду терпіти суть. Я зайняв час, щоб написати це тут, тому що той, хто підтримує пов’язану онлайн-документацію MySQL, не розуміє жодного з перерахованих вище, і після раундів все більш неприємних спроб пояснити це я відмовився. Хорошим свідченням того, наскільки погано вони зрозуміли, що вони пишуть, є дуже заплутане і майже нерозбірливе викладення теми.
На завершення, якщо вам потрібні високоточні обчислення плаваючої точки, за останні 20 років було досягнуто величезного прогресу в коді з плаваючою точкою, а апаратне забезпечення для 96-бітового і чотириразового точності поплавця знаходиться прямо за кутом, але там є хороші бібліотеки довільної точності, якщо важливо маніпулювати збереженим значенням.
Не тільки специфічна для MySQL, різниця між float і десятковими типами полягає в тому, як вони представляють дробові значення. Типи з плаваючою комою представляють дроби у двійковій формі, які можуть представляти лише значення як {m*2^n | m, n Integers}. такі значення, як 1/5, неможливо точно представити (без помилки округлення). Десяткові цифри аналогічно обмежені, але представляють числа як {m*10^n | m, n Integers}. Десяткові знаки все ще не можуть представляти числа, як 1/3, але часто трапляється у багатьох загальних сферах, як, наприклад, фінанси, очікується, що певні десяткові дроби завжди можуть бути виражені без втрати вірності. Оскільки десяткове число може представляти таке значення, як $0.20(п'ята частина долара), то в цих ситуаціях він є кращим.
десяткова призначена для фіксованих кількостей, таких як гроші, де потрібно певну кількість знаків після коми. Поплавці призначені для зберігання ... точних чисел з плаваючою комою.
Я вважаю це корисним:
Як правило, величини з плаваючою величиною корисні для наукових розрахунків, але не повинні використовуватися для фінансових / грошових цінностей. Для бізнес-орієнтованої математики завжди використовуйте Десяткові.
Джерело: http://code.rohitink.com/2013/06/12/mysql-integer-float-decimal-data-types-differences/
mysql> CREATE TABLE num(id int ,fl float,dc dec(5,2));
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO num VALUES(1,13.75,13.75);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO num VALUES(2,13.15,13.15);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM num WHERE fl = 13.15;
Empty set (0.00 sec)
mysql> SELECT * FROM num WHERE dc = 13.15;
+------+-------+-------+
| id | fl | dc |
+------+-------+-------+
| 2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
mysql> SELECT SUM(fl) ,SUM(dc) FROM num;
+--------------------+---------+
| SUM(fl) | SUM(dc) |
+--------------------+---------+
| 26.899999618530273 | 26.90 |
+--------------------+---------+
1 row in set (0.00 sec)
mysql> SELECT * FROM num WHERE ABS(fl - 13.15)<0.01;
+------+-------+-------+
| id | fl | dc |
+------+-------+-------+
| 2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
Типи з плаваючою комою (приблизне значення) - FLOAT, DOUBLE
Типи FLOAT та DOUBLE представляють приблизні числові значення. MySQL використовує чотири байти для значень одноточності та вісім байтів для значень подвійної точності.
Для FLOAT стандарт SQL дозволяє необов'язково специфікувати точність (але не діапазон показника) у бітах після ключового слова FLOAT у дужках. MySQL також підтримує цю необов'язкову специфікацію точності, але значення точності використовується лише для визначення розміру пам’яті. Точність від 0 до 23 призводить до 4-байтної одноточної колонки FLOAT. Точність від 24 до 53 призводить до 8-байтної колонки з подвійною точністю DOUBLE.
MySQL дозволяє нестандартний синтаксис: FLOAT (M, D) або REAL (M, D) або ДВОЙНИЙ ТОЧНИК (M, D). Тут "(M, D)" означає, що значення можуть зберігатися із загальною кількістю до M цифр, з яких D цифр можуть бути після десяткової крапки. Наприклад, стовпець, визначений як FLOAT (7,4), буде відображатися як -999,9999 при відображенні. MySQL виконує округлення під час зберігання значень, тому якщо ви вставите 999.00009 у стовпчик FLOAT (7,4), приблизний результат - 999.0001.
Оскільки значення з плаваючою комою є приблизними і не зберігаються як точні значення, спроби трактувати їх як точні при порівнянні можуть призвести до проблем. Вони також залежать від залежностей платформи чи реалізації.
Для максимальної переносимості код, що вимагає збереження приблизних числових значень даних, повинен використовувати FLOAT або DOUBLE PRECISION без конкретизації точності чи кількості цифр.
https://dev.mysql.com/doc/refman/5.5/uk/floating-point-types.html
Проблеми зі значеннями з плаваючою комою
Числа з плаваючою комою іноді викликають плутанину, оскільки вони є приблизними і не зберігаються як точні значення . Значення з плаваючою комою, записане в операторі SQL, не може бути таким же, як значення, представлене внутрішньо. Спроби трактувати значення з плаваючою комою як точні при порівнянні можуть призвести до проблем. Вони також залежать від залежностей платформи чи реалізації. Цим питанням підлягають типи даних FLOAT та DOUBLE. Для стовпців DECIMAL MySQL виконує операції з точністю до 65 десяткових цифр, що повинно вирішити найпоширеніші проблеми неточності.
У наступному прикладі використовується DOUBLE, щоб продемонструвати, як обчислення, виконані за допомогою операцій з плаваючою комою, піддаються помилці з плаваючою комою.
mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00),
-> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
-> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
-> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
-> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
-> (6, 0.00, 0.00), (6, -51.40, 0.00);
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b
-> FROM t1 GROUP BY i HAVING a <> b;
+------+-------+------+
| i | a | b |
+------+-------+------+
| 1 | 21.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
| 6 | -51.4 | 0 |
+------+-------+------+
Результат правильний. Хоча перші п’ять записів виглядають так, що вони не повинні задовольняти порівняння (значення a і b, схоже, не відрізняються), вони можуть зробити це, тому що різниця між числами виявляється біля десятої десяткової або приблизно, залежно від факторів наприклад, архітектура комп'ютера або версія компілятора або рівень оптимізації. Наприклад, різні процесори можуть по-різному оцінювати числа з плаваючою комою.
Якщо стовпці d1 і d2 були визначені як DECIMAL, а не DOUBLE, результат запиту SELECT містив би лише один рядок - останній, показаний вище.
Правильний спосіб порівняння чисел з плаваючою комою - спочатку визначитися з прийнятним допуском різниць між числами, а потім зробити порівняння зі значенням допуску. Наприклад, якщо ми погоджуємось, що числа з плаваючою комою слід вважати однаковими, якщо вони однакові в межах точності одна на десять тисяч (0,0001), порівняння слід записати, щоб знайти відмінності, більші за значення допуску:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
-> GROUP BY i HAVING ABS(a - b) > 0.0001;
+------+-------+------+
| i | a | b |
+------+-------+------+
| 6 | -51.4 | 0 |
+------+-------+------+
1 row in set (0.00 sec)
І навпаки, щоб отримати рядки, де числа однакові, тест повинен знайти відмінності в межах величини допуску:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
-> GROUP BY i HAVING ABS(a - b) <= 0.0001;
+------+------+------+
| i | a | b |
+------+------+------+
| 1 | 21.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
+------+------+------+
5 rows in set (0.03 sec)
Значення з плаваючою комою залежать від залежностей платформи чи реалізації. Припустимо, що ви виконуєте такі операції:
CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));
INSERT INTO t1 VALUES('1e+52','-1e+52');
SELECT * FROM t1;
На деяких платформах оператор SELECT повертає inf та -inf. Для інших він повертає 0 і -0.
Імплементація попередніх проблем полягає в тому, що якщо ви намагаєтеся створити ведений реплікації, скидаючи вміст таблиці з mysqldump на майстер і перезавантажуючи файл дампа в підлеглий, таблиці, що містять стовпці з плаваючою комою, можуть відрізнятися між двома хостами.
https://dev.mysql.com/doc/refman/5.5/uk/problems-with-float.html
Жорстке і швидке правило
Якщо все, що вам потрібно зробити, це додавати, віднімати чи множувати числа, які ви зберігаєте, найкраще DECIMAL.
Якщо вам потрібно розділити або зробити будь-яку іншу форму арифметики чи алгебри за даними, ви майже напевно будете щасливішими з поплавком. Бібліотеки з плаваючою комою та на процесорах Intel, сам процесор з плаваючою точкою, мають TON-операції з виправлення, виправлення, виявлення та оброблення пухир винятків, які трапляються при виконанні типових математичних функцій - особливо трансцендентальних функцій.
Щодо точності, я одного разу написав бюджетну систему, яка обчислювала відсотковий внесок кожного з 3000+ рахунків, для 3600 бюджетних одиниць, за місяць до вузла консолідації цієї одиниці, а потім виходячи з цієї матриці відсотків (3 000 + х 12 х 3600) Я помножив суму бюджету на найвищі організаційні вузли до наступних 3 рівнів організаційних вузлів, а потім обчислив усі (3000 + 12) значення для всіх 3200 одиниць деталізації з цього. Мільйони та мільйони та мільйони подвійних точних обчислень з плаваючою точкою, будь-який з яких би скинув згортання всіх цих проекцій під час консолідації знизу до найвищого рівня в організації.
Загальна помилка з плаваючою комою після всіх цих обчислень становила ZERO . Це було в 1986 році, і сьогодні бібліотеки з плаваючою комою набагато, набагато краще, ніж тоді. Intel робить все це проміжні обчислення подвоєнь з 80-бітовою точністю, що все, крім усунення помилки округлення. Коли хтось каже вам "це помилка з плаваючою комою", це майже впевненість НЕ правда.
float(і double) являє собою двійкові дроби
decimal являє десяткові дроби
declare @float as float(10)
declare @Decimal as decimal(10)
declare @Inetger as int
set @float =10.7
set @Decimal =10.7
set @Inetger=@Decimal
print @Inetger
у float, коли встановлено значення для цілого друку 10, але у десятковій цифрі 11
FLOAT(m,n), це призводить до двох округлення; тим часом він не дає нічого корисного.