Perché la divisione intera in C # restituisce un numero intero e non un float?


131

Qualcuno sa perché la divisione intera in C # restituisce un numero intero e non un float? Qual è l'idea alla base? (È solo un'eredità di C / C ++?)

In C #:

float x = 13 / 4;   
//== operator is overridden here to use epsilon compare
if (x == 3.0)
   print 'Hello world';

Il risultato di questo codice sarebbe:

'Hello world'

A rigor di termini, non esiste una divisione intera (la divisione per definizione è un'operazione che produce un numero razionale, i cui numeri interi sono un sottoinsieme molto piccolo.)


46
perché è integerdivisione non floating pointdivisione.
Hunter McMillen,

deve (in VB.Net) essere implementato diversamente in modo matematico naturale dove tutto il risultato dell'operazione di divisione è un numero irrazionale.
BanditoBunny,

3
Penso che intendi numeri razionali . Vedi Wikipedia : la divisione di due numeri interi può comportare un resto. Per completare la divisione del resto, il sistema numerico viene esteso per includere frazioni o numeri razionali come vengono più comunemente chiamati.
crashmstr,

6
Questo è il motivo per cui non sono un fan della "copia della sintassi" nelle lingue. Vengo da VB pensando "C # è .NET" e non "C # è come C". Il mio errore immagino, ma in questo caso preferisco il modo VB. Se hanno avuto il problema di generare un errore del compilatore quando si utilizzavano tipi semplici non inizializzati (non si riceve nemmeno un avviso in C), perché non avvisarti quando si assegna la divisione di numeri interi a un float?
darda,

Risposte:


96

Mentre è comune per il nuovo programmatore fare questo errore nell'eseguire la divisione di interi quando in realtà intendevano usare la divisione in virgola mobile, nella pratica la divisione di interi è un'operazione molto comune. Se stai assumendo che le persone lo usino raramente e che ogni volta che fai divisione devi sempre ricordarti di lanciare in virgola mobile, ti sbagli.

Prima di tutto, la divisione intera è un po 'più veloce, quindi se hai bisogno solo di un numero intero, vorrai utilizzare l'algoritmo più efficiente.

In secondo luogo, ci sono un certo numero di algoritmi che utilizzano la divisione di numeri interi e se il risultato della divisione era sempre un numero in virgola mobile, si sarebbe costretti a arrotondare il risultato ogni volta. Un esempio fuori dalla cima della mia testa sta cambiando la base di un numero. Il calcolo di ogni cifra implica la divisione intera di un numero insieme al resto, anziché la divisione in virgola mobile del numero.

A causa di questi (e altri motivi correlati), la divisione dei numeri interi risulta in un numero intero. Se vuoi ottenere la divisione in virgola mobile di due numeri interi, dovrai solo ricordarti di lanciarne uno in double/ float/ decimal.


5
In VB.Net .Net architects ha preso un'altra decisione: / - sempre una divisione float, \ - divisione intera, quindi è in qualche modo incoerente, tranne se si considera l'eredità C ++;
BanditoBunny,

4
È possibile determinare, al momento della compilazione, se l' /operatore eseguirà la divisione di numeri interi o in virgola mobile (a meno che non si stia utilizzando la dinamica). Se è difficile per te capire perché stai facendo così tanto su quella linea, ti suggerirei di suddividere quella linea in più linee in modo che sia più facile capire se gli operandi sono numeri interi o in virgola mobile. I futuri lettori del tuo codice probabilmente lo apprezzeranno.
Servito il

5
Personalmente trovo problematico il fatto che devo sempre pensare a quali variabili sto dividendo, lo considero uno spreco di attenzione.
BanditoBunny,

8
@pelesl Dato che sarebbe un enorme cambiamento per fare ciò per il quale un numero astronomico di programmi sarebbe rotto, posso dire con assoluta sicurezza che non accadrà mai in C #. Questo è il tipo di cosa che deve essere fatto dal primo giorno in una lingua o per niente.
Servito il

2
@Servy: ci sono molte cose del genere in C, C ++ e C #. Personalmente, penso che C # sarebbe un linguaggio migliore se ci fosse stato un operatore diverso per la divisione di numeri interi e, per evitare che il codice legittimo producesse un comportamento sorprendente, l' int/intoperatore era semplicemente illegale [con una diagnostica che specifica che il codice deve trasmettere un operando o usare il altro operatore, a seconda del comportamento desiderato]. Se fossero disponibili altre sequenze di token valide per la divisione di numeri interi, potrebbe essere possibile deprecare l'utilizzo /a tale scopo, ma non so quale sarebbe pratico.
Supercat,

77

Vedi le specifiche C # . Esistono tre tipi di operatori di divisione

  • Divisione intera
  • Divisione in virgola mobile
  • Divisione decimale

Nel tuo caso abbiamo la divisione Integer, con le seguenti regole applicate:

La divisione arrotonda il risultato a zero e il valore assoluto del risultato è il numero intero più grande possibile che è inferiore al valore assoluto del quoziente dei due operandi. Il risultato è zero o positivo quando i due operandi hanno lo stesso segno e zero o negativo quando i due operandi hanno segni opposti.

Penso che il motivo per cui C # usi questo tipo di divisione per gli interi (alcune lingue restituiscono risultati fluttuanti) è l'hardware - la divisione degli interi è più veloce e più semplice.


Quali lingue restituiscono risultati fluttuanti? @SergeyBerezovskiy
Ilaria

40

Ogni tipo di dati è in grado di sovraccaricare ciascun operatore. Se sia il numeratore che il denominatore sono numeri interi, il tipo intero eseguirà l'operazione di divisione e restituirà un tipo intero. Se si desidera la divisione in virgola mobile, è necessario eseguire il cast di uno o più dei numeri in tipi in virgola mobile prima di dividerli. Per esempio:

int x = 13;
int y = 4;
float x = (float)y / (float)z;

oppure, se stai usando valori letterali:

float x = 13f / 4f;

Tieni presente che i punti fluttuanti non sono precisi. Se ti interessa la precisione, usa invece qualcosa come il tipo decimale.


1
+1 per menzionare che solo un termine deve essere float per effettuare la divisione in virgola mobile.
Xynariz,

Ovviamente la tua affermazione sulla precisione è la giusta nel contesto dell'apprendimento e nel renderla non complicata da capire. Dato che dobbiamo essere il più precisi possibile nei nostri lavori, voglio ancora chiarire la precisione: secondo IEE 754-1985 puoi ottenere un risultato esatto (anche se per lo più non è così). È possibile ottenere un risultato preciso, quando i valori di calcolo sono stati presentati esattamente prima e il risultato è - semplicemente parlando - una somma di poteri di 2. Anche se non può essere la migliore pratica fare affidamento su quella precisione in quei casi speciali.
L. Monty,

Aggiunta alla precisione: la propensione a ottenere un risultato esatto migliora drasticamente poiché il risultato è vicino a 1 o -1. Potrebbe essere un po 'confuso che questa propensione rimanga ancora 0 poiché ci sono numeri infiniti e un numero finito di risultati, che possono essere presentati esattamente. :)
L. Monty,

1
@ L.Monty grazie per averlo sollevato. Ho imparato di più sui punti fluttuanti da quando ho scritto questa risposta e il punto che stai facendo è giusto. Tecnicamente, direi comunque che la mia affermazione "i punti fluttuanti non sono precisi" è accettabile, nel senso che solo perché qualcosa può essere accurato a volte non significa che, nel suo insieme, sia preciso. Come si suol dire, un orologio rotto è giusto due volte al giorno, ma non lo definirei mai uno strumento di precisione. In realtà sono piuttosto sorpreso che sia la parte che ti ha infastidito più del mio suggerimento che il tipo decimale sia preciso.
Steven Doggart,

I decimali sono imprecisi, per tutte le stesse ragioni che sono i galleggianti; è solo che i float sono base-2 e decimali sono base-10. Ad esempio, un tipo decimale non può contenere con precisione il valore preciso di 1/3.
Steven Doggart,

11

Dato che non usi alcun suffisso, i letterali 13e 4vengono interpretati come numeri interi:

Manuale :

Se il letterale non ha suffisso, ha il primo di questi tipi in cui il suo valore può essere rappresentato: int, uint, long, ulong.

Pertanto, poiché si dichiara 13come intero, verrà eseguita la divisione dei numeri interi:

Manuale :

Per un'operazione nel formato x / y, viene applicata la risoluzione di sovraccarico dell'operatore binario per selezionare un'implementazione specifica dell'operatore. Gli operandi vengono convertiti nei tipi di parametri dell'operatore selezionato e il tipo del risultato è il tipo di ritorno dell'operatore.

Gli operatori di divisione predefiniti sono elencati di seguito. Tutti gli operatori calcolano il quoziente di xe y.

Divisione intera:

int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);

