Em Javascript, Excel, Python e uma calculadora científica, a operação de módulo
-34086.1576962834 % 360.0 <-- python
=MOD(-34086.1576962834, 360.0) <-- Excel function
dá um valor de 113.84230371659942
. (Ou algo muito próximo disso, dependendo do número de casas decimais e arredondamento.)
Mas no T-SQL,
Select -34086.1576962834 % 360.0
retorna um valor de -246.1576962834
.
eu tentei usar
Select cast(-34086.1576962834 as numeric(30,15)) % cast(360.0 as numeric(30,15))
Como eu estava pensando, talvez este fosse um exemplo de flutuação versus matemática decimal fixa que não correspondia, mas isso não resolve.
Por que o SQL Server não executa esta operação de maneira consistente com as calculadoras/resultados esperados? E mais importante, existe uma maneira de fazer o T-SQL fazer aritmética da mesma forma que minha calculadora faz?
Estou usando principalmente o SQL Server 2019, mas confirmei que o SQL Server 2012 e 2014 também fornecem os mesmos -246... resultados.
A função Oracle mod(x,y)
e o python math.fmod(x,y)
fornecem o mesmo resultado -246.nnn.
De acordo com a Wikipedia , o padrão SQL implementa a versão truncada do
%
operador que para um número a = nq + r onde q é um número inteiro, define o resto r como:r = a - n [ a / n ] onde [] é a função de parte inteira (parte inteira) 1
Portanto, inserir o exemplo na equação resulta
Portanto, de acordo com o padrão SQL, a
%
função está funcionando conforme o esperado. Consulte Postgres e MySQL implementando o padrão de maneira semelhante.Se você deseja obter o mesmo resultado de um método que usa divisão horizontal em vez da parte integral quando o dividendo é negativo, você pode adicionar o divisor ao resultado como [ x ] + 1 = FLOOR( x ) quando x é negativo .
Ou, conforme demonstrado neste SO, a resposta
(n + a % n) % n)
converterá para o método de divisão por piso, independentemente de o dividendo ser positivo ou negativo 2 .Isto é o que o SQL Standard tem a dizer: (graças a ypercubeᵀᴹ e esta postagem no blog ):
Portanto, você notará muita "escala de zero", mas isso não é relevante, pois se removermos essa restrição de N , M e R , o cálculo permanecerá inalterado. Movendo as coisas temos:
R = N - M * K com || R || < || M || e SIGN( R ) = SIGN( N ).
Isso não é math.stackexchange.com , então não farei uma prova formal, mas essas duas restrições implicam que K nessa instância é [ N / M ] e não FLOOR( N / M ).
Usando seu exemplo temos então:
1 Isso não é o mesmo que a
FLOOR
função quando aplicada a valores negativos.2 A prova é deixada como um exercício para o leitor porque o autor não toca em um livro de matemática há mais de 10 anos.
Isso parece ser um problema de como o SQL Server calcula o módulo com números negativos.
Você notará que, se pegar sua calculadora científica (estou usando o Windows embutido
calc.exe
no modo científico) e fizer as contas para um quociente positivo e um quociente negativo, obterá 113.nnn e 246. números nnn:Se dividirmos a matemática do módulo e fizermos "o caminho mais longo", poderemos ver o que o SQL Server está fazendo:
IMHO, isso é um bug
O SQL Server está sempre fazendo um
FLOOR
para calcular o quociente, em vez de sempre arredondar para zero. Na minha opinião, isso é um bug, pois as regras da matemática nos dizem que devemos arredondar para zero, em vez de arredondar para baixo.O artigo da Wikipedia sobre "operação de módulo" mostra que existem diferentes interpretações do que "módulo" significa, particularmente quando se move para números reais - embora os números naturais tenham uma definição de módulo mais universalmente compreendida.
De acordo com o artigo da Wikipedia vinculado acima, o padrão ISO SQL exige a fórmula de "divisão truncada":
Observe que isso exige arredondamento para zero . Este é o comportamento que a maioria das pessoas espera, porque o arredondamento para zero parece "natural" para muitas pessoas ao arredondar.
Em vez disso, o SQL Server usa o método "divisão por piso":
Observe que isso exige arredondamento para baixo .
A Microsoft provavelmente não mudará o comportamento
O SQL Server tem esse comportamento há tanto tempo que não esperaria que o SQL Server mudasse sua implementação para usar a fórmula de "divisão truncada" exigida pelo padrão ISO SQL para ambos
%
emod()
. Alterar essa funcionalidade agora resultaria em anos de código que dependem do comportamento existente para quebrar.Em vez disso, eu esperaria que a Microsoft documentasse claramente a funcionalidade existente para deixar claro qual método eles estão usando para calcular o módulo. Se tivermos sorte, eles podem introduzir uma nova sintaxe que executa o módulo usando o outro método também, para que as pessoas possam selecionar o comportamento apropriado para suas necessidades.