Operatori aritmetici (Riferimenti per C#)

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

Questi operatori sono supportati da tutti i tipi numerici integrali e a virgola mobile.

Nel caso dei tipi integrali, tali operatori (ad eccezione degli operatori ++ e --) vengono definiti per i tipi int, uint, long e ulong. Quando gli operandi sono di altri tipi integrali (sbyte, byte, short, ushort o char), i relativi valori vengono convertiti nel tipo int, che è anche il tipo di risultato di un'operazione. Quando gli operandi sono di 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.

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 è supportato in due forme: l'operatore di decremento suffisso, 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 tipo Integer, il risultato dell'operatore / è di tipo Integer ed è uguale al quoziente dei due operandi arrotondato allo 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 tipo float, double,o 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 tipi float, double e decimal, il risultato dell'operatore / è 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 uno degli operandi è decimal, un altro operando non può essere né floatdouble, perché né floatdouble è convertibile implicitamente 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 interi, il risultato di a % b è il valore prodotto da a - (a / b) * b. Il segno del resto diverso da zero è uguale al segno dell'operando sinistro, 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, è uguale a quello di x.
  • Il valore assoluto di z è il valore prodotto da |x| - n * |y| in cui n è l'intero più grande possibile, che è minore o uguale a |x| / |y| e |x| e |y| sono i valori assoluti di x e y, rispettivamente.

Nota

Questo metodo di calcolo del resto è analogo a quello usato per gli operandi integer, ma diverso 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 gli operandi decimal, l'operatore di resto % equivale all'operatore di resto di tipo System.Decimal.

L'esempio seguente illustra 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 la rimozione dei delegati. Per altre informazioni, vedere l'articolo sugli 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

con la differenza 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

In ragione delle 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

Nell'elenco seguente gli operatori aritmetici sono ordinati dalla precedenza più elevata a quella più bassa:

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

Gli operatori aritmetici binari prevedono l'associazione all'operando sinistro. Ciò significa che gli operatori con lo stesso livello di precedenza vengono valutati 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.

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 con i tipi float e double non generano mai un'eccezione. Il risultato delle operazioni aritmetiche con questi tipi può essere uno dei valori speciali che rappresentano un numero 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 delle limitazioni generali della rappresentazione a virgola mobile dei numeri reali e dell'aritmetica a virgola mobile, possono verificarsi errori di arrotondamento nei calcoli con tipi a virgola mobile. Ossia, il risultato prodotto di un'espressione può essere diverso 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

Un tipo definito dall'utente può eseguire l'overload degli operatori aritmetici unari (++, --, + e -) e binari (*, /, %, + e -). Quando viene eseguito l'overload di un operatore binario, viene anche eseguito in modo implicito l'overload dell'operatore di assegnazione composta corrispondente. Un tipo definito dall'utente non può eseguire in modo esplicito l'overload di un operatore di assegnazione composta.

Operatori controllati definiti dall'utente

A partire da C# 11, quando si esegue l'overload di un operatore aritmetico, è possibile usare la parola chiave checked 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. L'operatore controllato viene chiamato in un contesto controllato; l'operatore senza il modificatore checked viene chiamato in un contesto deselezionato. Se si specifica solo l'operatore senza il modificatore checked, viene chiamato sia in un contesto checked che in un contesto unchecked.

Quando si definiscono entrambe le versioni di un operatore, è previsto che il comportamento sia 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:

Nota

Il contesto di controllo dell'overflow all'interno del corpo di un operatore controllato non è influenzato dalla presenza del modificatore checked. 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