SQL Server 2005 числовая потеря точности - PullRequest
5 голосов
/ 24 сентября 2008

Отладка некоторого кода SQL, связанного с финансами, обнаружила странную проблему с математической точностью (24,8).

Запустив следующий запрос на вашем MSSQL, вы получите результат выражения A + B * C, равный 0.123457

ВЫБРАТЬ А, B, C, A + B * C ОТ ( ВЫБЕРИТЕ CAST (0.12345678 AS NUMERIC (24,8)) AS A, CAST (0 AS NUMERIC (24,8)) AS B, АКТЕРЫ (500 КАК ЧИСЛО (24,8)) КАК C ) Т

Итак, мы потеряли 2 значимых символа. Пытаясь исправить это по-разному, я понял, что преобразование промежуточного результата умножения (нуля!) В числовое (24,8) будет работать нормально.

И, наконец, есть решение. Но все же у меня возникает вопрос - почему MSSQL ведет себя таким образом и какие преобразования типов действительно произошли в моем образце?

Ответы [ 3 ]

7 голосов
/ 24 сентября 2008

Так же, как сложение типа с плавающей точкой является неточным, умножение десятичных типов может быть неточным (или вызывать неточность), если вы превысите точность. См. Преобразование типов данных и Десятичные и числовые .

Так как вы умножили NUMERIC(24,8) и NUMERIC(24,8), и SQL Server будет проверять только тип, а не содержимое, он, вероятно, попытается сохранить возможные 16 недесятичных цифр (24 - 8), когда не может сохранить все 48 цифр точности (макс. 38). Объединив два из них, вы получите 32 недесятичных знака, в результате чего у вас останется только 6 десятичных знаков (38 - 32).

Таким образом, оригинальный запрос

SELECT A, B, C, A + B * C
FROM ( SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A,
  CAST(0 AS NUMERIC(24,8)) AS B,
  CAST(500 AS NUMERIC(24,8)) AS C ) T

уменьшается до

SELECT A, B, C, A + D
FROM ( SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A,
  CAST(0 AS NUMERIC(24,8)) AS B,
  CAST(500 AS NUMERIC(24,8)) AS C,
  CAST(0 AS NUMERIC(38,6)) AS D ) T

Опять же, между NUMERIC(24,8) и NUMERIC(38,6), SQL Server будет пытаться сохранить потенциальные 32 цифры не десятичных, поэтому A + D уменьшается до

SELECT CAST(0.12345678 AS NUMERIC(38,6))

, что дает вам 0.123457 после округления.

0 голосов
/ 04 июня 2013

Несмотря на то, что написано в Точность, масштаб и длина (Transact-SQL) . Я считаю, что он также применяет минимальный «масштаб» (количество десятичных разрядов), равный 6, к результирующему типу NUMERIC для умножения, так же, как и для деления и т. Д.

0 голосов
/ 24 сентября 2008

Следуя логике, указанной eed3si9n и тем, что вы сказали в своем вопросе, кажется, что лучший подход при выполнении математических операций - это извлечь их в функцию и дополнительно указать точность после каждой операции,

В этом случае функция может выглядеть примерно так:

create function dbo.myMath(@a as numeric(24,8), @b as numeric(24,8), @c as numeric(24,8))
returns  numeric(24,8)
as
begin 
    declare @d as numeric(24,8)
    set @d = @b* @c
    return @a + @d
end
...