Dziwny problem z operacją w programie SQL Server: -100 / -100 * 10 = 0

106
  • Jeśli wykonasz, SELECT -100/-100*10wynikiem jest 0.
  • Jeśli wykonasz, SELECT (-100/-100)*10wynikiem jest 10.
  • Jeśli wykonasz, SELECT -100/(-100*10)wynikiem jest 0.
  • Jeśli wykonasz, SELECT 100/100*10wynikiem jest 10.

BOL stwierdza:

Gdy dwa operatory w wyrażeniu mają ten sam poziom pierwszeństwa operatorów, są one oceniane od lewej do prawej na podstawie ich pozycji w wyrażeniu.

I

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

Czy BOL jest źle, czy coś mi brakuje? Wygląda na -to, że odrzuca (oczekiwany) priorytet.

cuizizhe
źródło
7
Jakie jest Twoje pytanie?
Ilyes
14
Jak myślisz, dlaczego masz do czynienia z bitami, pracujesz z liczbami całkowitymi. A integer / integer = integer. Więc -100 / -1000 to 0
sepupic
5
OK, zgadzam się, -to wydaje się być przyczyną „nieprawidłowego” przepływu. Jeśli spróbujesz -100/(-100)*10, otrzymasz wynik 10. wydaje się, że /jest on stosowany względem wartości -w równaniu, a następnie równanie 100*10jest określane. Nie jestem pewien, czy jest to błąd w BOL, ale bardziej niż SQL Server nie zachowuje się zgodnie z oczekiwaniami. Warto poruszyć problem dotyczący sql-docs i sprawdzić, jaka jest ich odpowiedź; być może można by dodać notatkę do dokumentacji informującą o „funkcji”.
Larnu
3
SELECT -100/(-100)*10również zwraca 10. Wygląda na to, że -jest traktowany jako -operator, który powinien być zastosowany dopiero po 100*10obliczeniu
Panagiotis Kanavos
7
A / -B * Cjest A <div> <negate> B <multiply> C. Negacja ma niższy priorytet niż mnożenie, zgodnie z dokumentami, więc wynik jest A / -(B * C). Możesz to zobaczyć wyraźniej, używając stałych zmiennoprzecinkowych: w 12e / -13e * 14eporównaniu 12e / (-13e) * 14ez 12e / 13e * 14e. Powodem, dla którego to nas wytrąca z równowagi, jest to, że generalnie oczekujemy, że jednoargumentowy minus stanie się częścią literału lub przynajmniej będzie miał bardzo wysoki priorytet, ale nie w ten sposób T-SQL Pracuje.
Jeroen Mostert

Odpowiedzi:

96

Zgodnie z tabelą pierwszeństwa jest to oczekiwane zachowanie. Operator o wyższym priorytecie ( /i *) jest oceniany przed operatorem o niższym priorytecie (jednoargumentowy -). Więc to:

-100 / -100 * 10

jest oceniany jako:

-(100 / -(100 * 10))

Zauważ, że to zachowanie różni się od większości języków programowania, w których jednoargumentowa negacja ma wyższy priorytet niż mnożenie i dzielenie, np. VB , JavaScript .

Salman A
źródło
38
Wow, kolejna funkcja perełek w T-SQL :) Myślę, że muszę teraz przeprowadzić audyt całego mojego kodu, aby wyszukać błędy.
usr
14
o stary. Jest to nawet gorsze niż błędy różnych operatorów trójskładnikowych PHP bugs.php.net/bug.php?id=61915 . Którzy rozsądni ludzie myślą, że operatory jednoargumentowe muszą mieć niższy priorytet niż operatory binarne?
phuclv
12
Prawdziwa różnica może polegać na tym, czy -jest uważany za operatora w -100. W niektórych językach jest częścią składni liczby całkowitej.
Barmar
7
Więc jest to błąd w ich pierwszeństwie jednoargumentowym -.
Kevin
4
Zwycięzcą projektu sprzecznego z intuicją jest ...: Microsoft - jeszcze raz
rexkogitans
34

BOL ma rację. -ma niższy priorytet niż *, tak

-A * B

jest analizowany jako

-(A * B)

Mnożenie jest tym, czym jest, zwykle tego nie zauważasz, z wyjątkiem mieszania dwóch innych operatorów binarnych z równym priorytetem: /i %(i %jest rzadko używane w takich wyrażeniach złożonych). Więc

C / -A * B

Jest analizowany jako

C / -(A * B)

wyjaśnianie wyników. Jest to sprzeczne z intuicją, ponieważ w większości innych języków jednoargumentowy minus ma wyższy priorytet niż *i /, ale nie w T-SQL, i jest to poprawnie udokumentowane.

Miły (?) Sposób, aby to zilustrować:

SELECT -1073741824 * 2

tworzy przepełnienie arytmetyczne, ponieważ -(1073741824 * 2)produkuje 2147483648jako półprodukt, który nie pasuje do INT, ale

SELECT (-1073741824) * 2

daje oczekiwany wynik -2147483648, który tak.

Jeroen Mostert
źródło
„minus” jest binarny. Jednoargumentowe -jest „negatywne”. Ludzie, którzy mówią na przykład „minus 10”, gdy mają na myśli „minus 10”, są nieprecyzyjni.
Akumulacja
11
@Acccumulation: niedokładność nie jest moja. -Operator, po nałożeniu na jednym argumencie, nazywa się MINUSw planów kwerend SQL. Jego binarny odpowiednik nazywa się SUB. Jeśli chcesz, interpretuj „jednoargumentowy minus” jako skrót od „jednoargumentowego operatora oznaczanego znakiem minus” - oznaczenie składniowe, a nie semantyczne.
Jeroen Mostert
3
„Negatywne 10” to standardowe amerykańskie użycie (jak sądzę), ale nie jest to standard w Wielkiej Brytanii.
Alchymist
12

Zauważ w dokumentacji, że (być może wbrew intuicji) kolejność pierwszeństwa dla - (Negative)jest trzecia.

Więc skutecznie otrzymujesz:

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

Jeśli umieścisz je w zmiennych, nie zobaczysz, że to się stanie, ponieważ po pomnożeniu nie ma operacji jednoargumentowej.

Więc tutaj A i B są takie same, podczas gdy C, D, E pokazują wynik, który widzisz (gdzie E ma pełne nawiasy)

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
źródło