Vraag Waarom wordt de implementatie van de C # -specificatie verlaten (int.MinValue / -1)?


De uitdrukking int.Minvalue / -1 resulteert in door de implementatie gedefinieerd gedrag volgens de C # -specificatie:

7.8.2 Divisie-operator

Als de linker operand de kleinste representeerbare int of lange waarde is en de rechter operand -1, is   overloop treedt op. In een gecontroleerde context veroorzaakt dit een   System.ArithmeticException (of een subklasse daarvan) die moet worden geworpen. In een   niet-gecontroleerde context, dat is het -Implementatie gedefinieerd of een   System.ArithmeticException (of een subklasse daarvan) wordt gegenereerd of de   overloop wordt niet gerapporteerd met als resultaat de waarde die van de   linker operand.

Testprogramma:

var x = int.MinValue;
var y = -1;
Console.WriteLine(unchecked(x / y));

Dit gooit een OverflowException op .NET 4.5 32bit, maar dat hoeft niet.

Waarom laat de specificatie de uitkomstimplementatie-gedefinieerd? Hier is de reden om dat te doen:

  1. De x86 idiv instructie resulteert altijd in een uitzondering in dit geval.
  2. Op andere platforms kan een runtime-controle nodig zijn om dit te emuleren. Maar de kosten van die controle zouden laag zijn in vergelijking met de kosten van de divisie. Integer deling is extreem duur (15-30 cycli).
  3. Dit opent compatibiliteitsrisico's ("schrijf eens uitgevoerd nergens").
  4. Ontwikkelaar verrassing.

Ook interessant is het feit, dat als x / y is een compiletime constante die we inderdaad krijgen unchecked(int.MinValue / -1) == int.MinValue:

Console.WriteLine(unchecked(int.MinValue / -1)); //-2147483648

Dit betekent dat x / y kan verschillende gedragingen hebben, afhankelijk van de syntactische vorm die wordt gebruikt (en niet alleen afhankelijk van de waarden van x en y). Dit is toegestaan ​​door de specificatie, maar het lijkt een onverstandige keuze. Waarom is C # zo ontworpen?

EEN soortgelijke vraag geeft aan waar in de specificatie dit exacte gedrag is voorgeschreven, maar dat het niet (voldoende) antwoordt waarom de taal is op deze manier ontworpen. Alternatieve keuzes worden niet besproken.


24
2017-08-02 18:14


oorsprong


antwoorden:


Dit is een neveneffect van de grotere broer van de C # -taalspecificatie, ECMA-335, de Common Language Infrastructure-specificatie. Hoofdstuk III, hoofdstuk 3.31 beschrijft wat de DIV-opcode doet. Een spec die de C # spec heel vaak moet uitstellen, is behoorlijk onvermijdelijk. Het specificeert dat mei gooien maar eist het niet.

Anders een realistische beoordeling van wat echte processors doen. En degene die iedereen gebruikt is de vreemde. Intel-processors zijn buitengewoon eigenzinnig over overloopgedrag, ze zijn al in de jaren 70 ontworpen met de veronderstelling dat iedereen de INTO-instructie zou gebruiken. Niemand doet, een verhaal voor een andere dag. Het negeert echter geen overflow op een IDIV en verhoogt de #DE val, kan die luide knal niet negeren.

Erg moeilijk om een ​​taalspecificatie te schrijven bovenop een wollige runtime-specificatie bovenop het inconsistente processorgedrag. Weinig dat het C # -team daarmee kon doen, maar de onnauwkeurige taal doorsturen. Ze gingen al verder dan de specificatie door OverflowException te documenteren in plaats van ArithmeticException. Erg ondeugend. Ze hadden een kijkje.

Een kijkje dat de praktijk onthulde. Het is zeer onwaarschijnlijk dat er een probleem is, de jitter beslist of hij al dan niet wil inline. En de niet-inline-versie worpen, is de verwachting dat de inline-versie dat ook doet. Niemand is nog teleurgesteld.


10
2017-08-02 18:30



Een van de belangrijkste ontwerpdoelstellingen van C # is naar verluidt 'Law of Minimum Surprise'. Volgens deze richtlijn moet de compiler niet proberen de bedoeling van de programmeur te raden, maar moet hij aan de programmeur aangeven dat er extra begeleiding nodig is om de intentie correct te specificeren. Dit is van toepassing op het geval van interesse omdat, binnen de beperkingen van twee-complement rekenkunde, de operatie resulteert in een zeer verrassend resultaat: Int32.MinValue / -1 evalueert naar Int32.MinValue. Er is een overloop opgetreden en een niet-beschikbaar 33-bits bit van 0 is vereist om de juiste waarde van Int32.MaxValue + 1.

Zoals verwacht, en genoteerd in je citaat, wordt in een gecontroleerde context een uitzondering gemaakt om de programmeur attent te maken op het niet correct specificeren van de intentie. In een niet-gecontroleerde context mag de implementatie zich gedragen als in de gecontroleerde context, of om de overloop en retourneer het verrassende resultaat. Er zijn bepaalde contexten, zoals bit-twiddling, waarbij het handig is om te werken met ondertekende int's maar waar het overloopgedrag eigenlijk wordt verwacht en gewenst. Door de implementatieopmerkingen te controleren, kan de programmeur bepalen of dit gedrag ook daadwerkelijk zo is verwacht.


7