[MS SQL] почему разные результаты?
От: Аноним  
Дата: 28.05.13 02:45
Оценка: 20 (1)
Посмотрите на этот пример:

declare @n int
set @n = 1

select     
    1/power(@n + 1.0, 0.7),                -- 0.625000000000000000000000000000000000
    1/power(convert(float,@n+1.0),0.7)    -- 0.615572206672458


почему так?
Re: [MS SQL] почему разные результаты?
От: _ABC_  
Дата: 28.05.13 03:45
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Посмотрите на этот пример:

А>почему так?

http://dba.stackexchange.com/questions/6900/why-does-select-power10-0-38-0-throw-an-arithmetic-overflow-error

Вкратце — результат вычисляется не как float, а как тип левого аргумента функции. При этом тип левого аргумента должен иметь способность неявного преобразования в float.
Re[2]: [MS SQL] почему разные результаты?
От: _ABC_  
Дата: 28.05.13 04:00
Оценка:
Здравствуйте, _ABC_, Вы писали:

_AB>http://dba.stackexchange.com/questions/6900/why-does-select-power10-0-38-0-throw-an-arithmetic-overflow-error

_AB>Вкратце — результат вычисляется не как float, а как тип левого аргумента функции. При этом тип левого аргумента должен иметь способность неявного преобразования в float.

Кстати, в доках по 2012 описание функции изменили, чтобы подробнее раскрыть как раз этот момент. Раньше описание оставляло простор для толкования.
Re: [MS SQL] почему разные результаты?
От: Olaf Россия  
Дата: 28.05.13 04:30
Оценка: 101 (3)
Здравствуйте, Аноним, Вы писали:

А>Посмотрите на этот пример:

А>почему так?

POWER ( float_expression , y )
Функция Power возвращает результат такого же типа, который передается в аргументе float_expression Когда вы делаете сложение @n + 1.0 на выходе получаете тип numeric(12, 1), т.е. 1 знак после запятой. Соответственно Power результат вычисления округляет до 1 знака. Если ваш пример является головоломкой, то распутать ее можно повышая точность для 1 на n знаков, чем больше n тем точнее результат получится. Посмотрите пример ниже:

declare @n int
set @n = 1

select     
    1/power(@n + 1.0, 0.7),                -- 0.625000000000000000000000000000000000
    1/power(@n + 1.00, 0.7),               -- 0.61728395061728395061728395061728395
    1/power(@n + 1.000, 0.7),              -- 0.6153846153846153846153846153846153
    --.... и т.д.
    1/power(@n + 1.000000, 0.7),           -- 0.6155721281251827479755371636283
    1/power(convert(float,@n+1.0),0.7)     -- 0.615572206672458

select
    power(@n + 1.0, 0.7),                  -- 1.6
    power(@n + 1.00, 0.7),                 -- 1.62
    power(@n + 1.000, 0.7),                -- 1.625
    --.... и т.д.
    power(@n + 1.000000, 0.7),             -- 1.624505
    power(convert(float,@n+1.0),0.7)       -- 1,62450479271247
Re[2]: [MS SQL] почему разные результаты?
От: Аноним  
Дата: 28.05.13 10:44
Оценка:
Здравствуйте, Olaf, Вы писали:

В принципе понятно, спасибо.
Моя ошибка была, что я хотел сделать float по-быстрому через (1.0+n), а надо быдо через cast/convert

Есть мелкте детали "на выходе получаете тип numeric(12, 1)" — почему не numeric(10, 1), например?

А в общем, так как они сделали это логично? Не является ли это дефектом? Получаются колоссальные потери точности
Re[3]: [MS SQL] почему разные результаты?
От: Olaf Россия  
Дата: 28.05.13 16:19
Оценка: 132 (3)
Здравствуйте, Аноним, Вы писали:

А>Есть мелкте детали "на выходе получаете тип numeric(12, 1)" — почему не numeric(10, 1), например?


При выполнении операций с разными типами данных SQL Server использует приоритет типов данных (Data Type Precedence), когда тип данных с меньшим приоритетом неявно преобразуется к типу данных с большим приоритетом (если это возможно!). Вы оперируете двумя типами данных – int и numeric. Т.к. int имеет более низкий приоритет, то все выражение @n + 1.0 приводится к типу numeric. По ссылке, которую я приложил можно посмотреть типы и их приоритеты.
Выбор длины результирующего типа, основывается на размерах типов данных, которые складываются. У вас два значения @n и 1.0, где…
@n является целым (int) и содержит всего 10 цифр (precision)
1.0 является numeric(2, 1), причем содержит всего две цифры (precision), одна из которых после запятой справа (scale)
Соответственно, складывая параметры scale и precision, на выходе получаем результирующий тип длиной 12 цифр, причем 1 из них справа после запятой. Все вышесказанное можно проверить, используя функцию sql_variant_property
declare @n int
set @n = 1

