Разница между типом данных float и decimal


175

Какая разница, когда я использую плавающие и десятичные типы данных в MySQL?

Когда я должен использовать что?


Не используйте FLOAT(m,n), это приводит к двум округлениям; Между тем, он не дает ничего полезного.
Рик Джеймс

Ответы:


185

Это то, что я нашел, когда у меня было это сомнение.

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 никоим образом не даст вам «арифметику безотказности».

Надеюсь это поможет.


4
Отличный тест. Несколько лет назад функции преобразования данных в C lib часто создавали тонны незначительных различий в значениях, преобразованных из ASCII в плавающее, по сравнению с, скажем, в SQLServer. Это редко правда больше. Тестирование - лучшая политика, так как лучше точно знать, каковы компромиссы.

15
На самом деле, DECIMAL дополнение ошибочно. Если вы добавите 33,333333333 три раза, вы не получите 100. Если вы поделите 100 на 3, вы не получите рациональное число без повторяющегося набора конечных цифр, поэтому вы не можете умножить его на 3 и получить 100. калькулятор и попробуйте. Логически мы знаем, что 1/3 + 1/3 + 1/3 должно равняться 3 / 3rds IE: 1, но этот класс рациональных чисел не позволяет нам сделать это. Ответ с плавающей точкой правильный, но ваш бухгалтер НЕНАВИЖЕТ его !
user2548100

5
Разве @a99.999999999000000000000000000000 не дают ДЕСЯТИЧНЫЙ? Что технически правильно.
Винсент Пуарье

78

«Плавающее» в большинстве сред является двоичным типом с плавающей точкой. Он может точно хранить значения base-2 (до определенной точки), но не может точно хранить много значений base-10 (десятичных). Поплавки наиболее подходят для научных расчетов. Они не подходят для большинства бизнес-ориентированной математики, и неправильное использование поплавков укусит вас. Многие десятичные значения не могут быть точно представлены в base-2. 0.1не может, например, и поэтому вы видите странные результаты, как 1.0 - 0.1 = 0.8999999.

Десятичные числа хранят базу-10 чисел. Десятичный является хорошим типом для большинства бизнес-математик (но любой встроенный тип «деньги» больше подходит для финансовых расчетов), где диапазон значений превышает диапазон, обеспечиваемый целочисленными типами, и требуются дробные значения. Десятичные числа, как следует из названия, предназначены для чисел от 10 до 10 - они могут точно хранить десятичные значения (опять же, до определенной точки).


@Michael Petrotta - пользователь просто вводит свои десятичные числа в поле, указанное в формах. Мне нужно просто сохранить их в БД. который будет более подходящим. ?
Хакер

12
@Pradeep: Я чувствую, что вы не отвечаете на мои вопросы. Это может быть потому, что вы сами не знаете ответов - может быть, вам неудобно спрашивать у вашего менеджера или клиента более подробную информацию. Если это так, я предлагаю укусить пули, сесть с ними на пару часов и действительно пройтись по вашему заявлению. Для чего именно и очень подробно используются ваши данные?
Майкл Петротта

1
На самом деле, в настоящее время и float, и DECIMAL хранят свои числа одинаково. Разница в том, как эти цифры используются. DECIMAL использует все биты для составления целого числа с дополнением до двух с подразумеваемой десятичной точкой. У поплавка есть два целых числа, и одно возводит другое в степень. И основание, и показатель степени являются двумя целыми числами дополнения.
user2548100

1
Я думаю, что ваш ответ может быть технически правильным, но акцент на том, что float является двоичным типом, скрывает тот факт, что они оба хранят свои данные в одном и том же формате. Число с плавающей запятой, возведенное в первую степень, является целым числом и хранится именно таким образом. Фактически, для 80-битной точности с плавающей точкой, основой является int64. И наоборот, если вы написали библиотеку для целых чисел, которая возводила их в степени, вы столкнулись бы с теми же проблемами с целыми числами, или DECIMALS, или римскими числами, или леденцами на палочке. "Хранилище ошибок" создает не хранилище, а математическая обработка библиотеки.
user2548100

1
Принимая во внимание очень низкое качество вопроса, когда практически не указываются параметры, указывающие на то, что является предметом озабоченности ОП, трудно понять, что является подходящим ответом. Как правило, DECIMAL будет хранить большие числа, и математические библиотеки соответствуют ожиданиям бухгалтера, в то время как двойное плавание - это менее эффективный носитель с массивно оптимизированными математическими библиотеками, что намного лучше отвечает ожиданиям ученых и финансов (не бухгалтеров).
user2548100

