Um problema de operação estranha no SQL Server: -100 / -100 * 10 = 0

106
  • Se você executar SELECT -100/-100*10o resultado é 0.
  • Se você executar SELECT (-100/-100)*10o resultado é 10.
  • Se você executar SELECT -100/(-100*10)o resultado é 0.
  • Se você executar SELECT 100/100*10o resultado é 10.

BOL declara:

Quando dois operadores em uma expressão têm o mesmo nível de precedência de operador, eles são avaliados da esquerda para a direita com base em sua posição na expressão.

E

Level   Operators
  1     ~ (Bitwise NOT)
  2     * (Multiplication), / (Division), % (Modulus)
  3     + (Positive), - (Negative), + (Addition), + (Concatenation), - (Subtraction), & (Bitwise AND), ^ (Bitwise Exclusive OR), | (Bitwise OR)

O BOL está errado ou estou faltando alguma coisa? Parece que o -está jogando fora a precedência (esperada).

cuizizhe
fonte
7
Qual é a sua dúvida?
Ilyes
14
Por que você acha que tem a ver com bits, você está trabalhando com inteiros. E inteiro / inteiro = inteiro. Então -100 / -1000 é 0
sepupic
5
OK, concordo, isso -parece estar fazendo com que o fluxo saia "errado". Se você tentar, -100/(-100)*10obterá o resultado 10. parece que o /está sendo aplicado contra o valor -na equação e então a equação 100*10está sendo determinada. Não tenho certeza se isso é um erro com o BOL, mas mais que o SQL Server não está se comportando conforme o esperado. Pode valer a pena levantar um problema em sql-docs e ver qual é a resposta deles; talvez uma nota possa ser adicionada à documentação avisando sobre o "recurso".
Larnu
3
SELECT -100/(-100)*10também retorna 10. Parece que -é tratado como o -operador que deve ser aplicado somente após 100*10ser calculado
Panagiotis Kanavos
7
A / -B * Cé A <div> <negate> B <multiply> C. Negar tem precedência menor do que multiplicar, de acordo com os documentos, então o resultado é A / -(B * C). Você pode ver isso mais claramente usando constantes flutuantes: 12e / -13e * 14evs. 12e / (-13e) * 14evs. 12e / 13e * 14eO motivo pelo qual isso nos confunde é porque geralmente esperamos que o menos unário se torne parte do literal, ou pelo menos tenha uma precedência muito alta, mas não é assim que o T-SQL trabalho.
Jeroen Mostert

Respostas:

96

De acordo com a tabela de precedência, esse é o comportamento esperado. O operador com maior precedência ( /e *) é avaliado antes do operador com menor precedência (unário -). Então, é isso:

-100 / -100 * 10

é avaliado como:

-(100 / -(100 * 10))

Observe que esse comportamento é diferente da maioria das linguagens de programação onde a negação unária tem precedência mais alta do que a multiplicação e divisão, por exemplo , VB , JavaScript .

Salman A
fonte
38
Uau, outro recurso precioso no T-SQL :) Acho que tenho que auditar todo o meu código agora para procurar por bugs.
usr
14
Oh cara. Isso é ainda pior do que os vários bugs do operador ternário do PHP , bugs.php.net/bug.php?id=61915 . Que pessoas sãs pensam que os operadores unários devem ter precedência mais baixa do que os binários?
phuclv
12
A diferença real pode ser se -é considerado um operador em -100. Em alguns idiomas, faz parte da sintaxe de um inteiro.
Barmar
7
Portanto, é um bug em sua precedência de unário -.
Kevin
4
E o vencedor do design contra-intuitivo é ...: Microsoft - mais uma vez
rexkogitans
34

BOL está correto. -tem precedência menor do que *, então

-A * B

é analisado como

-(A * B)

Sendo a multiplicação o que é, você normalmente não percebe isso, exceto ao misturar os dois outros operadores binários com igual precedência: /e %(e %raramente é usado em expressões compostas como esta). assim

C / -A * B

É analisado como

C / -(A * B)

explicando os resultados. Isso é contra-intuitivo porque na maioria das outras linguagens, menos unário tem precedência mais alta do que *e /, mas não em T-SQL, e isso está documentado corretamente.

Uma boa (?) Maneira de ilustrar:

SELECT -1073741824 * 2

produz um estouro aritmético, porque -(1073741824 * 2)produz 2147483648como um intermediário, que não cabe em um INT, mas

SELECT (-1073741824) * 2

produz o resultado esperado -2147483648, que o faz.

Jeroen Mostert
fonte
"menos" é binário. Unário -é "negativo". Pessoas que dizem coisas como "menos 10" quando querem dizer "10 negativo" estão sendo imprecisas.
Acumulação de
11
@Accumulação: a imprecisão não é minha. O -operador, quando aplicado a um único operando, é chamado MINUSnos planos de consulta SQL. Sua contraparte binária é chamada SUB. Se quiser, interprete "menos unário" como uma abreviação para "o operador unário representado pelo sinal de menos" - uma designação sintática em vez de semântica.
Jeroen Mostert
3
"negativo 10" é o uso americano padrão (eu acredito), mas não é padrão no Reino Unido.
Alchymist
12

Observe na documentação que (talvez contra-intuitivamente) a ordem de precedência para - (Negative)é a terceira.

Então você efetivamente obtém:

-(100/-(100*10)) = 0

Se você colocá-los em variáveis, não verá isso acontecer, pois não há nenhuma operação unária que ocorre após a multiplicação.

Portanto, aqui A e B são iguais, enquanto C, D, E mostram o resultado que você está vendo (com E tendo o suporte completo)

DECLARE @i1 int, @i2 int, @i3 int;

SELECT @i1 = -100,
       @i2 = -100,
       @i3 = 10;

SELECT @i1/@i2*@i3      [A],
       -100/(-100)*10   [B],
       -100/-100*10     [C],
       -100/-(100*10)   [D],
       -(100/-(100*10)) [E];

A - 10
B - 10
C - 0
D - 0
E - 0
Jamie Pollard
fonte