Ответы:
Это то, что я нашел, когда у меня было это сомнение.
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 (до определенной точки), но не может точно хранить много значений base-10 (десятичных). Поплавки наиболее подходят для научных расчетов. Они не подходят для большинства бизнес-ориентированной математики, и неправильное использование поплавков укусит вас. Многие десятичные значения не могут быть точно представлены в base-2. 0.1
не может, например, и поэтому вы видите странные результаты, как 1.0 - 0.1 = 0.8999999
.
Десятичные числа хранят базу-10 чисел. Десятичный является хорошим типом для большинства бизнес-математик (но любой встроенный тип «деньги» больше подходит для финансовых расчетов), где диапазон значений превышает диапазон, обеспечиваемый целочисленными типами, и требуются дробные значения. Десятичные числа, как следует из названия, предназначены для чисел от 10 до 10 - они могут точно хранить десятичные значения (опять же, до определенной точки).
MySQL недавно изменил способ хранения типа DECIMAL . В прошлом они сохраняли символы (или nybble) для каждой цифры, содержащей ASCII (или nybble) представление числа - против целого числа с дополнением до двух или некоторой его производной.
Текущий формат хранения для DECIMAL представляет собой серию из 1,2,3 или 4-байтовых целых чисел, биты которых объединяются для создания числа дополнения до двух с подразумеваемой десятичной точкой, определенной вами, и сохраняются в схеме БД при объявлении столбец и укажите его размер DECIMAL и положение десятичной точки.
Например, если вы берете 32-битное целое число, вы можете хранить любое число от 0 до 4 294 967 295. Это будет надежно покрывать только 999 999 999, поэтому, если вы выбросите 2 бита и будете использовать (1 << 30 -1), вы ничего не потеряете. Покрытие всех 9-значных чисел только 4 байтами более эффективно, чем покрытие 4-х цифр в 32-х битах с использованием 4 символов ASCII или 8 цифр nybble. (nybble 4-битный, допуская значения 0-15, больше, чем необходимо для 0-9, но вы не можете устранить эти потери, перейдя к 3 битам, потому что это охватывает только значения 0-7)
В примере, используемом в онлайн-документации MySQL, в качестве примера используется DECIMAL (18,9). Это на 9 цифр впереди и на 9 цифр позади подразумеваемой десятичной точки, что, как объяснено выше, требует следующего хранения.
Как 18 8-битных символов: 144 бита
Как 18 4-битных nybbles: 72 бита
Как 2 32-битных целых числа: 64 бит
В настоящее время DECIMAL поддерживает максимум 65 цифр, как DECIMAL (M, D), где наибольшее допустимое значение для M равно 65, а наибольшее допустимое значение D равно 30.
Чтобы не требовалось порций по 9 цифр за раз, целые числа меньше 32 бит используются для добавления цифр с использованием 1,2 и 3 байтовых целых чисел. По какой-то причине, которая не поддается логике, вместо беззнаковых целочисленных элементов использовались подписанные, и при этом выбрасывается 1 бит, что приводит к следующим возможностям хранения. Для 1,2 и 4 байтовых бит потерянный бит не имеет значения, но для 3-байтового int это катастрофа, потому что вся цифра теряется из-за потери этого единственного бита.
С 7-битным int: 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 для механизма DB, чтобы преобразовать «число» во что-то, что CPU распознает как число. Нет округления, нет ошибок преобразования, это реальное число, которым ЦПУ может манипулировать.
Вычисления для этого произвольно большого целого числа должны выполняться в программном обеспечении, поскольку аппаратная поддержка для такого типа числа отсутствует, но эти библиотеки очень старые и сильно оптимизированы, они были написаны 50 лет назад для поддержки данных IBM с плавающей точкой произвольной точности 370 Fortran , Они все еще намного медленнее, чем целочисленная алгебра фиксированного размера, выполняемая с помощью целочисленного аппаратного обеспечения ЦП, или вычисления с плавающей запятой, выполняемые на FPU.
С точки зрения эффективности хранения, поскольку показатель степени с плавающей точкой привязан к каждому и каждому плавающему элементу, неявно указывая, где находится десятичная точка, он в значительной степени избыточен и, следовательно, неэффективен для работы с БД. В БД вы уже знаете, где находится десятичная точка впереди, и каждая строка в таблице, которая имеет значение для столбца DECIMAL, должна смотреть только на спецификацию 1 & only того, где эта десятичная точка должна быть размещена и сохранена. в схеме в качестве аргументов DECIMAL (M, D) в качестве значения значений M и D.
Многие замечания, найденные здесь о том, какой формат должен использоваться для различных приложений, являются правильными, поэтому я не буду вдаваться в подробности. Я нашел время, чтобы написать это здесь, потому что, кто бы ни поддерживал связанную онлайновую документацию MySQL, не понимает ничего из вышеперечисленного, и после нескольких попыток все более и более разочаровывающих попыток объяснить это им я отказался. Хорошим показателем того, насколько плохо они понимали, что они пишут, является очень запутанное и почти неразборчивое представление предмета.
В заключение, если вам нужны высокоточные вычисления с плавающей запятой, за последние 20 лет в коде с плавающей запятой произошли огромные успехи, и аппаратная поддержка для 96-битных и Quadruple Precision float уже не за горами, но есть хорошие библиотеки произвольной точности, если важно манипулировать сохраненным значением.
Не только для MySQL, разница между типами с плавающей запятой и десятичными типами заключается в том, что они представляют дробные значения. Типы с плавающей точкой представляют дробные числа в двоичном формате, которые могут представлять только значения как {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) или DOUBLE PRECISION (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/en/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/en/problems-with-float.html
Жесткое и быстрое правило
Если все, что вам нужно сделать, это сложить, вычесть или умножить числа, которые вы храните, лучше всего использовать DECIMAL.
Если вам нужно разделить или сделать любую другую форму арифметики или алгебры данных, вы почти наверняка будете счастливее с помощью float. Библиотеки с плавающей запятой и на процессорах Intel, самом процессоре с плавающей запятой, имеют TON операций для исправления, исправления, обнаружения и обработки метели исключений, возникающих при выполнении типичных математических функций - особенно трансцендентных функций.
Что касается точности, я однажды написал бюджетную систему, которая вычисляла% вклада каждой из 3000+ учетных записей, для 3600 бюджетных единиц, по месяцам в узел консолидации этой единицы, а затем на основе этой матрицы процентов (3000 + x 12 x 3600) Я умножил суммы, заложенные в бюджет на самые высокие организационные узлы, на следующие 3 уровня организационных узлов, а затем вычислил все (3000 + 12) значения для всех 3200 подробных единиц из этого. Миллионы, миллионы и миллионы вычислений с плавающей запятой двойной точности, любой из которых отбросит сведение всех этих прогнозов при консолидации снизу вверх до самого высокого уровня в организации.
Общая ошибка с плавающей запятой после всех этих вычислений была нулевой . Это было в 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
в плавающей запятой, когда установлено значение целочисленной печати 10, но в десятичной 11
FLOAT(m,n)
, это приводит к двум округлениям; Между тем, он не дает ничего полезного.