Очередной раз разбираясь с проблемами ошибок округлений и точности данных типа numeric в mssql пришлось перелопатить BOL. Собственно задачка была достаточно простая - надо было разделить два числа типа numeric(16,7). Все бы хорошо, но... пример:
declare @n1 numeric(16,7), @n2 numeric(16,7)
select @n1=1, @n2=1
select @n1*@n2 as mul, @n2/@n1 as div
mul div
--------------------------------------- ---------------------------------------
1.00000000000000 1.0000000000000000000000
Количество нолей для mul – 14, для div - 22
А почему для DIV-а появляется столько нулей? Правила умножения и деления для numeric(p,s) в MSSQL:
Operation | Result precision | Result scale * |
e1 * e2 | p1 + p2 + 1 | s1 + s2 |
e1 / e2 | p1 - s1 + s2 + max(6, s1 + p2 + 1) | max(6, s1 + p2 + 1) |
Т.е. для данного примера получается следующее:
Операция | Точность | Масштаб | Итоговый тип |
e1 * e2 | 16 + 16 + 1 = 33 | 7 + 7 = 14 | Numeric(33,14) |
e1 / e2 | 16 – 7 + 7 + max(6, 7 + 16 + 1) = 40 | max(6, 16 + 7 + 1) = 24 | Numeric(40, 24) |
Все было бы хорошо, но согласно правил мы должны получить 24 нуля в дробной части при делении, а мы получили только 22.
Тут не стоит забывать о том, что в MSSQL максимальная точность для numeric – 38 разрядов. Из-за этого в результате деления двух numeric-ов будут потеряны 2 разряда. Согласно BOL для сохранения точности целой части результата будет сокращена дробная часть итогового числа, т.е. в результате получим тип numeric(38,22).
Поосторожней надо быть с такой разрядностью :)


