Books and articles about SQL Rambler's Top100 Сменить язык на: Русский 19 April 2024 18:45:56


www.sql-ex.ru
Skip Navigation Links  

 

Print  Версия для печати

На главную страницу

Умножение и деление чисел типа NUMERIC.

Mat (оригинал: Multiplication and Division with Numerics.)
Перевод Моисеенко С.И.

Может быть вам покажутся удивительными результаты следующих арифметических операций:

declare @num1 numeric(38,10)
declare @num2 numeric(38,10)
set @num1 = .0000006
set @num2 = 1.0
select cast( @num1 * @num2 as numeric(38,10))

Дает: .0000010000
Вместо: .0000006000

Почему?

Ну, BOL (смотри Precision, Scale и Length) определяет следующие правила для арифметических операций c числами типа NUMERIC:

Операция Точность результата Масштаб результата *
e1 + e2 max(s1, s2) + max(p1-s1, p2-s2) + 1 max(s1, s2)
e1 - e2 max(s1, s2) + max(p1-s1, p2-s2) + 1 max(s1, s2)
e1 * e2 p1 + p2 + 1 s1 + s2
e1 / e2 p1 - s1 + s2 + max(6, s1 + p2 + 1) max(6, s1 + p2 + 1)
e1 { UNION | EXCEPT | INTERSECT } e2 max(s1, s2) + max(p1-s1, p2-s2) max(s1, s2)

В нашем случае точность и масштаб умножения вычисляется так:

Точность = P1 + P2 + 1 = 38 + 38 + 1 = 77
Масштаб = S1 + S2 = 10 + 10 = 20

Соответственно, результатом должно быть число типа numeric(77, 20), что не допускается. Вот где нам понадобится сноска:

* Точность и масштаб результата имеет абсолютный максимум 38. Если точность результата превышает 38, соответствующий масштаб уменьшается, чтобы предотвратить усечение целой части результата.

BOL в настоящий момент не вдается в подробности того, как выполняется усечение.

Поскольку точность превышает 38, мы постараемся избежать усечения целой части значения уменьшением масштаба (вместо этого усекая дробную часть значения). Каким количеством масштаба пожертвовать? Здесь нет правильного ответа. Если оставить слишком много, то будет потерян результат умножения больших чисел. Если оставить слишком мало, умножение малых чисел станет проблемой.

В SQL Server 2005 RTM (и предыдущих версиях) мы решили оставить минимальный масштаб - 6 как для умножения, так и деления. Поэтому наше numeric(77,20) усекалось до numeric(38,6), а затем приводилось к numeric(38,10). Однако это было сделано слишком поздно, и некоторые данные были потеряны. Это объясняет результат, который вы можете увидеть выше.

Поэтому важно стараться задавать по минимуму точность и масштаб операндов в умножении и делении. В этом случае:

declare @num1 numeric(18,10)
declare @num2 numeric(18,10)
set @num1 = .0000006
set @num2 = 1.0
select cast( @num1 * @num2 as numeric(38,10))

тип результата должен быть numeric(37,20). Поскольку точность и масштаб типа не превышает наших текущих пределов, неявное усечение не проводится. Затем мы приводим результат к numeric(38,10), что не вызывает потери данных в нашем случае.

Если вы не можете точно типизировать значения, участвующие в умножении и делении, например, если они являются параметрами процедуры, которая вызывается с большим разбросом значений, возможно, стоит обратить внимание на приближенные числовые типы (float, real) или определить свой собственный "высокоемкий" пользовательский точный числовой тип данных, используя CLR.

Я надеюсь, что это было полезно для вас. Дайте нам знать, если вы хотите прочитать о чем-то конкретно.

29/03/2006

На главную страницу

Print  Версия для печати


Usage of any materials of this site is possible
only under condition of mandatory allocation of the direct link to a site
http://www.sqlbooks.ru
on each page where used materials are placed.

 Main   Articles    Books 
Рейтинг@Mail.ru Rambler's Top100 Alt Упражнения по SQL: обучение, тестирование, сертификация по языку SQL Copyright c 2002-2006. All rights reserved.