Un extraño problema de operación en SQL Server: -100 / -100 * 10 = 0

106
  • Si ejecuta SELECT -100/-100*10el resultado es 0.
  • Si ejecuta SELECT (-100/-100)*10el resultado es 10.
  • Si ejecuta SELECT -100/(-100*10)el resultado es 0.
  • Si ejecuta SELECT 100/100*10el resultado es 10.

BOL declara:

Cuando dos operadores de una expresión tienen el mismo nivel de precedencia de operadores, se evalúan de izquierda a derecha según su posición en la expresión.

Y

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

¿BOL está mal o me falta algo? Parece que -está descartando la precedencia (esperada).

cuizizhe
fuente
7
¿Cuál es tu pregunta?
Ilyes
14
Por qué cree que tiene que ver con bits, está trabajando con enteros. Y entero / entero = entero. Entonces -100 / -1000 es 0
sepupic
5
De acuerdo, estoy de acuerdo, eso -parece estar causando que el flujo "salga mal". Si lo intentas -100/(-100)*10, obtienes el resultado 10. parece que /se está aplicando el valor -en la ecuación y luego 100*10se está determinando la ecuación . No estoy seguro de que esto sea un error con BOL, pero más que SQL Server no se está comportando como se esperaba. Podría valer la pena plantear un problema sobre sql-docs y ver cuál es su respuesta; tal vez podría agregarse una nota a la documentación informando sobre la "característica".
Larnu
3
SELECT -100/(-100)*10también devuelve 10. Parece que -se trata como el -operador que debe aplicarse solo después de que 100*10se calcula
Panagiotis Kanavos
7
A / -B * Ces A <div> <negate> B <multiply> C. Negar tiene menor precedencia que multiplicar, según los documentos, por lo que el resultado es A / -(B * C). Puede ver esto más claramente usando constantes flotantes: 12e / -13e * 14evs 12e / (-13e) * 14evs. 12e / 13e * 14eLa razón por la que esto nos desconcierta es porque generalmente esperamos que los menos unarios se conviertan en parte del literal, o al menos tengan una precedencia muy alta, pero no es así como T-SQL trabajos.
Jeroen Mostert

Respuestas:

96

Según la tabla de precedencia, este es el comportamiento esperado. El operador con mayor precedencia ( /y *) se evalúa antes que el operador con menor precedencia (unario -). Así que esto:

-100 / -100 * 10

se evalúa como:

-(100 / -(100 * 10))

Tenga en cuenta que este comportamiento es diferente de la mayoría de los lenguajes de programación donde la negación unaria tiene mayor precedencia que la multiplicación y la división, por ejemplo , VB , JavaScript .

Salman A
fuente
38
Vaya, otra característica de la joya en T-SQL :) Supongo que ahora tengo que auditar todo mi código para buscar errores.
usr
14
Oh hombre. Esto es incluso peor que los diversos errores del operador ternario de PHP bugs.php.net/bug.php?id=61915 . ¿Qué gente cuerda piensa que los operadores unarios deben tener menor precedencia que los binarios?
phuclv
12
La verdadera diferencia puede ser si -se considera un operador en -100. En algunos lenguajes, es parte de la sintaxis de un número entero.
Barmar
7
Entonces es un error en su precedencia de unario -.
Kevin
4
Y el ganador del diseño contrario a la intuición es ...: Microsoft - una vez más
rexkogitans
34

BOL es correcto. -tiene menor precedencia que *, entonces

-A * B

se analiza como

-(A * B)

Siendo la multiplicación lo que es, normalmente no se nota esto, excepto cuando se mezclan los otros dos operadores binarios con igual precedencia: /y %(y %rara vez se usa en expresiones compuestas como esta). Entonces

C / -A * B

Se analiza como

C / -(A * B)

explicando los resultados. Esto es contrario a la intuición porque en la mayoría de los otros lenguajes, unario menos tiene mayor precedencia que *y /, pero no en T-SQL, y esto está documentado correctamente.

Una buena (?) Forma de ilustrarlo:

SELECT -1073741824 * 2

produce un desbordamiento aritmético, porque -(1073741824 * 2)produce 2147483648como un intermedio, que no cabe en un INT, pero

SELECT (-1073741824) * 2

produce el resultado esperado -2147483648, que lo hace.

Jeroen Mostert
fuente
"menos" es binario. Unario -es "negativo". Las personas que dicen cosas como "menos 10" cuando quieren decir "menos 10" están siendo imprecisas.
Acumulación
11
@ Acumulación: la imprecisión no es mía. El -operador, cuando se aplica a un solo operando, se llama MINUSen los planes de consulta SQL. Su contraparte binaria se llama SUB. Si lo desea, interprete "menos unario" como abreviatura de "el operador unario representado por el signo menos", una designación sintáctica en lugar de semántica.
Jeroen Mostert
3
"10 negativo" es el uso estándar estadounidense (creo), pero no es estándar en el Reino Unido.
Alquimista
12

Observe en la documentación que (quizás de forma contraria a la intuición) el orden de precedencia de - (Negative)es tercero.

Entonces efectivamente obtienes:

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

Si los coloca en variables, no verá que esto suceda, ya que no hay una operación unaria que ocurra después de la multiplicación.

Entonces, aquí A y B son iguales, mientras que C, D, E muestran el resultado que está viendo (con E con el corchete 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
fuente