-- Для @n
select
    sql_variant_property(@n, 'BaseType') as Type
    ,sql_variant_property(@n, 'Precision') as Precision
    ,sql_variant_property(@n, 'Scale') as Scale

-- Для 1.0
select
    sql_variant_property(1.0, 'BaseType') as Type
    ,sql_variant_property(1.0, 'Precision') as Precision
    ,sql_variant_property(1.0, 'Scale') as Scale

-- Для @n + 1.0        
select
    sql_variant_property(@n + 1.0, 'BaseType') as Type
    ,sql_variant_property(@n + 1.0, 'Precision') as Precision
    ,sql_variant_property(@n + 1.0, 'Scale') as Scale

А>А в общем, так как они сделали это логично? Не является ли это дефектом? Получаются колоссальные потери точности

Дефектов нет, все в порядке Поведение предсказуемое и описано в документации. Ну а чтобы избегать потерь точности, нужно аккуратно выбирать и применять типы данных, в принципе как и везде.
Re[4]: Признаю, был не прав в методике расчета точности и масштаба…
От: Olaf Россия  
Дата: 03.06.13 12:53
Оценка:
Коллеги, приношу извинения, если кого-то ввёл в заблуждение своими вычислениями точности и масштаба результирующего типа. Правильная методика в корне отличается от той, которую я привел. Оригинал находится здесь Precision, Scale, and Length (Transact-SQL), там описаны формулы для всех операций.

В частности, для сложения двух чисел e1(p1,s1) + e2(p2, s2) = e3(p3, s3), используется формула p3 = max(s1, s2) + max(p1-s1, p2-s2) + 1 и s3 = max(s1, s2), где
pN – точность числа (общее кол-во цифр в числе)
sN – масштаб числа (кол-во цифр после запятой)
N = 1,2 — первое и второе слагаемое
N = 3 — результат сложения

Рассмотрим более сложный пример на двух числах @m и @n
declare @m int = 10 (p1 = 10, s1 = 0)
declare @n numeric(34, 3) = 1.0 (p2 = 34, s2 = 3)
рассчитываем точность и масштаб для e3(p3, s3)
p3 = max(0, 3) + max(10 – 0, 34 – 3) + 1 = 35, s3 = max(0, 3) = 3
В итоге, на выходе с учетом старшинства типов получаем numeric(35, 3)

Код
  Скрытый текст
declare @n numeric(34, 3)
set @n = 0
declare @m int
set @m = 0
 
-- Для @n
select
    sql_variant_property(@n, 'BaseType') as Type
    ,sql_variant_property(@n, 'Precision') as Precision
    ,sql_variant_property(@n, 'Scale') as Scale
 
-- Для @m
select
    sql_variant_property(@m, 'BaseType') as Type
    ,sql_variant_property(@m, 'Precision') as Precision
    ,sql_variant_property(@m, 'Scale') as Scale
 
-- Для @n + @m
select
    sql_variant_property(@n + @m, 'BaseType') as Type
    ,sql_variant_property(@n + @m, 'Precision') as Precision
    ,sql_variant_property(@n + @m, 'Scale') as Scale
Re[5]: Признаю, был не прав в методике расчета точности и масштаба…
От: _FRED_ Черногория
Дата: 10.06.13 20:05
Оценка:
Здравствуйте, Olaf, Вы писали:

O>Коллеги, приношу извинения, если кого-то ввёл в заблуждение своими вычислениями точности и масштаба результирующего типа. Правильная методика в корне отличается от той, которую я привел. Оригинал находится здесь Precision, Scale, and Length (Transact-SQL), там описаны формулы для всех операций.


Спасибо за то, что откликнулись на эту тему. Детали-то там находятся все по ссылке из предыдущего сообщения
Help will always be given at Hogwarts to those who ask for it.
Re[6]: Признаю, был не прав в методике расчета точности и масштаба…
От: Olaf Россия  
Дата: 11.06.13 04:22
Оценка: +1
Здравствуйте, _FRED_, Вы писали:

_FR>Спасибо за то, что откликнулись на эту тему. Детали-то там находятся все по ссылке из предыдущего сообщения


Согласен, ссылка там есть, но решил отдельно акцентировать внимание, вдруг кто-то не воспользовался ссылкой
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.