为什么在 SQL Server 中乘以 DECIMAL 时会出现精度损失?

Sco*_*.Hu 4 sql t-sql sql-server

在SQL Server中运行此脚本(我的版本是SQL Server 2016):

DECLARE @num1 DECIMAL(20,7)
DECLARE @num2 DECIMAL(20,7)
SET @num1 = 0.0000005
SET @num2 = 1.0
SELECT @num1 * @num2 AS Result
Run Code Online (Sandbox Code Playgroud)

结果:

Result
---------------------------------------
0.00000050000
Run Code Online (Sandbox Code Playgroud)

结果的规模为 0.00000050000DECIMAL(X,11)

现在,如果我们将脚本更改如下:

DECLARE @num1 DECIMAL(21,7)
DECLARE @num2 DECIMAL(20,7)
SET @num1 = 0.0000005
SET @num2 = 1.0
SELECT @num1 * @num2 AS Result
Run Code Online (Sandbox Code Playgroud)

结果:

Result
---------------------------------------
0.0000005000
Run Code Online (Sandbox Code Playgroud)

你可以看到结果是0.0000005000,看起来比例缩小了DECIMAL(X,10)

继续提高@num1的精度:

DECLARE @num1 DECIMAL(22,7)
DECLARE @num2 DECIMAL(20,7)
SET @num1 = 0.0000005
SET @num2 = 1.0
SELECT @num1 * @num2 AS Result
Run Code Online (Sandbox Code Playgroud)

结果:

Result
---------------------------------------
0.000000500
Run Code Online (Sandbox Code Playgroud)

现在的结果是 0.000000500,看起来比例缩小了DECIMAL(X,9)

我想问两个DECIMAL数值计算后的精确精度和小数位数是多少。如果我们将两个 DECIMAL 数字相乘,似乎会出现严重的精度损失。由于精度损失,以下脚本将给出完全错误的结果,这将舍入超出结果范围的分数。

DECLARE @num1 DECIMAL(25,7)
DECLARE @num2 DECIMAL(20,7)
DECLARE @num3 DECIMAL(25,7)
DECLARE @num4 DECIMAL(20,7)
SET @num1 = 0.0000005
SET @num2 = 1.0
SET @num3 = 0.0000004
SET @num4 = 1.0
SELECT @num1 * @num2 AS Result1, @num3 * @num4 AS Result2
Run Code Online (Sandbox Code Playgroud)

结果:

Result1                                 Result2
--------------------------------------- ---------------------------------------
0.000001                                0.000000
Run Code Online (Sandbox Code Playgroud)

Zho*_*rov 6

当您在问题中进行乘法时,实际的精度和小数位数是根据以下规则计算的:

  • 结果精度和小数位的绝对最大值为 38。
  • 结果精度为p1 + p2 + 1,结果小数位数为s1 + s2p1s1是第一个表达式的精度和小数位数,p2s2第二个表达式的精度和小数位数)。
  • 如果整数部分小于 32,则结果比例会减小min(scale, 38 - (precision-scale)),因为它不能大于 38 - (精度比例)。在这种情况下,结果可能会四舍五入

所以,在你的例子中:

DECLARE @num1 DECIMAL(20,7)
DECLARE @num2 DECIMAL(20,7)
SET @num1 = 0.0000005
SET @num2 = 1.0
SELECT @num1 * @num2 AS Result
Run Code Online (Sandbox Code Playgroud)

比例的实际计算是:

  • 7 + 7 = 14
  • 分钟 (14, 38 - ((20 + 20 + 1) - (7 + 7))) = 11