Re[2]: T-SQL. Деление. Частное - 17 значащих десятичных разрядов
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.06.23 08:56
Оценка: 13 (2)
Здравствуйте, Воронин Иван, Вы писали:

ВИ>Сейчас стал перепроверять и обнаружил мне не понятное поведение.

ВИ>Если играть с точность и масштабом, то точность вычислений меняется и не больше значит больше.
ВИ>Нужно смотреть реализацию, но мне это не до этого.

  подробности
ВИ>
ВИ>declare @N1 Numeric(20,2)
ВИ>declare @N2 Numeric(20,2)

ВИ>set @N1 = 42183246.61
ВИ>set @N2 = 41761414.14

ВИ>select @N2 * 100 / @N1

ВИ>результат: 98.99999999075462
ВИ>



ВИ>
ВИ>declare @N1 Numeric(17,2)
ВИ>declare @N2 Numeric(17,2)

ВИ>set @N1 = 42183246.61
ВИ>set @N2 = 41761414.14

ВИ>select @N2 * 100 / @N1

ВИ>результат: 98.99999999075462342
ВИ>


ВИ>Результат в калькуляторе от Win7 в инженерном режиме: 98,999999990754623426553748609157


Предположим, у нас @N1 и @N2 имеют тип Numeric(x,2) (рубли с копейками).

Согласно справочнику, типы результатов выводятся следующим образом:
numeric(x, 2) * numeric(3, 0) => numeric(x+4, 2)
numeric(x+4, 2) / numeric(x, 2) => numeric(x + 4 + max(6, x + 3), max(6, x + 3))

Если x >= 3, то выражение упрощается до numeric(2x+7, x+3)
То есть, для x = 17 тип должен получаться numeric(41, 20). Но максимум точности — 38, и тип будет урезан с потерей младших разрядов, то есть в реальности получится numeric(38, 17).
Для x = 20 получаем numeric(47, 23), который урезается до (38, 14)
Давайте повнимательнее посмотрим на процесс урезания. От x+3 мы отнимаем превышение 2x+7 над 38.
То есть если x <= 15, то урезания не произойдёт.

А вот если x > 15, то результирующий масштаб (scale) будет равен x+3 — (2x+7-38) = 34 — x, при точности 38.
Из-за этого вы и наблюдаете такой парадокс: чем больше исходная точность (precision), тем хуже финальный масштаб (scale). SQL server резервирует для вас лидирующие цифры в целой части.

Вы хотите получить 15 знаков после запятой. Решение уравнения напрямую даёт нам x = 19.
Если от вас не требуется оперировать суммами более десяти квадриллионов рублей, то можно просто вычислять вот так:
print cast(41761414.14 as dec(19, 2)) * 100 / cast(42183246.61 as dec(19,2))
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.