Operadores aritméticos (referência do C#)

Os seguintes operadores executam operações aritméticas com operandos de tipos numéricos:

Esses operadores são suportados por todos os tipos numéricos integrais e de ponto flutuante.

No caso de tipos integrais, esses operadores (exceto os operadores ++ e --) são definidos para os tipos int, uint, long e ulong. Quando os operandos são de outros tipos integrais (sbyte, byte, short, ushort ou char), seus valores são convertidos no tipo int, que também é o tipo de resultado de uma operação. Quando operandos são de tipos diferentes de ponto flutuante ou integral, seus valores são convertidos para o tipo recipiente mais próximo, se esse tipo existir. Para saber mais, confira a seção Promoções numéricas da Especificação da linguagem C#. Os operadores ++ e -- são definidos para todos os tipos numéricos de ponto integral e flutuante e para o tipo char. O tipo de resultado de uma expressão de atribuição composta é o tipo do operando à esquerda.

Operador de incremento ++

O operador de incremento unário ++ incrementa seu operando em 1. O operando precisa ser uma variável, um acesso de propriedade ou um acesso de indexador.

Há duas formas de suporte para o operador de incremento: o operador de incremento pós-fixado, x++, e o operador de incremento pré-fixado, ++x.

Operador de incremento pós-fixado

O resultado de x++ é o valor de xantes da operação, como mostra o exemplo a seguir:

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

Operador de incremento de prefixo

O resultado de ++x é o valor de xapós a operação, como mostra o exemplo a seguir:

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

Operador de decremento --

O operador de decremento unário -- decrementa o operando em 1. O operando precisa ser uma variável, um acesso de propriedade ou um acesso de indexador.

Há duas formas de suporte para o operador de decremento: o operador de decremento pós-fixado, x--, e o operador de decremento pré-fixado, --x.

Operador de decremento pós-fixado

O resultado de x-- é o valor de xantes da operação, como mostra o exemplo a seguir:

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

Operador de decremento de prefixo

O resultado de --x é o valor de xapós a operação, como mostra o exemplo a seguir:

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

Operadores unários de adição e subtração

O operador unário + retorna o valor do operando. O operador unário - calcula a negação numérica do 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

O tipo ulong não dá suporte ao operador unário -.

Operador de multiplicação *

O operador de multiplicação * calcula o produto dos operandos:

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

O operador unário * é o operador de indireção de ponteiro.

Operador de divisão /

O operador de divisão / divide o operando à esquerda pelo operando à direita.

Divisão de inteiros

Para os operandos de tipos inteiros, o resultado do operador / é de um tipo inteiro e igual ao quociente dos dois operandos arredondados para 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

Para obter o quociente dos dois operandos como um número de ponto flutuante, use o tipo float, double ou decimal:

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

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

Divisão de ponto flutuante

Para os tipos float, double e decimal, o resultado do operador / é o quociente dos dois operandos:

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 um dos operandos é decimal, outro operando não pode ser float nem double, porque nem float ou double é implicitamente conversível para decimal. Você deve converter explicitamente o operando float ou double para o tipo decimal. Para saber mais sobre as conversões entre tipos numéricos, confira a as Conversões numéricas internas.

Operador de resto %

O operador de resto % calcula o resto após dividir o operando à esquerda pelo à direita.

Resto inteiro

Para os operandos de tipos inteiros, o resultado de a % b é o valor produzido por a - (a / b) * b. O sinal do resto diferente de zero é o mesmo do operando à esquerda, conforme mostra o seguinte exemplo:

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

Use o método Math.DivRem para calcular a divisão de inteiros e os resultados do resto.

Resto de ponto flutuante

Para os operandos float e double, o resultado de x % y para o x e o y finitos é o valor z tal que

  • O sinal de z, se diferente de zero, é o mesmo que o sinal de x.
  • O valor absoluto de z é o valor produzido por |x| - n * |y|, em que n é o maior inteiro possível que é inferior ou igual a |x| / |y|, e |x| e |y| são os valores absolutos de x e y, respectivamente.

Observação

Esse método de calcular o resto é semelhante ao usado para operandos inteiros, mas é diferente da especificação IEEE 754. Se precisar que a operação de resto esteja em conformidade com a especificação IEEE 754, use o método Math.IEEERemainder.

Para saber mais sobre o comportamento do operador % com operandos não finitos, confira a seção Operador de restante da especificação da linguagem C#.

Para os operandos decimal, o operador de resto % é equivalente ao operador de resto do tipo System.Decimal.

O seguinte exemplo demonstra o comportamento do operador de resto com os operandos de ponto flutuante:

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

Operador de adição +

O operador de adição + calcula a soma dos operandos:

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

Você também pode usar o operador + para a combinação de delegado e concatenação de cadeia de caracteres. Para obter mais informações, confira o artigo Operadores + e +=.

Operador de subtração -

O operador de subtração - subtrai o operando à direita do operando à esquerda:

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

Você também pode usar o operador - para a remoção de delegado. Para obter mais informações, confira o artigo Operadores - e -=.

Atribuição composta

Para um operador binário op, uma expressão de atribuição composta do formato

x op= y

é equivalente a

x = x op y

exceto que x é avaliado apenas uma vez.

O seguinte exemplo demonstra o uso da atribuição composta com operadores aritméticos:

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

Devido a promoções numéricas, o resultado da operação op pode não ser implicitamente conversível no tipo T de x. Nesse caso, se op for um operador predefinido e o resultado da operação for explicitamente convertido no tipo T de x, uma expressão de atribuição composta da forma x op= y será equivalente a x = (T)(x op y), exceto que x será avaliada apenas uma vez. O exemplo a seguir demonstra esse 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

No exemplo anterior, o valor 44 é o resultado da conversão do valor 300 para o tipo byte.

Observação

No contexto de verificação de estouro verificado, o exemplo anterior lança um OverflowException. Para obter mais informações, confira a seção Estouro aritmético inteiro.

Use também os operadores += e -= para assinar e cancelar a assinatura de eventos, respectivamente. Para obter mais informações, confira Como assinar e cancelar a assinatura de eventos.

Precedência e associatividade do operador

A seguinte lista ordena os operadores aritméticos da precedência mais alta para a mais baixa:

  • Incluir um pós-fixo a operadores de incremento x++ e decremento x--
  • Incluir um prefixo a operadores de incremento ++x e de decremento --x e operadores unários + e -
  • Operadores de multiplicação *, / e %
  • Operadores de adição + e -

Operadores aritméticos binários são associativos à esquerda. Ou seja, os operadores com o mesmo nível de precedência são avaliados da esquerda para a direita.

Use parênteses, (), para alterar a ordem de avaliação imposta pela precedência e pela capacidade de associação do operador.

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

Para obter a lista completa de operadores C# ordenados por nível de precedência, consulte a seção Precedência de operador do artigo Operadores C#.

Estouro aritmético e divisão por zero

Quando o resultado de uma operação aritmética está fora do intervalo de valores finitos possíveis do tipo numérico envolvido, o comportamento de um operador aritmético depende do tipo dos operandos.

Estouro aritmético de inteiros

A divisão de inteiro por zero sempre lança um DivideByZeroException.

Se ocorrer um estouro aritmético inteiro, o contexto de verificação de estouro, que pode ser checked ou unchecked, controlará o comportamento resultante:

  • Em um contexto verificado, se o estouro acontece em uma expressão de constante, ocorre um erro em tempo de compilação. Caso contrário, quando a operação é executada em tempo de execução, uma OverflowException é gerada.
  • Em um contexto não verificado, o resultado é truncado pelo descarte dos bits de ordem superior que não se ajustam ao tipo de destino.

Juntamente com as instruções checked e unchecked, você pode usar os operadores checked e unchecked para controlar o contexto de verificação de estouro, no qual uma expressão é avaliada:

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}.");
}

