SQL Modulo 函数给出了错误的值?

CaM*_*CaM 8 sql-server t-sql

在 Javascript、Excel、Python 和科学计算器中,模运算

-34086.1576962834 % 360.0      <-- python
=MOD(-34086.1576962834, 360.0) <-- Excel function
Run Code Online (Sandbox Code Playgroud)

给出一个值113.84230371659942. (或者非常接近的东西,取决于小数位数和四舍五入。)

但在 T-SQL 中,

Select -34086.1576962834 % 360.0
Run Code Online (Sandbox Code Playgroud)

返回值-246.1576962834.

我尝试过使用

Select cast(-34086.1576962834 as numeric(30,15)) % cast(360.0 as numeric(30,15))
Run Code Online (Sandbox Code Playgroud)

我在想,这可能是浮点与固定十进制数学不匹配的一个例子,但这并不能解决问题。

为什么 SQL Server 不以与计算器/预期结果一致的方式执行此操作?更重要的是,有没有办法让 T-SQL 像我的计算器一样进行算术运算?

我主要使用 SQL Server 2019,但我已经确认 SQL Server 2012 和 2014 也给出相同的 -246... 结果。

Oracle 函数mod(x,y)和 pythonmath.fmod(x,y)给出相同的 -246.nnn 结果。

小智 13

根据Wikipedia,SQL 标准实现了%运算符的截断版本,对于数字a = nq + r(其中q是整数),定义余数r定义为:

\n

r = a - n [ a / n ] 其中 [] 是积分部分函数(整数部分) 1

\n

因此,将示例代入方程可得出

\n
r = -34086.1576962834 - 360.0[-34086.1576962834/360.0] \n\n  = -34086.1576962834 - 360.0[-94.68377138]\n\n  = -34086.1576962834 - 360.0(-94)\n\n  = -246.1576962834\n
Run Code Online (Sandbox Code Playgroud)\n

因此,根据 SQL 标准,该%函数正在按预期工作。请参阅PostgresMySQL类似地实现该标准。

\n

如果您希望在被除数为负数时从使用下取除法而不是积分部分的方法获得相同的结果,则可以在x为负数时将除数添加到结果中,如下所示 [ x ] + 1 = FLOOR( x ) 。

\n
r + n = a - n[a/n] + n\n\n      = a - n([a/n] + 1) \n\n      = a - n(FLOOR(a/n))\n
Run Code Online (Sandbox Code Playgroud)\n

或者,如本SO 所示,(n + a % n) % n)无论股息是正还是负,答案都将转换为底除法2

\n
\n

这就是 SQL 标准所说的:(感谢ypercube\xe1\xb5\x80\xe1\xb4\xb9和这篇博客文章):

\n
<modulus expression> ::=\n  MOD <left paren>\n      <numeric value expression dividend>\n      <comma>\n      <numeric value expression divisor>\n      <right paren>\n...\nIf <modulus expression> is specified,\nthen the declared type of each\n<numeric value expression>\nshall be exact numeric with scale 0 (zero).\nThe declared type of the result is the\ndeclared type of the immediately contained\n<numeric value expression divisor>.\n...\n9)If <modulus expression> is specified,\n  then let N be the value of the immediately\n  contained <numeric value expression\n  dividend> and let M be the value of the\n  immediately contained <numeric value\n  expression divisor>.\n  Case:\n    a)If at least one of N and M is the null\n      value, then the result is the null value.\n    b)If M is zero,\n      then an exception condition is raised:\n      data exception\xe2\x80\x94division by zero.\n    c)Otherwise, the result is the unique exact\n      numeric value R with scale 0 (zero) such\n      that all of the following are true:\n      i)R has the same sign as N.\n      ii)The absolute value of R is less than\n         the absolute value of M.\n      iii)N = M * K + R for some exact numeric \n          value K with scale 0 (zero).\n
Run Code Online (Sandbox Code Playgroud)\n

因此,您会注意到很多“零范围”,但这并不相关,就好像我们从NMR中删除该限制一样,计算也没有改变。移动事物我们有:

\n

R = N - M * K与 || || < || || 且 SIGN( R ) = SIGN( N )。

\n

这不是math.stackexchange.com,所以我不会做正式证明,但这两个限制意味着在这种情况下K是 [ N / M ] 而不是 FLOOR( N / M )。

\n

使用您的示例,我们有:

\n
R = -34086.1576962834 - 360.0*(-94)\n  = -246.1576962834\n
Run Code Online (Sandbox Code Playgroud)\n
\n

1FLOOR这与应用于负值时的函数不同。

\n

2这个证明留给读者作为练习,因为作者已经十多年没有碰过数学教科书了。

\n

  • Db2 和 Firebird 也返回 -246.xxx。所以看来所有 SQL 数据库都同意它应该如何工作 (2认同)