21

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 уже не за горами, но есть хорошие библиотеки произвольной точности, если важно манипулировать сохраненным значением.


Я считаю, что в архитектуре Intel Hazwell есть операции AVX-2 с 256-битными целыми числами, охватывающие все возможные значения, которые могут представлять 77 цифр, которые можно использовать для непосредственной работы с целыми числами с расширенной точностью DECIMAL. В будущем может оказаться целесообразным, чтобы Oracle поддержал новую форму DECIMAL, охватывающую 77 цифр против 65. Я бы оценил повышение производительности в 5-10 раз, используя аппаратное обеспечение вместо программного обеспечения. 2 ^ 256 = 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129, 639,936 (78 цифр)

Векторные процессоры Intel теперь поддерживают 512-битные математические операции. Это будет охватывать 154 цифры. 2 ^ 512 = 13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096 (155 цифр)

13

Не только для MySQL, разница между типами с плавающей запятой и десятичными типами заключается в том, что они представляют дробные значения. Типы с плавающей точкой представляют дробные числа в двоичном формате, которые могут представлять только значения как {m*2^n | m, n Integers}. значения, такие как 1/5, не могут быть точно представлены (без ошибки округления). Десятичные числа аналогичным образом ограничены, но представляют числа как {m*10^n | m, n Integers}. Десятичные дроби по-прежнему не могут представлять числа, подобные 1/3, но часто во многих общих областях, таких как финансы, существует вероятность того, что определенные десятичные дроби всегда могут быть выражены без потери точности. Поскольку десятичное число может представлять значение, подобное $0.20(одна пятая доллара), в таких ситуациях оно является предпочтительным.


Поскольку процессоры Intel выполняют все промежуточные операции двойного числа с точностью до 80 бит, практически не исключается ошибка округления, когда конечный результат обрезается с 80 до 64 бит. Даже многие программные библиотеки с плавающей точкой могут обрабатывать эти и сотни других арифметических аномалий. Теория и практика, таким образом, сильно расходятся в этой области.

9

десятичная дробь для фиксированных количеств, таких как деньги, где вы хотите определенное количество знаков после запятой. Поплавки предназначены для хранения ... чисел с плавающей запятой.


6

Я нашел это полезным:

Как правило, значения с плавающей запятой хороши для научных расчетов, но не должны использоваться для финансовых / денежных значений. Для бизнес-ориентированной математики всегда используйте десятичную.

Источник: http://code.rohitink.com/2013/06/12/mysql-integer-float-decimal-data-types-differences/


5
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)

2

Если вам нужна производительность, а не точность, вы должны заметить, что вычисления с плавающей точкой намного быстрее десятичных


2

Типы с плавающей точкой (Приблизительное значение) - 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


0

Жесткое и быстрое правило

Если все, что вам нужно сделать, это сложить, вычесть или умножить числа, которые вы храните, лучше всего использовать DECIMAL.

Если вам нужно разделить или сделать любую другую форму арифметики или алгебры данных, вы почти наверняка будете счастливее с помощью float. Библиотеки с плавающей запятой и на процессорах Intel, самом процессоре с плавающей запятой, имеют TON операций для исправления, исправления, обнаружения и обработки метели исключений, возникающих при выполнении типичных математических функций - особенно трансцендентных функций.

Что касается точности, я однажды написал бюджетную систему, которая вычисляла% вклада каждой из 3000+ учетных записей, для 3600 бюджетных единиц, по месяцам в узел консолидации этой единицы, а затем на основе этой матрицы процентов (3000 + x 12 x 3600) Я умножил суммы, заложенные в бюджет на самые высокие организационные узлы, на следующие 3 уровня организационных узлов, а затем вычислил все (3000 + 12) значения для всех 3200 подробных единиц из этого. Миллионы, миллионы и миллионы вычислений с плавающей запятой двойной точности, любой из которых отбросит сведение всех этих прогнозов при консолидации снизу вверх до самого высокого уровня в организации.

Общая ошибка с плавающей запятой после всех этих вычислений была нулевой . Это было в 1986 году, и библиотеки с плавающей запятой сегодня намного лучше, чем были тогда. Intel выполняет все промежуточные вычисления двойных чисел с точностью 80 бит, что практически исключает ошибку округления. Когда кто-то говорит вам «это ошибка с плавающей запятой», это почти наверняка НЕ ​​правда.


-2

floatdouble) представляет двоичные дроби

decimal представляет десятичные дроби


-2
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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.