2011-01-05 4 views
34

Risparmi il seguente codice ed osservazioni:incoerenza nel comportamento di divisione per zero tra diversi tipi di valore

Console.WriteLine(1/0); // will not compile, error: Division by constant zero 

int i = 0; 
Console.WriteLine(1/i); // compiles, runs, throws: DivideByZeroException 

double d = 0; 
Console.WriteLine(1/d); // compiles, runs, results in: Infinity 

posso capire il compilatore controlla attivamente per divisione per costante zero e la DivideByZeroException in fase di esecuzione, ma:

Perché utilizzare un doppio in un ritorno infinito di divisione per zero anziché generare un'eccezione? È questo di progettazione o è un bug?

Solo per i calci, ho fatto questo in VB.NET e, con risultati "più coerenti":

dim d as double = 0.0 
Console.WriteLine(1/d) ' compiles, runs, results in: Infinity 

dim i as Integer = 0 
Console.WriteLine(1/i) ' compiles, runs, results in: Infinity 

Console.WriteLine(1/0) ' compiles, runs, results in: Infinity 

EDIT:

sulla base del feedback di kekekela Ho eseguito il seguente che ha portato nell'infinito:

Console.WriteLine(1/
    .0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001); 

Questo test sembra corroborare l'idea e un doppio letterale di 0.0 è in realtà una frazione molto, molto piccola che risulterà in Infinity ...

+3

Ecco il mio articolo sull'argomento: http://blogs.msdn.com/b/ericlippert/archive/2009/10/15/as-timeless-as-infinity.aspx –

+0

@EricLippert cool! – Jalal

risposta

32

In sintesi: il tipo double definisce un valore di infinito mentre il tipo int non. Quindi nel caso double, il risultato del calcolo è un valore che è possibile esprimere effettivamente nel tipo dato da quando è stato definito. Nel caso int, non esiste alcun valore per l'infinito e quindi nessun modo per restituire un risultato accurato. Da qui l'eccezione.

VB.NET fa le cose in modo un po 'diverso; la divisione intera determina automaticamente un valore in virgola mobile utilizzando l'operatore /. Questo per consentire agli sviluppatori di scrivere, ad es., L'espressione 1/2, e farlo valutare a 0.5, che alcuni considererebbero intuitivo. Se volete vedere un comportamento coerente con C#, provate questo:

Console.WriteLine(1 \ 0) 

Nota l'uso della intero divisione operatore (\, non /) di cui sopra. Credo che otterrai un'eccezione (o un errore di compilazione - non so quale).

Analogamente, provate questo:

Dim x As Object = 1/0 
Console.WriteLine(x.GetType()) 

Il codice sopra emetterà System.Double.

Per quanto riguarda il punto sull'imprecisione, ecco un altro modo di guardarlo. Non è che il tipo double non abbia valore esattamente uguale a zero (lo fa); piuttosto, il tipo double non ha lo scopo di fornire risultati matematicamente esatti in primo luogo. (Alcuni valori possono essere rappresentati esattamente, sì. Ma i calcoli non danno alcuna garanzia di accuratezza.) Dopotutto, il valore dell'espressione matematica non è definito (l'ultima volta che ho controllato) espressione 1/0. Ma 1/x si avvicina all'infinito quando x si avvicina a zero. Quindi, da questa prospettiva, se non possiamo rappresentare la maggior parte delle frazioni n/mesattamente, è opportuno trattare la custodia x/0 come approssimativa e dare il valore approcci - il minimo, l'infinito è definito, almeno.

+1

Anche se molto utilizzabile, anche questa risposta è leggermente errata. La risposta è sbagliata perché la divisione per zero non è l'infinito - è matematicamente indefinito. La vera risposta è che i doppi NON sono numeri reali (R) come indicato nell'ultima parte di questa risposta. Sono numeri in virgola mobile e OP sta cercando di applicare un ragionamento basato su numeri reali su qualcosa che non è un numero reale. Sembrano molto simili perché sono stati progettati per essere simili, ma sono fondamentalmente diversi. Doppie definiscono qualcosa chiamato 'NaN'," Not a Number ", (continua ...) – AnorZaken

+1

(cont ...) che sarebbe un" risultato "matematicamente corretto se si trattasse effettivamente di numeri reali. Eppure non ha restituito NaN. Il motivo è approssimativamente come descritto nella risposta: poiché nella logica in virgola mobile si assume per lo più che "0.0" sia un numero molto piccolo. Non si può tuttavia dire che è un numero piccolo, affermando che c'è _is_ e la rappresentazione esatta di zero è fuorviante, perché non esiste un mapping 1-a-1 da float a R. Piuttosto, ogni valore float si associa a un intervallo di valori reali -valori e il float "0.0" include sia _actual_ zero _and_ un intervallo di altri piccoli valori. – AnorZaken

+0

Un altro esempio di virgola mobile che è fondamentalmente diverso dai numeri reali è che i numeri in virgola mobile definiscono "-0.0" (zero negativo) e hanno OP diviso per il risultato che sarebbe stato "Infinito negativo". Eppure a cosa pensi che '0.0 == -0.0' valuti? – AnorZaken

7

Un double è un numero in virgola mobile e non un valore esatto, quindi quello che stai realmente dividendo dal punto di vista del compilatore è qualcosa che si avvicina a zero, ma non esattamente zero.

+1

In realtà, i doppi hanno una rappresentazione di un valore esattamente uguale a zero. La vera ragione è che per definizione una divisione per zero di un doppio risulta nel valore di Inf - che è di progettazione non per caso. – slebetman

+2

@slebetman - Se i doppi hanno "una rappresentazione di un valore che è esattamente zero", allora perché i doppi distinguono tra "+0" e "-0" e perché 1/+ 0 e 1/-0 danno risultati diversi? L'idea di uno "zero firmato" ha senso solo se quei valori sono visti come valori positivi o negativi che sono troppo piccoli per essere rappresentati normalmente. Si noti che non vi è alcun zero senza segno nei tipi di virgola mobile IEEE 754. –

+1

@Jeffrey: +0 e -0 sono ancora 0. Anche se si sostiene che i numeri inferiori a epsilon sono rappresentati da 0, si sta ancora dicendo che i numeri troppo piccoli sono effettivamente 0. Sì, in matematica -0 non rende senso, ma quando 754 è stato progettato, i fisici che eseguono simulazioni hanno sostenuto che volevano sapere da quale direzione proveniva un risultato se il limite di un calcolo portava a 0. – slebetman

1

Questo probabilmente ha qualcosa a che fare con il fatto che i numeri in virgola mobile IEEE standard e in virgola mobile a precisione doppia hanno un valore "infinito" specificato. .NET sta semplicemente rivelando qualcosa che esiste già, a livello hardware.

Vedere la risposta di kekekela sul perché questo ha senso, logicamente.

1

Questo è di progettazione poiché il tipo double è conforme allo standard IEEE 754, lo standard per l'aritmetica in virgola mobile. Controlla la documentazione per Double.NegativeInfinity e Double.PositiveInfinity.

Il valore di questa costante è il risultato della divisione di un numero positivo {o negativo} per zero.

2

Poiché il punto di virgola mobile "numerico" non è nulla del genere. Operazioni in virgola mobile:

  • non sono associative
  • non sono distributiva
  • non possono avere un inverso moltiplicativo

(vedi http://www.cs.uiuc.edu/class/fa07/cs498mjg/notes/floating-point.pdf per alcuni esempi)

La virgola mobile è un costruisci per risolvere un problema specifico e si abitua dappertutto quando non dovrebbe essere. Penso che siano piuttosto orribili, ma questo è soggettivo.