C# Matemática (Math)
Os operadores +, -, *, / e % (aula Operadores Aritméticos) fazem as contas do dia a dia. Mas o C# não tem um operador para raiz quadrada, nem para potência (o ^ do C# não é potência!), nem para arredondar. Essas contas ficam na classe Math ("matemática"), a calculadora científica do .NET: Math.Sqrt(81) dá 9, Math.Pow(2, 10) dá 1024, Math.Round(2.7) dá 3. Nesta aula você vai conhecer as funções mais usadas, com a saída real de cada uma, e duas surpresas: o arredondamento que transforma 2,5 em 2, e a raiz cúbica de 27 que não dá exatamente 3.
- As funções básicas:
Abs,Max,Min,Pow,Sqrt,Cbrte as constantesPIeE. - Os quatro jeitos de tirar as casas decimais:
Round,Floor,CeilingeTruncate. - Por que
Math.Round(2.5)dá 2, e como pedir o arredondamento "da escola". - Como arredondar dinheiro com
decimale casas decimais. - Como limitar um valor com
Clamp, e o sinal comSign. - Seno, cosseno e logaritmos, e por que
Math.Sin(30)não dá 0,5. - Quais funções aceitam
intedecimal, e quais sódouble. - Os resultados especiais (
NaN,∞) e os erros reaisCS0266,CS1503,CS1501eOverflowException.
Todos os programas foram rodados com o .NET 10 (dotnet --version = 10.0.112, C# 14) na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cs, com dotnet run arquivo.cs. O notebook está em português, por isso os números com casas decimais saem com vírgula (1,4142…).
A ideia: a calculadora científica
Uma calculadora comum tem as quatro operações. A científica tem uma fileira de teclas a mais: √, xʸ, π, sin, log… Você digita o número, aperta a tecla e lê o resultado.
A classe Math é essa fileira de teclas. Cada tecla é um método: você escreve Math., o nome da tecla e, entre parênteses, o número. O resultado volta para o seu programa, para guardar numa variável ou mostrar. Como a Array da aula A Classe Array, é uma classe estática: não precisa criar nada, é só usar.
1. As funções básicas
Console.WriteLine(Math.Abs(-7)); Console.WriteLine(Math.Max(12, 45)); Console.WriteLine(Math.Min(12, 45)); Console.WriteLine(Math.Pow(2, 10)); Console.WriteLine(Math.Sqrt(81)); Console.WriteLine(Math.Sqrt(2)); Console.WriteLine(Math.Cbrt(27)); Console.WriteLine(Math.PI); Console.WriteLine(Math.E);
$ dotnet run basico.cs 7 45 12 1024 9 1,4142135623730951 3,0000000000000004 « a raiz cúbica de 27 "quase" 3 3,141592653589793 2,718281828459045
| Linha | Nome (em inglês) | O que faz |
|---|---|---|
Math.Abs(-7) | absolute, valor absoluto | Tira o sinal: −7 vira 7, e 7 continua 7. Útil para "a diferença entre dois números", sem importar qual é maior: Math.Abs(a - b). |
Math.Max(12, 45) | maximum, máximo | O maior dos dois: 45. |
Math.Min(12, 45) | minimum, mínimo | O menor dos dois: 12. |
Math.Pow(2, 10) | power, potência | 2 elevado a 10: 2 × 2 × … × 2, dez vezes = 1024. O resultado é double. |
Math.Sqrt(81) | square root, raiz quadrada | O número que, vezes ele mesmo, dá 81: 9. |
Math.Sqrt(2) | A raiz de 2 não é um número "redondo": 1,4142135623730951 (as casas que cabem num double). | |
Math.Cbrt(27) | cube root, raiz cúbica | O número que, multiplicado três vezes, dá 27. Deveria ser 3, mas veio 3,0000000000000004. |
Math.PI | π | A constante π (pi), a volta do círculo dividida pelo diâmetro. Não tem parênteses: não é uma tecla, é um número guardado. |
Math.E | e | O número de Euler, base dos logaritmos naturais. |
O double guarda os números em binário, com um número limitado de casas, e a maioria das contas com ele tem um errinho na última casa (aula float, double e decimal). Na maior parte do tempo, o errinho "some" quando o número é mostrado. Na raiz cúbica de 27, no Linux, ele apareceu. No editor do guia, que roda o .NET em WebAssembly, o mesmo Math.Cbrt(27) mostra 3: as duas máquinas fazem a conta por caminhos um pouco diferentes, e as duas estão dentro do que o double promete. Por isso, nunca compare um resultado de Math com ==; arredonde antes (Math.Round(x, 6) == 3) ou confira se a diferença é minúscula (Math.Abs(x - 3) < 0.000001).
^ não é potência
Em muitas calculadoras e planilhas, 2^10 é "2 elevado a 10". Em C#, o ^ é outra coisa (o "ou exclusivo" de bits), e 2 ^ 10 dá 8, sem erro nenhum. Para potência, sempre Math.Pow(2, 10). Para elevar ao quadrado, x * x também serve, e é mais rápido.
Missão: num triângulo retângulo com lados 3 e 4, a hipotenusa (o lado maior) é a raiz quadrada de 3² + 4². O código já calcula 3² + 4². Complete com Math.Sqrt para mostrar Hipotenusa: 5.
Hipotenusa: 5
double a = 3, b = 4;
double c = Math.Pow(a, 2) + Math.Pow(b, 2); // falta a raiz
Console.WriteLine($"Hipotenusa: {c}");
double a = 3, b = 4;
double c = Math.Sqrt(Math.Pow(a, 2) + Math.Pow(b, 2));
Console.WriteLine($"Hipotenusa: {c}");
2. Tirar as casas decimais: Round, Floor, Ceiling, Truncate
São quatro jeitos diferentes de transformar 2,7 num número inteiro, e cada um serve para uma coisa:
double[] valores = [2.3, 2.5, 2.7, -2.5, 3.5];
foreach (double x in valores)
{
Console.WriteLine($"{x,5}: Round {Math.Round(x),3} Floor {Math.Floor(x),3} Ceiling {Math.Ceiling(x),3} Truncate {Math.Truncate(x),3}");
}
$ dotnet run arredondar.cs 2,3: Round 2 Floor 2 Ceiling 3 Truncate 2 2,5: Round 2 Floor 2 Ceiling 3 Truncate 2 2,7: Round 3 Floor 2 Ceiling 3 Truncate 2 -2,5: Round -2 Floor -3 Ceiling -2 Truncate -2 3,5: Round 4 Floor 3 Ceiling 4 Truncate 3
- Math.Round
- "Arredondar": vai para o inteiro mais perto. 2,3 → 2; 2,7 → 3. E no meio do caminho, o 2,5? Veja a próxima seção.
- Math.Floor
- "Chão": sempre para baixo, para o inteiro menor. 2,7 → 2. Com negativos, "para baixo" é para mais longe do zero: −2,5 → −3.
- Math.Ceiling
- "Teto": sempre para cima. 2,3 → 3. Bom para "quantas caixas eu preciso", em que sobrar um item já exige mais uma caixa.
- Math.Truncate
- "Truncar", cortar: joga fora as casas decimais, sem arredondar nada. 2,7 → 2; −2,5 → −2. É o que o
(int)faz (aula Conversão de Tipos).
Os quatro devolvem um double (ou um decimal, se você passar um decimal), só que sem casas. Para guardar num int, converta: int caixas = (int)Math.Ceiling(x);.
Missão: a Ana vai mandar 23 potes de doce, e cada caixa leva 6. O programa diz Caixas: 3, mas em 3 caixas só cabem 18 potes! Use Math.Ceiling para arredondar para cima e mostrar Caixas: 4. Dica: a divisão tem de ser com casas decimais, senão o 23 / 6 já vira 3 antes do Ceiling.
Caixas: 4
int potes = 23;
int porCaixa = 6;
int caixas = potes / porCaixa;
Console.WriteLine($"Caixas: {caixas}");
int potes = 23;
int porCaixa = 6;
int caixas = (int)Math.Ceiling(potes / (double)porCaixa);
Console.WriteLine($"Caixas: {caixas}");
3. O arredondamento bancário: 2,5 vira 2
Na tabela acima, o Math.Round(2.5) deu 2, e o Math.Round(3.5) deu 4. Não é defeito. Quando o número está exatamente no meio, o Round vai para o inteiro par mais perto: 2,5 → 2 (par), 3,5 → 4 (par). Esse é o arredondamento bancário (ou "do par"). Ele existe porque, numa lista grande de valores, arredondar sempre para cima faria a soma crescer; indo para o par, metade das vezes sobe e metade desce, e a soma fica justa.
Console.WriteLine(Math.Round(2.5)); Console.WriteLine(Math.Round(3.5)); Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero)); Console.WriteLine(Math.Round(3.14159, 2)); Console.WriteLine(Math.Round(2.665m, 2)); Console.WriteLine(Math.Round(2.665m, 2, MidpointRounding.AwayFromZero));
$ dotnet run bancario.cs 2 4 3 3,14 2,66 2,67
- MidpointRounding.AwayFromZero
- "No ponto do meio, afaste do zero": o arredondamento que se aprende na escola. 2,5 → 3, e −2,5 → −3. Use quando a regra do seu problema for essa (notas escolares, por exemplo).
- Math.Round(3.14159, 2)
- O segundo número diz quantas casas decimais manter: 3,14.
- Math.Round(2.665m, 2)
- Com
decimal(omno fim), a conta é exata. 2,665 está bem no meio entre 2,66 e 2,67, e o bancário escolhe o par: 2,66. ComAwayFromZero, 2,67.
decimal e uma regra escolhida de propósito
Para preços, use decimal e arredonde só no fim da conta: Math.Round(preco * 0.85m, 2). E escolha a regra de propósito: o padrão (bancário) é o comum em sistemas financeiros; o AwayFromZero é o que muita gente espera ver numa nota fiscal. Só para mostrar (sem mudar o valor guardado), use a formatação: {preco:C} ou {preco:F2} (aula Formatar Números).
4. Limitar e ver o sinal: Clamp e Sign
int[] pedidos = [-5, 3, 12];
foreach (int volume in pedidos)
{
Console.WriteLine($"{volume,3} → {Math.Clamp(volume, 0, 10)}");
}
Console.WriteLine(Math.Sign(-8));
Console.WriteLine(Math.Sign(0));
Console.WriteLine(Math.Sign(3.5));
$ dotnet run clamp.cs -5 → 0 3 → 3 12 → 10 -1 0 1
- Math.Clamp(volume, 0, 10)
- "Prender" o valor entre um mínimo e um máximo: abaixo de 0 vira 0, acima de 10 vira 10, e o que está dentro fica como está. É o volume de um aparelho, que não passa do máximo. Faz o mesmo que
Math.Min(Math.Max(volume, 0), 10), só que legível. - Math.Sign(x)
- O "sinal": −1 se for negativo, 0 se for zero, 1 se for positivo. Sempre um
int, mesmo com3.5.
5. Seno, cosseno e logaritmos
double graus = 30; double radianos = graus * Math.PI / 180; Console.WriteLine(Math.Sin(radianos)); Console.WriteLine(Math.Round(Math.Sin(radianos), 4)); Console.WriteLine(Math.Sin(30)); Console.WriteLine(Math.Log10(1000)); Console.WriteLine(Math.Log(Math.E)); Console.WriteLine(Math.Log2(1024));
$ dotnet run trig.cs 0,49999999999999994 0,5 -0,9880316240928618 « Math.Sin(30): 30 radianos, não 30 graus! 3 1 10
- graus * Math.PI / 180
- O
Sin("seno"), oCos("cosseno") e oTan("tangente") medem os ângulos em radianos, não em graus. Uma volta inteira tem 360 graus ou 2π radianos; esta conta converte. - 0,49999999999999994
- O seno de 30° é 0,5. Saiu "quase" 0,5, pelo mesmo motivo do
Cbrt. Arredondado para 4 casas, 0,5. - Math.Sin(30)
- Esquecer a conversão não dá erro: dá o seno de 30 radianos (umas 4,8 voltas), que é −0,988.
- Math.Log10(1000)
- Logaritmo na base 10: "10 elevado a quanto dá 1000?". 3. Ele conta os dígitos de um número:
(int)Math.Log10(n) + 1. - Math.Log(x)
- Logaritmo natural, na base e.
Math.Log(x, b)usa a baseb. - Math.Log2(1024)
- Na base 2: 10, porque 2¹⁰ = 1024. É quantas vezes dá para dividir por 2, a conta da busca binária da aula A Classe Array.
6. int, double ou decimal?
Algumas funções têm uma versão para cada tipo e devolvem o mesmo tipo que receberam. Outras só existem para double:
| Funções | Aceitam | Devolvem |
|---|---|---|
Abs, Max, Min, Clamp, Sign | int, long, double, decimal… | o mesmo tipo (o Sign, sempre int) |
Round, Floor, Ceiling, Truncate | double e decimal | o mesmo tipo |
Pow, Sqrt, Cbrt, Sin, Cos, Log… | só double (um int vira double sozinho) | double |
decimal area = 49m; double lado = Math.Sqrt((double)area); Console.WriteLine(lado); Console.WriteLine(Math.Abs(-19.90m)); Console.WriteLine(Math.Max(4.99m, 6.50m)); Console.WriteLine(Math.Round(16.9915m, 2)); int resultado = (int)Math.Pow(2, 3); Console.WriteLine(resultado);
$ dotnet run tipos.cs 7 19,90 6,50 16,99 8
- Math.Sqrt((double)area)
- O
Sqrtnão aceitadecimal: é preciso converter paradoubleantes (sem isso, erroCS1503). - Math.Abs(-19.90m)
- Com
decimal, o resultado também édecimale mantém as casas:19,90. - (int)Math.Pow(2, 3)
- O
Powsempre devolvedouble, mesmo com doisint. Para guardar numint, converta (sem isso, erroCS0266).
Escrever menos: using static
Num programa cheio de contas, repetir Math. cansa. Com using static System.Math; na primeira linha do arquivo, as funções podem ser chamadas pelo nome (aula Namespaces e using):
$ cat staticusing.cs using static System.Math; Console.WriteLine(Sqrt(16) + Pow(2, 3) + PI); $ dotnet run staticusing.cs 15,141592653589793
Sem essa linha, escrever só Sqrt(16) dá error CS0103: O nome "Sqrt" não existe no contexto atual. O guia continua escrevendo Math., que deixa claro de onde a função vem.
7. Resultados especiais: NaN e ∞
Com double, algumas contas impossíveis não dão erro: dão um valor especial.
Console.WriteLine(Math.Sqrt(-1)); Console.WriteLine(Math.Log(0)); Console.WriteLine(1.0 / 0); Console.WriteLine(double.IsNaN(Math.Sqrt(-1)));
$ dotnet run especiais.cs NaN -∞ ∞ True
- NaN
- Not a Number, "não é um número": a raiz de −1 não existe entre os números reais. Qualquer conta com
NaNdáNaN, e ele "contamina" tudo o que vem depois. - -∞ e ∞
- Infinito negativo e positivo: o logaritmo de 0 e a divisão de um
doublepor zero. (Comint, dividir por zero é exceção:DivideByZeroException.) - double.IsNaN(x)
- O jeito certo de perguntar "é NaN?". O
x == double.NaNdá semprefalse, até quandoxéNaN.
Quando dá errado
error CS0266: Não é possível converter implicitamente tipo "double" em "int". Existe uma conversão explícita (há uma conversão ausente?)
$ cat potencia.cs int resultado = Math.Pow(2, 3); $ dotnet run potencia.cs /home/ana/cs/potencia.cs(1,17): error CS0266: Não é possível converter implicitamente tipo "double" em "int". Existe uma conversão explícita (há uma conversão ausente?) Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: o Math.Pow (e o Sqrt, o Round, o Ceiling…) devolve double, e um double não cabe num int sem você pedir, porque poderia perder as casas.
Como consertar: guarde num double, ou converta: int resultado = (int)Math.Pow(2, 3);.
error CS1503: Argumento 1: não é possível converter de "decimal" para "double"
$ cat raiz-decimal.cs decimal area = 49m; decimal lado = Math.Sqrt(area); $ dotnet run raiz-decimal.cs /home/ana/cs/raiz-decimal.cs(2,26): error CS1503: Argumento 1: não é possível converter de "decimal" para "double" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: o Math.Sqrt só existe para double, e o C# não converte decimal em double sozinho.
Como consertar: double lado = Math.Sqrt((double)area);. Se precisa do resultado em decimal, converta de volta: (decimal)Math.Sqrt((double)area).
error CS1501: Nenhuma sobrecarga para o método "Max" leva 3 argumentos
O que significa: você escreveu Math.Max(3, 9, 5). O Max e o Min comparam dois números de cada vez.
Como consertar: encadeie, Math.Max(3, Math.Max(9, 5)), ou ponha num array e use o LINQ: new[] { 3, 9, 5 }.Max() (aula Agregar).
error CS0103: O nome "Sqrt" não existe no contexto atual
O que significa: faltou o Math. antes do nome da função.
Como consertar: Math.Sqrt(16), ou using static System.Math; na primeira linha.
Unhandled exception. System.OverflowException: Negating the minimum value of a twos complement number is invalid.
$ cat abs-min.cs int menor = int.MinValue; Console.WriteLine(menor); Console.WriteLine(Math.Abs(menor)); $ dotnet run abs-min.cs -2147483648 Unhandled exception. System.OverflowException: Negating the minimum value of a twos complement number is invalid. at Program.<Main>$(String[] args) in /home/ana/cs/abs-min.cs:line 3
O que significa: "inverter o sinal do menor valor não é válido". O menor int é −2.147.483.648, mas o maior é 2.147.483.647: o positivo dele não cabe num int (aula Inteiros). Em vez de dar um número errado, o Math.Abs para o programa (código 134).
Como consertar: se os valores podem chegar perto dos limites, use long: Math.Abs((long)menor).
Math.Round(2.5) deu 2, Math.Sin(30) deu −0,988
O que significa: o Round usa o arredondamento bancário (o meio vai para o par), e as funções de ângulo usam radianos.
Como consertar: Math.Round(x, MidpointRounding.AwayFromZero) para o arredondamento da escola, e graus * Math.PI / 180 para converter os ângulos.
Referência rápida
| Método | Exemplo | Resultado |
|---|---|---|
Math.Abs(x) | Math.Abs(-7) | 7 |
Math.Max(a, b) / Math.Min(a, b) | Math.Max(12, 45) | 45 |
Math.Pow(x, y) | Math.Pow(2, 10) | 1024 (double) |
Math.Sqrt(x) / Math.Cbrt(x) | Math.Sqrt(81) | 9 |
Math.Round(x) | Math.Round(2.5) | 2 (bancário) |
Math.Round(x, MidpointRounding.AwayFromZero) | Math.Round(2.5, …) | 3 |
Math.Round(x, casas) | Math.Round(3.14159, 2) | 3,14 |
Math.Floor(x) / Math.Ceiling(x) | Math.Floor(2.7) / Math.Ceiling(2.3) | 2 / 3 |
Math.Truncate(x) | Math.Truncate(-2.7) | −2 |
Math.Clamp(x, min, max) | Math.Clamp(12, 0, 10) | 10 |
Math.Sign(x) | Math.Sign(-8) | −1 |
Math.Sin / Cos / Tan | Math.Sin(Math.PI / 2) | 1 (ângulos em radianos) |
Math.Log(x) / Log10 / Log2 | Math.Log10(1000) | 3 |
Math.PI / Math.E | 3,14159… / 2,71828… |
Exercícios
1. Potência
Complete para calcular 5 elevado a 3:
2. Para baixo
Qual método transforma 9,99 em 9 e −9,99 em −10?
3. Limitar
Complete para a nota ficar sempre entre 0 e 10:
4. Quanto vale Math.Round(4.5)?
5. Quanto vale 2 ^ 3 em C#?
6. A Ana precisa de ônibus de 40 lugares para 130 pessoas. Qual conta dá o número certo de ônibus?
Missão final: uma pizza grande ou duas médias?
Missão: na pizzaria, a grande tem 35 cm de diâmetro e a média, 25 cm. O que dá mais pizza: uma grande ou duas médias? A área de um círculo é π × raio², e o raio é metade do diâmetro. O programa usa 3.14 no lugar de π e erra a grande. Troque pelo Math.PI e calcule a área das duas médias juntas. A saída tem de ser Grande: 962,11 cm² e Duas médias: 981,75 cm².
Grande: 962,11 cm² Duas médias: 981,75 cm²
double grande = 3.14 * (35 / 2.0) * (35 / 2.0);
double duasMedias = 0; // calcule aqui
Console.WriteLine($"Grande: {grande:F2} cm²");
Console.WriteLine($"Duas médias: {duasMedias:F2} cm²");
double grande = Math.PI * Math.Pow(35 / 2.0, 2);
double duasMedias = 2 * Math.PI * Math.Pow(25 / 2.0, 2);
Console.WriteLine($"Grande: {grande:F2} cm²");
Console.WriteLine($"Duas médias: {duasMedias:F2} cm²");
Surpresa: as duas médias dão um pouco mais de pizza. Com o 3.14, a grande saía 961,62 cm², meio centímetro quadrado a menos: o Math.PI tem muito mais casas (3,141592653589793). O 35 / 2.0, com o .0, garante a divisão com casas decimais (17,5); com 35 / 2, daria 17 e uma pizza menor.
Resumo
- A classe
Mathé a calculadora científica do .NET:Math.Abs,Max,Min,Pow,Sqrt,Cbrt, e as constantesMath.PIeMath.E. Potência éMath.Pow, nunca^. - Para tirar as casas:
Round(o mais perto),Floor(para baixo),Ceiling(para cima) eTruncate(corta). - O
Roundusa o arredondamento bancário: 2,5 → 2, 3,5 → 4. Para o da escola,MidpointRounding.AwayFromZero. Com dinheiro,decimaleMath.Round(valor, 2)no fim. Clampprende um valor entre um mínimo e um máximo;Signdá o sinal.Sin,CoseTanusam radianos (graus * Math.PI / 180).Pow,Sqrte as funções científicas trabalham comdouble: converta para guardar emint(CS0266) ou para passar umdecimal(CS1503). Os resultados podem ter um errinho na última casa, que pode até mudar entre o editor e o Linux: não compare com==.