2014-10-15 16 views
8

oparte na tym ciekawym pytaniem: Addition of int and uint i toying wokół z stałej składania jak wspomniano w Nicholas Carey'sanswer, ja natknęliśmy się na pozornie niespójne zachowanie kompilatora:Odjęcie uint oraz int i ciągłe składanie

Rozważmy następujący fragment kodu:

int i = 1; 
uint j = 2; 
var k = i - j; 

tu kompilator poprawnie rozwiązuje k do long. To szczególne zachowanie jest dobrze określone w specyfikacjach, jak wyjaśniono w odpowiedziach na poprzednie pytanie.

Co było zaskakujące dla mnie, jest to, że zachowanie zmienia się, gdy do czynienia z dosłownych stałych lub stałych w ogóle. Czytając Nicholas Carey answer zdałem sobie sprawę, że zachowanie może być niezgodne więc sprawdziłem i rzeczywiście:

const int i = 1; 
const uint j = 2; 
var k = i - j; //Compile time error: The operation overflows at compile time in checked mode. 
k = 1 - 2u; //Compile time error: The operation overflows at compile time in checked mode. 

k w tym przypadku został rozwiązany do Uint32.

Czy istnieje powód, dla którego zachowanie różni się, gdy mamy do czynienia z stałych lub jest to mały, ale niefortunny "błąd" (brak lepszego terminu) w kompilatorze?

+2

Przy odgadywaniu kompilator nie wykonuje niejawnych konwersji stałych. – Powerlord

+0

@Powerlord Cóż, to przecież jest * to * niejawna konwersja 'int' na' uint'. – InBetween

+1

Specyfikacja pozwala na to, chociaż ... §6.1.9 Konwersja stałych konwersji wyrażenia: "Wyrażenie stałe (§7.19) typu int może zostać przekonwertowane na typ' sbyte', 'byte',' short', ' ushort', 'uint' lub' ulong', pod warunkiem, że wartość wyrażenia stałego mieści się w zakresie typu miejsca docelowego. " Nadal próbuję znaleźć to, co mówi literały i/lub stałe zmienne. – Powerlord

Odpowiedz

3

Sprawdź tę odpowiedź here

Problem polega na tym, że używasz const.

W czasie wykonywania, gdy jest const, zachowanie jest dokładnie takie samo jak w literałach, lub tak, jakbyś po prostu zakodował te liczby w kodzie, więc od liczby 1 i 2 rzutuje na Uint32, ponieważ 1 jest w zakresie uint32. Wtedy, gdy spróbujesz odjąć 1 - 2 z uint32, przepełni się, ponieważ 1u - 2u = +4,294,967,295 (0xFFFFFFFF).

Kompilator może przeglądać literatury i interpretować je inaczej niż inne zmienne. Ponieważ const nigdy się nie zmieni, może uzyskać gwarancje, których inaczej nie mógłby uczynić. w tym przypadku może zagwarantować, że 1 znajduje się w zakresie uint, w związku z czym może go w sposób niecelowy obsadzić. W normalnych okolicznościach (bez stałej) nie może ona zagwarantować, że podpisany int mieści się w zakresie -2,147,483,648 (0x80000000) do +2,147,483,647 (0x7FFFFFFF).

bez znaku int wynosi od 0 (0x00000000) do +4,294,967,295 (0xFFFFFFFF).

Morał z historii, bądź ostrożny przy mieszaniu const i var, możesz dostać coś, czego się nie spodziewasz.

+0

Prawo, derp, problem dotyczy 'var', a nie stałej. Pokonaj jednego, aby zszedł na złą ścieżkę, przeglądając specyfikację. – Powerlord

+0

przy użyciu 'var' tutaj nie jest problem, ponieważ jest to błąd czasu kompilacji. W prawdziwym kodzie nigdy nie użyłbym 'var' w wyrażeniu, w którym typ nie jest krystalicznie czysty na pierwszy rzut oka. I wiem, że używanie 'const' lub * constant literales * będzie miało takie samo zachowanie. Moje pytanie brzmi: * dlaczego * to zachowanie jest niespójne z ogólnym, w którym biorą udział zmienne. – InBetween

+0

@Powerlord Nie, 'var' nie jest problemem. 'long k = 1 - 2u;' jest także błędem czasu kompilacji. W języku C# typ zwracany nigdy nie odgrywa roli w rozdzielczości przeciążania, więc dlaczego "var" byłby częścią problemu?Problem polega na tym, że '1 - 2u' radzi sobie z' uint' w przypadku * stałych *. – InBetween

4

Z C# specification version 5, sekcja 6.1.9, Constant Ekspresja pozwalają jedynie następujących niejawne konwersje

6.1.9 niejawnej stałą ekspresję konwersje
Pośrednia konwersja stała ekspresja umożliwia następujące konwersje:
* a ciągła ekspresja (§7.19) typu int można przekonwertować na typ sbyte, byte, short, ushort, uint lub ulong, pod warunkiem, że wartość wyrażenia stałego mieści się w zakresie typu miejsca docelowego.
• Stałą ekspresję typu long można przekonwertować na typ ulong, pod warunkiem, że wartość wyrażenia stałego nie jest ujemna.

Uwaga: long nie znajduje się na liście konwersji .

Druga połowa problem jest to, że tylko niewielka liczba promocji numerycznych zdarzyć operacji binarnych:

(z sekcji 7.3.6.2 binarne promocjach numeryczny):

  • Jeśli któryś z operandów jest typu dziesiętnego, drugi operand jest konwertowany na dziesiętny typu lub błąd czasu wiązania występuje, gdy drugi operand jest typu float lub double.
  • W przeciwnym razie, jeśli jeden z operandów jest typu double, drugi operand jest konwertowany na typ double.
  • W przeciwnym wypadku, jeśli jeden z argumentów jest typu zmiennoprzecinkowego, drugi operand jest konwertowany na typ zmiennoprzecinkowy.
  • W przeciwnym wypadku, jeśli jeden z operandów jest typu ulong, drugi operand jest konwertowany na typ ulong lub błąd czasu wiązania występuje, gdy drugi operand jest typu sbyte, short, int lub long.
  • W przeciwnym wypadku, jeśli jeden z operandów jest typu long, drugi operand jest konwertowany na typ long.
  • W przeciwnym wypadku, jeśli jeden z operandów jest typu uint, a drugi operand jest typu sbyte, short lub int, oba operandy są przekształcane na typ long.
  • W przeciwnym wypadku, jeśli jeden z operandów jest typu uint, drugi operand jest konwertowany na typ uint.
  • W przeciwnym razie oba operandy są konwertowane na typ int.

PAMIĘTAJ: int do long konwersji zabronione stałych, co oznacza, że ​​oba argumenty są natomiast promowany do uint s.

+0

Nawiasem mówiąc, nic nie wskazuje na to, dlaczego wyrażenia stałe "int" nie mogą być konwertowane na 'long's ... ani jakiekolwiek rozważania, czy stałe' uint' mogą być w ogóle konwertowane ... i nie ma wyjaśnienia, dlaczego w którymkolwiek z nich tych przypadków. – Powerlord