E così si verifica l'arrotondamento:

La divisione arrotonda il risultato a zero e il valore assoluto del risultato è il numero intero più grande possibile che è inferiore al valore assoluto del quoziente dei due operandi. Il risultato è zero o positivo quando i due operandi hanno lo stesso segno e zero o negativo quando i due operandi hanno segni opposti.

Se esegui le seguenti operazioni:

int x = 13f / 4f;

Riceverai un errore del compilatore, poiché una divisione in virgola mobile (l' /operatore di 13f) genera un float, che non può essere implicitamente lanciato.

Se vuoi che la divisione sia una divisione in virgola mobile, dovrai rendere il risultato un float:

float x = 13 / 4;

Si noti che continuerai a dividere numeri interi, che verranno implicitamente lanciati su float: il risultato sarà 3.0. Per dichiarare esplicitamente gli operandi come float, usando il fsuffisso ( 13f, 4f).


+1 per spiegare che è possibile avere la risposta come float ma fare comunque una divisione intera. Inoltre, un altro modo comune che ho visto per forzare la divisione in virgola mobile è moltiplicare il primo termine della divisione per 1.0.
Xynariz,

8

È solo un'operazione di base .

Ricorda quando hai imparato a dividere. All'inizio abbiamo risolto 9/6 = 1 with remainder 3.

9 / 6 == 1  //true
9 % 6 == 3 // true

L'operatore / in combinazione con l'operatore% viene utilizzato per recuperare quei valori.


6

Potrebbe essere utile:

double a = 5.0/2.0;   
Console.WriteLine (a);      // 2.5

double b = 5/2;   
Console.WriteLine (b);      // 2

int c = 5/2;   
Console.WriteLine (c);      // 2

double d = 5f/2f;   
Console.WriteLine (d);      // 2.5

Prova ad aggiungere alcune spiegazioni per la tua risposta
NetStarter

L'ultima espressione produrrà 2.5, no 2.
Lasse V. Karlsen,

Sì, errore di ortografia. Grazie.
eozten,

4

Il risultato sarà sempre di tipo con la gamma maggiore del numeratore e del denominatore. Le eccezioni sono byte e short, che producono int (Int32).

var a = (byte)5 / (byte)2;  // 2 (Int32)
var b = (short)5 / (byte)2; // 2 (Int32)
var c = 5 / 2;              // 2 (Int32)
var d = 5 / 2U;             // 2 (UInt32)
var e = 5L / 2U;            // 2 (Int64)
var f = 5L / 2UL;           // 2 (UInt64)
var g = 5F / 2UL;           // 2.5 (Single/float)
var h = 5F / 2D;            // 2.5 (Double)
var i = 5.0 / 2F;           // 2.5 (Double)
var j = 5M / 2;             // 2.5 (Decimal)
var k = 5M / 2F;            // Not allowed

Non esiste una conversione implicita tra i tipi a virgola mobile e il tipo decimale, quindi la divisione tra di essi non è consentita. Devi espressamente lanciare e decidere quale vuoi (Decimale ha più precisione e un intervallo più piccolo rispetto ai tipi a virgola mobile).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.