Por padrão, as operações aritméticas ocorrem em um contexto não verificado.

Estouro aritmético de ponto flutuante

As operações aritméticas com os tipos float e double nunca geram uma exceção. O resultado de operações aritméticas com esses tipos pode ser um dos valores especiais que representam o infinito e um valor não é um número:

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

Para os operandos do tipo decimal, o estouro aritmético sempre gera uma OverflowException. A divisão por zero sempre lança uma DivideByZeroException.

Erros de arredondamento

Devido às limitações gerais de uma representação de ponto flutuante de números reais e da aritmética de ponto flutuante, os erros de arredondamento podem ocorrer em cálculos com tipos de ponto flutuante. Ou seja, o resultado produzido de uma expressão pode diferir do resultado matemático esperado. O seguinte exemplo demonstra vários casos desse tipo:

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

Para obter mais informações, confira os comentários nas páginas de referência System.Double, System.Single ou System.Decimal.

Capacidade de sobrecarga do operador

Um tipo definido pelo usuário pode sobrecarregar os operadores aritméticos unários (++, --, + e -) e binários (*, /, %, + e -). Quando um operador binário está sobrecarregado, o operador de atribuição composta correspondente também é implicitamente sobrecarregado. Um tipo definido pelo usuário não pode sobrecarregar explicitamente um operador de atribuição composta.

Operadores verificados definidos pelo usuário

A partir do C# 11, quando você sobrecarrega um operador aritmético, pode usar a checked palavra-chave para definir a versão marcada desse operador. O seguinte exemplo mostra como fazer isso:

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);
    }
}

Ao definir um operador verificado, você também deve definir o operador correspondente sem o modificador checked. O operador verificado é chamado em um contexto verificado; o operador sem o modificador checked é chamado em um contexto não verificado. Se você fornecer apenas o operador sem o modificador checked, ele será chamado em um contexto checked e unchecked.

Quando você define ambas as versões de um operador, espera-se que o comportamento delas seja diferente somente quando o resultado de uma operação for muito grande para representar no tipo de resultado da seguinte maneira:

  • Um operador verificado lança um OverflowException.
  • Um operador sem o modificador checked retornará uma instância que representa um resultado truncado.

Para obter informações sobre a diferença no comportamento dos operadores aritméticos internos, consulte a seção Estouro aritmético e divisão por zero.

Você só poderá usar o checked modificador quando sobrecarregar qualquer um dos seguintes operadores:

Observação

O contexto de verificação de estouro dentro do corpo de um operador verificado não é afetado pela presença do modificador checked. O contexto padrão é definido pelo valor da opção do compilador CheckForOverflowUnderflow. Use as instruções checked e unchecked para especificar explicitamente o contexto de verificação de estouro, como demonstra o exemplo no início desta seção.

Especificação da linguagem C#

Para obter mais informações, confira as seguintes seções da especificação da linguagem C#:

Confira também