Condividi tramite


Operatori aritmetici (Riferimenti per C#)

Gli operatori seguenti eseguono operazioni aritmetiche con operandi di tipi numerici:

Tutti i tipi numerici integrali e a virgola mobile supportano questi operatori.

I inttipi , uintlong, e ulong definiscono tutti questi operatori. Gli altri tipi integrali (sbyte, byte, short, ushort, e char) definiscono solo gli operatori ++ e --. Per gli altri operatori, se si usano i tipi sbyteintegrali , , byteshort, ushorto char come operandi, i valori vengono convertiti nel int tipo e il tipo di risultato è int. Se gli operandi sono tipi integrali o a virgola mobile diversi, i relativi valori vengono convertiti nel tipo contenitore più vicino, se tale tipo esiste. Per altre informazioni, vedere la sezione Promozioni numeriche della specifica del linguaggio C#. Gli operatori ++ e -- sono definiti per tutti i tipi numerici integrali e a virgola mobile e il tipo char. Il tipo di risultato di un'espressione di assegnazione composta è il tipo dell'operando di sinistra.

Il riferimento al linguaggio C# documenta la versione rilasciata più di recente del linguaggio C#. Contiene anche la documentazione iniziale per le funzionalità nelle versioni di anteprima pubblica per la prossima versione del linguaggio di programmazione.

La documentazione identifica tutte le funzionalità introdotte nelle ultime tre versioni della lingua o nelle anteprime pubbliche correnti.

Suggerimento

Per trovare quando una funzionalità è stata introdotta per la prima volta in C#, vedere l'articolo sulla cronologia delle versioni del linguaggio C#.

Operatore di incremento ++

L'operatore di incremento unario ++ incrementa il suo operando di 1. L'operando deve essere una variabile, un accesso a una proprietà o un accesso a un indicizzatore.

L'operatore di incremento è supportato in due forme: l'operatore di incremento suffisso, x++, e l'operatore di incremento prefisso, ++x.

Operatore di incremento suffisso

Il risultato di x++ è il valore di xprima dell'operazione, come illustrato nell'esempio seguente:

int i = 3;
Console.WriteLine(i);   // output: 3
Console.WriteLine(i++); // output: 3
Console.WriteLine(i);   // output: 4

Operatore di incremento prefisso

Il risultato di ++x è il valore di xdopo l'operazione, come illustrato nell'esempio seguente:

double a = 1.5;
Console.WriteLine(a);   // output: 1.5
Console.WriteLine(++a); // output: 2.5
Console.WriteLine(a);   // output: 2.5

Operatore di decremento --

L'operatore di decremento unario -- decrementa il proprio operando di 1. L'operando deve essere una variabile, un accesso a una proprietà o un accesso a un indicizzatore.

L'operatore di decremento è disponibile in due forme: l'operatore di decremento postfix, x--e l'operatore di decremento prefisso, --x.

Operatore di decremento suffisso

Il risultato di x-- è il valore di xprima dell'operazione, come illustrato nell'esempio seguente:

int i = 3;
Console.WriteLine(i);   // output: 3
Console.WriteLine(i--); // output: 3
Console.WriteLine(i);   // output: 2

Operatore di decremento prefisso

Il risultato di --x è il valore di xdopo l'operazione, come illustrato nell'esempio seguente:

double a = 1.5;
Console.WriteLine(a);   // output: 1.5
Console.WriteLine(--a); // output: 0.5
Console.WriteLine(a);   // output: 0.5

Operatori più e meno unari

L'operatore + unario restituisce il valore del proprio operando. L'operatore - unario calcola la negazione numerica del proprio operando.

Console.WriteLine(+4);     // output: 4

Console.WriteLine(-4);     // output: -4
Console.WriteLine(-(-4));  // output: 4

uint a = 5;
var b = -a;
Console.WriteLine(b);            // output: -5
Console.WriteLine(b.GetType());  // output: System.Int64

Console.WriteLine(-double.NaN);  // output: NaN

Il tipo di ulong non supporta l'operatore - unario.

Operatore di moltiplicazione *

L'operatore di moltiplicazione * calcola il prodotto degli operandi:

Console.WriteLine(5 * 2);         // output: 10
Console.WriteLine(0.5 * 2.5);     // output: 1.25
Console.WriteLine(0.1m * 23.4m);  // output: 2.34

L'operatore * unario è l'operatore di riferimento indiretto a puntatore.

Operatore di divisione /

L'operatore di divisione / divide l'operando di sinistra per l'operando di destra.

Divisione di interi

Per gli operandi di tipi integer, il risultato dell'operatore / è di un tipo integer ed è uguale al quoziente dei due operandi arrotondati verso zero:

Console.WriteLine(13 / 5);    // output: 2
Console.WriteLine(-13 / 5);   // output: -2
Console.WriteLine(13 / -5);   // output: -2
Console.WriteLine(-13 / -5);  // output: 2

Per ottenere il quoziente dei due operandi come numero a virgola mobile, usare il floattipo , doubleo decimal :

Console.WriteLine(13 / 5.0);       // output: 2.6

int a = 13;
int b = 5;
Console.WriteLine((double)a / b);  // output: 2.6

Divisione a virgola mobile

Per i floattipi , doublee decimal , l'operatore / restituisce il quoziente dei due operandi:

Console.WriteLine(16.8f / 4.1f);   // output: 4.097561
Console.WriteLine(16.8d / 4.1d);   // output: 4.09756097560976
Console.WriteLine(16.8m / 4.1m);   // output: 4.0975609756097560975609756098

Se un operando è decimal, l'altro operando non può essere float o double, perché non float ha né double una conversione implicita in decimal. È necessario convertire esplicitamente l'operando float o double nel tipo decimal. Per altre informazioni sulle conversioni tra tipi numerici, vedere Conversioni numeriche predefinite.

Operatore di resto %

L'operatore di resto % calcola il resto dopo aver diviso l'operando di sinistra per l'operando di destra.

Resto intero

Per gli operandi di tipi integer, il risultato di a % b è il valore prodotto da $a - \frac{a}{b} \times b$. Il segno del resto diverso da zero corrisponde al segno dell'operando di sinistra, come illustrato nell'esempio seguente:

Console.WriteLine(5 % 4);   // output: 1
Console.WriteLine(5 % -4);  // output: 1
Console.WriteLine(-5 % 4);  // output: -1
Console.WriteLine(-5 % -4); // output: -1

Usare il metodo Math.DivRem per calcolare sia la divisione di numeri interi, sia i risultati del resto.

Resto a virgola mobile

Per gli operandi float e double, il risultato di x % y per x e y finiti è il valore z tale che

  • Il segno di z, se diverso da zero, corrisponde al segno di x.
  • Il valore assoluto di z deriva dal calcolo $|x| - n \times |y|$, dove n è il numero intero più grande minore o uguale a $\frac{|x|}{|y|}$. In questo caso, $|x|$ e $|y|$ rappresentano rispettivamente i valori assoluti di x e y.

Nota

Questo metodo di calcolo del resto è simile al metodo usato per gli operandi integer, ma differisce dalla specifica IEEE 754. Se è necessaria l'operazione di resto conforme alla specifica IEEE 754, usare il metodo Math.IEEERemainder.

Per informazioni sul comportamento dell'operatore % in caso di operandi non finiti, vedere la sezione Operatore di resto della specifica del linguaggio C#.

Per decimal gli operandi, l'operatore % di resto funziona come l'operatore di resto del System.Decimal tipo.

Nell'esempio seguente viene illustrato il comportamento dell'operatore di resto con operandi a virgola mobile:

Console.WriteLine(-5.2f % 2.0f); // output: -1.2
Console.WriteLine(5.9 % 3.1);    // output: 2.8
Console.WriteLine(5.9m % 3.1m);  // output: 2.8

Operatore di addizione +

L'operatore di addizione + calcola la somma degli operandi:

Console.WriteLine(5 + 4);       // output: 9
Console.WriteLine(5 + 4.3);     // output: 9.3
Console.WriteLine(5.1m + 4.2m); // output: 9.3

È anche possibile usare l'operatore + per la concatenazione di stringhe e la combinazione di delegati. Per altre informazioni, vedere l'articolo sugli operatori + e +=.

Operatore di sottrazione -

L'operatore - di sottrazione sottrae l'operando di destra dall'operando di sinistra:

Console.WriteLine(47 - 3);      // output: 44
Console.WriteLine(5 - 4.3);     // output: 0.7
Console.WriteLine(7.5m - 2.3m); // output: 5.2

È anche possibile usare l'operatore - per rimuovere un delegato. Per altre informazioni, vedere gli - operatori e -=.

Assegnazione composta

Per un operatore binario op, un'espressione di assegnazione composta in formato

x op= y

Equivale a

x = x op y

Ad eccezione del fatto che x viene valutato una sola volta.

L'esempio seguente illustra l'uso dell'assegnazione composta con gli operatori aritmetici:

int a = 5;
a += 9;
Console.WriteLine(a);  // output: 14

a -= 4;
Console.WriteLine(a);  // output: 10

a *= 2;
Console.WriteLine(a);  // output: 20

a /= 4;
Console.WriteLine(a);  // output: 5

a %= 3;
Console.WriteLine(a);  // output: 2

A causa di promozioni numeriche, il risultato dell'operazione op potrebbe non essere convertibile in modo implicito nel tipo T di x. In questo caso, se op è un operatore già definito e il risultato dell'operazione è convertibile in modo esplicito nel tipo T di x, un'espressione di assegnazione composta nel formato x op= y equivale a x = (T)(x op y), con la differenza che x viene valutato una sola volta. L'esempio seguente illustra questo comportamento:

byte a = 200;
byte b = 100;

var c = a + b;
Console.WriteLine(c.GetType());  // output: System.Int32
Console.WriteLine(c);  // output: 300

a += b;
Console.WriteLine(a);  // output: 44

Nell'esempio precedente, il valore 44 è il risultato della conversione del valore 300 nel tipo byte.

Nota

Nel contesto di controllo dell'overflow controllato, l'esempio precedente genera un OverflowException. Per altre informazioni, vedere la sezione Overflow aritmetico integer.

Si usano anche gli operatori += e -= per sottoscrivere e annullare la sottoscrizione a un evento rispettivamente. Per altre informazioni, vedere Come iscriversi agli eventi e annullare l'iscrizione.

Precedenza e associatività degli operatori

L'elenco seguente ordina gli operatori aritmetici dalla precedenza più alta alla precedenza più bassa:

  • Operatori di incremento x++ e decremento x-- in forma suffissa
  • Operatori di incremento ++x e decremento --x del prefisso e operatori unari + e -
  • Operatori moltiplicativi *, / e %
  • Operatori di addizione + e -

Gli operatori aritmetici binari prevedono l'associazione all'operando sinistro. Ovvero, il compilatore valuta gli operatori con lo stesso livello di precedenza da sinistra a destra.

Usare le parentesi, (), per cambiare l'ordine di valutazione imposto dalla precedenza e dall'associatività degli operatori.

Console.WriteLine(2 + 2 * 2);   // output: 6
Console.WriteLine((2 + 2) * 2); // output: 8

Console.WriteLine(9 / 5 / 2);   // output: 0
Console.WriteLine(9 / (5 / 2)); // output: 4

Per l'elenco completo degli operatori C# ordinati per livello di priorità, vedere la sezione Priorità degli operatori nell'articolo Operatori C#.

Overflow aritmetico e divisione per zero

Quando il risultato di un'operazione aritmetica non rientra nell'intervallo di valori finiti possibili del tipo numerico interessato, il comportamento di un operatore aritmetico dipende dal tipo dei relativi operandi.

Overflow aritmetico di numeri interi

La divisione di interi per zero genera sempre un'eccezione DivideByZeroException.

Se si verifica un overflow aritmetico integer, il contesto di controllo dell'overflow, che può essere controllato o deselezionato, controlla il comportamento risultante:

  • In un contesto verificato, se l'overflow si verifica in un'espressione costante, verrà restituito un errore in fase di compilazione. Quando invece l'operazione avviene in fase di esecuzione, viene generata una OverflowException.
  • In un contesto non verificato, il risultato viene troncato tramite la rimozione dei bit più significativi che non rientrano nel tipo di destinazione.

Nota

La divisione integer ha un caso speciale in cui un ArithmeticException oggetto può essere generato anche in un contesto non selezionato. Quando l'operando sinistro è il valore minimo di un tipo integer con segno (int.MinValue o long.MinValue) e l'operando destro è -1, il risultato non può essere rappresentato nel tipo di destinazione. Il runtime .NET genera un'eccezione ArithmeticException in questo caso, come illustrato nell'esempio seguente:

int a = int.MinValue;
int b = -1;
try
{
    int c = unchecked(a / b);
}
catch (ArithmeticException)
{
    Console.WriteLine($"Overflow occurred when dividing {a} by {b}.");
}

Oltre alle istruzioni controllate e deselezionate, è possibile usare gli operatori checked e unchecked per controllare il contesto di controllo dell'overflow, in cui viene valutata un'espressione:

int a = int.MaxValue;
int b = 3;

Console.WriteLine(unchecked(a + b));  // output: -2147483646
try
{
    int d = checked(a + b);
}
catch(OverflowException)
{
    Console.WriteLine($"Overflow occurred when adding {a} to {b}.");
}

Per impostazione predefinita, le operazioni aritmetiche vengono eseguite in un contesto non verificato.

Overflow aritmetico di numeri a virgola mobile

Le operazioni aritmetiche usando i float tipi e double non generano mai un'eccezione. Il risultato di operazioni aritmetiche che usano questi tipi può essere uno dei valori speciali che rappresentano l'infinito e non un numero:

double a = 1.0 / 0.0;
Console.WriteLine(a);                    // output: Infinity
Console.WriteLine(double.IsInfinity(a)); // output: True

Console.WriteLine(double.MaxValue + double.MaxValue); // output: Infinity

double b = 0.0 / 0.0;
Console.WriteLine(b);                // output: NaN
Console.WriteLine(double.IsNaN(b));  // output: True

Per gli operandi del tipo decimal, l'overflow aritmetico genera sempre un OverflowException. La divisione per zero genera sempre un DivideByZeroException.

Errori di arrotondamento

A causa di limitazioni generali nella rappresentazione a virgola mobile di numeri reali e aritmetici a virgola mobile, gli errori di arrotondamento possono verificarsi nei calcoli che usano tipi a virgola mobile. Il risultato di un'espressione potrebbe differire dal risultato matematico previsto. L'esempio seguente illustra molti di questi casi:

Console.WriteLine(.41f % .2f); // output: 0.00999999

double a = 0.1;
double b = 3 * a;
Console.WriteLine(b == 0.3);   // output: False
Console.WriteLine(b - 0.3);    // output: 5.55111512312578E-17

decimal c = 1 / 3.0m;
decimal d = 3 * c;
Console.WriteLine(d == 1.0m);  // output: False
Console.WriteLine(d);          // output: 0.9999999999999999999999999999

Per altre informazioni, vedere le osservazioni nelle pagine di riferimento System.Double, System.Single o System.Decimal .

Overload degli operatori

È possibile eseguire l'overload degli operatori aritmetici unari (++, --, +, e -) e binari (*, +/%, , e -) unario per un tipo definito dall'utente. Quando si esegue l'overload di un operatore binario, si esegue anche l'overload implicito dell'operatore di assegnazione composta corrispondente. A partire da C# 14, un tipo definito dall'utente può eseguire in modo esplicito l'overload degli operatori di assegnazione composta (op=) per fornire un'implementazione più efficiente. In genere, un tipo sovraccarica questi operatori perché il valore può essere aggiornato direttamente, anziché allocare una nuova istanza per archiviare il risultato dell'operazione. Se un tipo non fornisce un overload esplicito, il compilatore genera l'overload implicito.

Operatori controllati definiti dall'utente

Quando si esegue l'overload di un operatore aritmetico, è possibile usare la checked parola chiave per definire la versione controllata di tale operatore. L'esempio seguente illustra come eseguire questa operazione:

public record struct Point(int X, int Y)
{
    public static Point operator checked +(Point left, Point right)
    {
        checked
        {
            return new Point(left.X + right.X, left.Y + right.Y);
        }
    }
    
    public static Point operator +(Point left, Point right)
    {
        return new Point(left.X + right.X, left.Y + right.Y);
    }
}

Quando si definisce un operatore controllato, è necessario definire anche l'operatore corrispondente senza il modificatore checked. Un contesto controllato chiama l'operatore controllato e un contesto deselezionato chiama l'operatore senza il checked modificatore.

Quando si definiscono entrambe le versioni di un operatore, il comportamento è diverso solo quando il risultato di un'operazione è troppo grande per rappresentare nel tipo di risultato come indicato di seguito:

  • Un operatore controllato genera un'eccezione OverflowException.
  • Un operatore senza il modificatore checked restituisce un'istanza che rappresenta un risultato troncato.

Per informazioni sulla differenza di comportamento degli operatori aritmetici predefiniti, vedere la sezione overflow aritmetico e divisione per zero.

È possibile usare il modificatore checked solo quando si esegue l'overload di uno degli operatori seguenti:

  • Operatori unari ++, -- e -
  • Operatori binari *, /, +, e -
  • Operatori di assegnazione composta *=, /=, +=, e -= (C# 14 e versioni successive)
  • Operatori di conversione espliciti

Nota

Il checked modificatore non influisce sul contesto di controllo dell'overflow all'interno del corpo. Il contesto predefinito è definito dal valore dell'opzione del compilatore CheckForOverflowUnderflow. Utilizzare le istruzioni checked e unchecked per specificare in modo esplicito il contesto di controllo dell'overflow, come illustrato nell'esempio all'inizio di questa sezione.

Specifiche del linguaggio C#

Per altre informazioni, vedere le sezioni seguenti delle specifiche del linguaggio C#:

Vedi anche