C Matemática (math.h)

Os operadores + - * / % (Operadores Aritméticos) fazem as quatro operações. Para raiz quadrada, potência, arredondamento ou seno, o C tem uma caixa de funções prontas: a biblioteca de matemática, que você abre com #include <math.h>. Nesta aula você vai usar as funções mais comuns, entender por que o gcc às vezes exige uma opção a mais (-lm) e ver o que acontece quando a conta não tem resposta, como a raiz de um número negativo.

Você vai aprender
  • Calcular raiz quadrada (sqrt) e potência (pow).
  • Por que o gcc mostra referência não definida para "sqrt" e como o -lm resolve.
  • A diferença entre abs (inteiros) e fabs (números com vírgula).
  • Arredondar de quatro jeitos: round, floor, ceil e trunc.
  • O resto com vírgula (fmod), a distância entre dois pontos (hypot) e o π.
  • Seno e cosseno trabalham em radianos, não em graus.
  • O que são nan e inf, e como testá-los com isnan e isinf.

Todos os programas foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia, gcc -std=c23 -Wall -Wextra arquivo.c -o programa, mais o -lm no fim, que a seção 1 explica.


A ideia: a calculadora científica

A calculadora comum da Ana faz as quatro operações. Para a lição de geometria, ela pega a calculadora científica, que tem as teclas √, xʸ, sin, cos e π. Em C é parecido:

Na calculadoraEm C
As quatro operações, sempre à mãoOs operadores + - * /, que fazem parte da linguagem
Pegar a calculadora científica na gaveta#include <math.h> no topo do arquivo
Apertar a tecla √ e digitar 49Chamar a função: sqrt(49.0)

As funções de math.h recebem e devolvem double (float e double). Você pode passar um int: o C converte sozinho (Conversão de Tipos). Mas o resultado é sempre um double, e por isso é mostrado com %f, nunca com %d.


1. Raiz quadrada: sqrt e o -lm

A Ana quer cercar um canteiro quadrado de 49 m². Qual é o tamanho de cada lado? É a raiz quadrada da área: sqrt ("square root", raiz quadrada).

#include <stdio.h>
#include <math.h>

int main(void) {
    double area = 49.0;
    double lado = sqrt(area);

    printf("Um quadrado de %.1f m² tem lado de %.1f m\n", area, lado);
    printf("Raiz de 2: %.6f\n", sqrt(2.0));
    return 0;
}
#include <math.h>
Traz as declarações das funções de matemática: o compilador fica sabendo que sqrt existe, que recebe um double e devolve um double.
double lado = sqrt(area);
Chama a função com o valor de area e guarda o resultado, 7.0.
sqrt(2.0)
A raiz de 2 não é exata (1,41421356…). O %.6f mostra seis casas.

Compilando com o comando de sempre, vem uma surpresa:

$ gcc -std=c23 -Wall -Wextra raiz.c -o raiz
/usr/bin/x86_64-linux-gnu-ld.bfd: /tmp/ccPawk0J.o: na função "main":
raiz.c:(.text+0x23): referência não definida para "sqrt"
collect2: error: ld returned 1 exit status
$ gcc -std=c23 -Wall -Wextra raiz.c -o raiz -lm
$ ./raiz
Um quadrado de 49.0 m² tem lado de 7.0 m
Raiz de 2: 1.414214

O erro não veio do compilador, e sim do ligador (ld), a etapa final que junta o seu programa com as bibliotecas (Como o C Funciona). Referência não definida para "sqrt" (em inglês, undefined reference to `sqrt') quer dizer: "o seu código usa sqrt, mas eu não achei o corpo dessa função em lugar nenhum".

O #include <math.h> só apresenta a função ao compilador. O código dela mora num arquivo à parte, a biblioteca de matemática (libm, "lib" de biblioteca e "m" de matemática). Por uma tradição antiga do Unix, o gcc no Linux junta sozinho a biblioteca principal do C (a do printf), mas não a de matemática. A opção -lm pede: "ligue (-l) a biblioteca m".

O -lm vai no fim

O ligador lê o comando da esquerda para a direita. Se a biblioteca vem antes do arquivo que a usa, ele ainda não sabe que vai precisar dela e a descarta. O mesmo erro volta:

$ gcc -std=c23 -Wall -Wextra -lm raiz.c -o raiz
/usr/bin/x86_64-linux-gnu-ld.bfd: /tmp/cckch8md.o: na função "main":
raiz.c:(.text+0x23): referência não definida para "sqrt"
collect2: error: ld returned 1 exit status

Regra: os arquivos .c primeiro, as bibliotecas (-lm) depois.

Por que às vezes funciona sem -lm?

Você pode encontrar programas com sqrt que compilam sem o -lm. Este, por exemplo:

#include <stdio.h>
#include <math.h>

int main(void) {
    printf("Raiz de 49: %.1f\n", sqrt(49.0));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra constante.c -o constante     « sem -lm, e compilou
$ ./constante
Raiz de 49: 7.0

Aqui o número está escrito no código (49.0), e o gcc é esperto: ele mesmo calcula a raiz durante a compilação e escreve 7.0 no programa. Ninguém precisa chamar a sqrt de verdade, e o ligador não procura a biblioteca. O mesmo acontece com o fabs (seção 3), que o gcc troca por uma instrução do processador. Com uma variável, como no raiz.c, a conta só pode ser feita quando o programa roda, e aí a biblioteca é necessária. Para não ter de pensar nisso: usou math.h, ponha -lm.

No editor do guia

O editor já junta a biblioteca de matemática sozinho: as caixas desta aula funcionam sem -lm. No Linux, com o gcc, ele é necessário.


2. Potência: pow

O C não tem operador de potência. O ^ existe, mas é outra coisa: o "ou exclusivo" de Operadores de Bits. O engano é tão comum que o gcc 15 avisa:

$ cat xor.c
#include <stdio.h>

int main(void) {
    printf("%d\n", 2 ^ 10);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra xor.c -o xor
xor.c: In function ‘main’:
xor.c:4:22: warning: result of ‘2^10’ is 8; did you mean ‘1 << 10’ (1024)? [-Wxor-used-as-pow]
    4 |     printf("%d\n", 2 ^ 10);
      |                      ^
      |                    -
      |                    1 <<
xor.c:4:20: note: you can silence this warning by using a hexadecimal constant (0x2 rather than 2)
    4 |     printf("%d\n", 2 ^ 10);
      |                    ^
      |                    0x2
$ ./xor
8

"O resultado de 2^10 é 8; você quis dizer 1 << 10 (1024)?" (o << também é de Operadores de Bits, e só serve para potências de 2). Para elevar um número qualquer a outro, use pow(base, expoente) ("power", potência):

#include <stdio.h>
#include <math.h>

int main(void) {
    printf("2 elevado a 10 = %.0f\n", pow(2, 10));
    printf("1.5 ao quadrado = %.2f\n", pow(1.5, 2));
    printf("raiz cúbica de 27 = %.1f\n", pow(27, 1.0 / 3));

    double guardado = 1000.0;
    double depois = guardado * pow(1.01, 12);
    printf("R$ %.2f a 1%% ao mês, depois de 12 meses: R$ %.2f\n", guardado, depois);
    return 0;
}
pow(2, 10)
210 = 1024. Os int 2 e 10 viram double na entrada; o resultado é o double 1024.0, mostrado com %.0f (zero casas).
pow(27, 1.0 / 3)
Elevar a 1/3 é tirar a raiz cúbica. Repare no 1.0: 1 / 3 seria divisão inteira e daria 0 (float e double), e pow(27, 0) é 1.
guardado * pow(1.01, 12)
Juros compostos: 1% ao mês é multiplicar por 1,01 a cada mês; doze meses são 1,0112.
1%%
Dentro do printf, %% mostra um % (Formatos do printf).
$ gcc -std=c23 -Wall -Wextra potencia.c -o potencia -lm
$ ./potencia
2 elevado a 10 = 1024
1.5 ao quadrado = 2.25
raiz cúbica de 27 = 3.0
R$ 1000.00 a 1% ao mês, depois de 12 meses: R$ 1126.83

O resultado é double

O engano mais comum com pow é mostrar o resultado com %d:

#include <stdio.h>
#include <math.h>

int main(void) {
    printf("%d\n", pow(2, 10));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra powd.c -o powd -lm
powd.c: In function ‘main’:
powd.c:5:14: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’ [-Wformat=]
    5 |     printf("%d\n", pow(2, 10));
      |             ~^     ~~~~~~~~~~
      |              |     |
      |              int   double
      |             %f
$ ./powd
1293164760

"O formato %d espera um int, mas o argumento é double". O gcc até sugere o %f. Ignorando o aviso, o programa mostra lixo: um double é guardado de um jeito totalmente diferente de um int, e o printf leu o lugar errado. No editor, o mesmo programa mostra 0. Se você quer um inteiro, converta com um cast: int casas = (int) pow(2, 10);.

Para um quadrado, multiplique

x * x é mais simples e mais rápido que pow(x, 2), e funciona com int sem conversão. Deixe o pow para expoentes grandes ou com vírgula.

Experimente: mude os juros para 2% ao mês (1.02) e o prazo para 24 meses. Depois tente calcular a raiz quadrada de 2 com pow (que expoente dá a raiz quadrada?) e compare com sqrt(2.0).

#include <stdio.h>
#include <math.h>

int main(void) {
    double guardado = 1000.0;
    double depois = guardado * pow(1.01, 12);
    printf("Depois de 12 meses: R$ %.2f\n", depois);

    printf("sqrt(2.0) = %.6f\n", sqrt(2.0));
    return 0;
}

3. Valor absoluto: abs e fabs

O valor absoluto é o número sem o sinal: o absoluto de −35 é 35, e o de 35 também. Serve para medir distâncias e diferenças ("errou o palpite por quanto?"). O C tem uma função para cada tipo:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void) {
    int saldo = -35;
    double temperatura = -2.5;

    printf("abs(%d) = %d\n", saldo, abs(saldo));
    printf("fabs(%.1f) = %.1f\n", temperatura, fabs(temperatura));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra absoluto.c -o absoluto -lm
$ ./absoluto
abs(-35) = 35
fabs(-2.5) = 2.5
abs
"Absolute", absoluto, para int. Mora no <stdlib.h>, não no math.h. Para long e long long, existem labs e llabs.
fabs
"Floating absolute", absoluto de ponto flutuante, para double. Mora no <math.h>.

Trocar uma pela outra é um erro silencioso: abs com um double joga fora a parte depois da vírgula. O -Wextra avisa:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    double temperatura = -2.5;
    printf("%d\n", abs(temperatura));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra abserrado.c -o abserrado
abserrado.c: In function ‘main’:
abserrado.c:6:20: warning: using integer absolute value function ‘abs’ when argument is of floating-point type ‘double’ [-Wabsolute-value]
    6 |     printf("%d\n", abs(temperatura));
      |                    ^~~
$ ./abserrado
2

"Usando a função de valor absoluto de inteiros, abs, com um argumento de ponto flutuante". O −2.5 virou −2 na entrada, e o resultado foi 2, não 2.5. O Clang do editor dá o mesmo aviso e ainda sugere: note: use function 'fabs' instead ("use a função fabs").


4. Arredondar: round, floor, ceil e trunc

"Arredondar" pode querer dizer quatro coisas diferentes. Veja as quatro funções com os mesmos números:

#include <stdio.h>
#include <math.h>

int main(void) {
    double valores[] = {2.4, 2.5, 2.6, -2.4, -2.5, -2.6};

    printf("valor   round  floor   ceil  trunc\n");
    for (int i = 0; i < 6; i++) {
        double x = valores[i];
        printf("%5.1f  %5.1f  %5.1f  %5.1f  %5.1f\n",
               x, round(x), floor(x), ceil(x), trunc(x));
    }
    return 0;
}
$ gcc -std=c23 -Wall -Wextra arredondar.c -o arredondar -lm
$ ./arredondar
valor   round  floor   ceil  trunc
  2.4    2.0    2.0    3.0    2.0
  2.5    3.0    2.0    3.0    2.0
  2.6    3.0    2.0    3.0    2.0
 -2.4   -2.0   -3.0   -2.0   -2.0
 -2.5   -3.0   -3.0   -2.0   -2.0
 -2.6   -3.0   -3.0   -2.0   -2.0
FunçãoNomeO que faz2.5−2.5
round"arredondar"Para o inteiro mais próximo; o meio (,5) vai para longe do zero3−3
floor"chão"Sempre para baixo (o inteiro menor ou igual)2−3
ceil"ceiling", tetoSempre para cima (o inteiro maior ou igual)3−2
trunc"truncar", cortarCorta a parte depois da vírgula (vai em direção ao zero)2−2

Repare nos negativos: floor(-2.4) é −3, porque −3 é menor que −2.4. Já o trunc faz o mesmo que um cast (int): só corta. Todas devolvem double (por isso o 3.0). Para receber um inteiro, existe o lround, que devolve long (aula Conversão de Tipos).

Quando usar cada um: as caixas de ovos

A Ana tem 40 ovos e cada caixa leva 12. Quantas caixas ela precisa? 40 / 12 = 3,33…, e 3 caixas não bastam: sobram ovos. Aqui o certo é o ceil, sempre para cima. Mas cuidado com a divisão:

#include <stdio.h>
#include <math.h>

int main(void) {
    int ovos = 40;
    int por_caixa = 12;

    int caixas = (int) ceil((double) ovos / por_caixa);
    int errado = (int) ceil(ovos / por_caixa);

    printf("Caixas necessárias: %d\n", caixas);
    printf("Com divisão inteira: %d\n", errado);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra caixas.c -o caixas -lm
$ ./caixas
Caixas necessárias: 4
Com divisão inteira: 3

Na segunda conta, ovos / por_caixa é uma divisão de dois int: dá 3, sem vírgula, antes de o ceil receber o valor. Não há o que arredondar. O (double) na primeira conta faz a divisão ser com vírgula (3,33…), e o ceil leva a 4. O (int) da frente converte o resultado (4.0) para guardar num int.

Arredondar com duas casas decimais

As funções arredondam para o inteiro. Para arredondar a média 7,4567 em duas casas, multiplique por 100, arredonde e divida de volta:

#include <stdio.h>
#include <math.h>

int main(void) {
    double media = 7.4567;
    double arredondada = round(media * 100) / 100;

    printf("Média guardada: %.4f\n", media);
    printf("Arredondada:    %.4f\n", arredondada);
    printf("Só na tela:     %.2f\n", media);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra casas.c -o casas -lm
$ ./casas
Média guardada: 7.4567
Arredondada:    7.4600
Só na tela:     7.46

7,4567 × 100 = 745,67; arredondado, 746; dividido por 100, 7,46. Se você só quer mostrar com duas casas, o %.2f já arredonda na tela, sem mudar o valor guardado. Use o round quando o valor arredondado for usado em outras contas.


5. Resto com vírgula: fmod

O % só funciona com inteiros. Com double, o gcc recusa:

$ gcc -std=c23 -Wall -Wextra restoerr.c -o restoerr
restoerr.c: In function ‘main’:
restoerr.c:5:27: error: invalid operands to binary % (have ‘double’ and ‘double’)
    5 |     printf("%.1f\n", fita % 3.0);
      |                           ^

"Operandos inválidos para o % (recebeu double e double)". Para o resto com vírgula, use fmod ("floating modulo"). A Ana tem 10 m de fita e corta pedaços de 3 m:

#include <stdio.h>
#include <math.h>

int main(void) {
    double fita = 10.0;
    double pedaco = 3.0;

    printf("Pedaços inteiros: %.0f\n", floor(fita / pedaco));
    printf("Sobra: %.1f m\n", fmod(fita, pedaco));
    printf("Sobra de 7.5 / 2: %.1f\n", fmod(7.5, 2.0));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra resto.c -o resto -lm
$ ./resto
Pedaços inteiros: 3
Sobra: 1.0 m
Sobra de 7.5 / 2: 1.5

Três pedaços de 3 m usam 9 m e sobra 1 m. E 7,5 = 3 × 2 + 1,5. No editor, a mensagem do Clang para o % com double é invalid operands to binary expression ('double' and 'double').


6. Distância entre dois pontos: sqrt e hypot

No mapa do bairro, a casa da Ana fica no ponto (1, 2) e a escola no ponto (4, 6), em quarteirões. Em linha reta, qual é a distância? É o teorema de Pitágoras: a raiz de (diferença em x)² + (diferença em y)².

#include <stdio.h>
#include <math.h>

int main(void) {
    double x1 = 1.0, y1 = 2.0;
    double x2 = 4.0, y2 = 6.0;

    double dx = x2 - x1;
    double dy = y2 - y1;

    printf("Com sqrt:  %.2f\n", sqrt(dx * dx + dy * dy));
    printf("Com hypot: %.2f\n", hypot(dx, dy));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra distancia.c -o distancia -lm
$ ./distancia
Com sqrt:  5.00
Com hypot: 5.00

dx é 3 e dy é 4: 3² + 4² = 9 + 16 = 25, e a raiz de 25 é 5. A função hypot ("hypotenuse", hipotenusa) faz a mesma conta de uma vez e, com números muito grandes ou muito pequenos, perde menos precisão.

Missão: a pracinha fica no ponto (2, 3) e a sorveteria no ponto (14, 8). Complete o cálculo da distância em linha reta, com sqrt ou hypot.

Distância: 13.00
#include <stdio.h>
#include <math.h>

int main(void) {
    double x1 = 2.0, y1 = 3.0;
    double x2 = 14.0, y2 = 8.0;

    double dx = x2 - x1;
    double dy = y2 - y1;
    double d = dx + dy;   // troque pela conta certa

    printf("Distância: %.2f\n", d);
    return 0;
}
#include <stdio.h>
#include <math.h>

int main(void) {
    double x1 = 2.0, y1 = 3.0;
    double x2 = 14.0, y2 = 8.0;

    double dx = x2 - x1;
    double dy = y2 - y1;
    double d = sqrt(dx * dx + dy * dy);

    printf("Distância: %.2f\n", d);
    return 0;
}

7. O número π

Muita gente escreve M_PI para o π, e muitos tutoriais usam. Com o comando padrão do guia, o gcc não reconhece:

#include <stdio.h>
#include <math.h>

int main(void) {
    double raio = 2.0;
    printf("Área: %.2f\n", M_PI * raio * raio);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra pi.c -o pi -lm
pi.c: In function ‘main’:
pi.c:6:28: error: ‘M_PI’ undeclared (first use in this function)
    6 |     printf("Área: %.2f\n", M_PI * raio * raio);
      |                            ^~~~
pi.c:6:28: note: each undeclared identifier is reported only once for each function it appears in
$ gcc -Wall -Wextra pi.c -o pi -lm          « sem o -std=c23
$ ./pi
Área: 12.57

O M_PI não faz parte do C padrão: é um extra do Unix. Com -std=c23 (C padrão, sem extras), o math.h não o define. Sem o -std, o gcc usa o "C23 com extras do GNU" e ele aparece. No editor, o Clang também recusa: use of undeclared identifier 'M_PI'. Para um programa que compila em qualquer lugar, defina o seu π com constexpr (Constantes):

#include <stdio.h>
#include <math.h>

constexpr double PI = 3.14159265358979323846;

int main(void) {
    double raio = 3.0;
    printf("Área do canteiro: %.2f m²\n", PI * raio * raio);
    printf("Comprimento da borda: %.2f m\n", 2 * PI * raio);
    printf("acos(-1) = %.15f\n", acos(-1.0));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra pi2.c -o pi2 -lm
$ ./pi2
Área do canteiro: 28.27 m²
Comprimento da borda: 18.85 m
acos(-1) = 3.141592653589793
constexpr double PI = 3.14159265358979323846;
Uma constante de verdade C23, com mais casas do que um double consegue guardar (ele guarda umas 16). Em C antigo, escreva #define PI 3.14159265358979323846.
acos(-1.0)
Outro jeito clássico de obter o π: o arco cujo cosseno é −1 mede π radianos (meia volta). Mostra que o π calculado bate com o escrito.

8. Seno e cosseno: em radianos

As funções sin (seno), cos (cosseno) e tan (tangente) recebem o ângulo em radianos, não em graus. Uma volta inteira tem 360 graus, ou 2π radianos. Para converter: radianos = graus × π / 180.

#include <stdio.h>
#include <math.h>

constexpr double PI = 3.14159265358979323846;

int main(void) {
    double graus = 30.0;
    double rad = graus * PI / 180.0;

    printf("sin(30 graus) = %.4f\n", sin(rad));
    printf("cos(60 graus) = %.4f\n", cos(60.0 * PI / 180.0));
    printf("sin(30) sem converter = %.4f\n", sin(30.0));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra trig.c -o trig -lm
$ ./trig
sin(30 graus) = 0.5000
cos(60 graus) = 0.5000
sin(30) sem converter = -0.9880     « o seno de 30 radianos, não de 30 graus

O seno de 30 graus é 0,5, como na escola. Sem converter, sin(30.0) calcula o seno de 30 radianos (quase 5 voltas), e o resultado não tem nada a ver. O compilador não avisa: para ele, 30.0 é só um número.


9. Quando a conta não tem resposta: nan e inf

Não existe raiz quadrada (real) de um número negativo, nem logaritmo de zero. Em vez de travar, as funções devolvem valores especiais do double:

#include <stdio.h>
#include <math.h>

int main(void) {
    double x = -4.0;
    double zero = 0.0;
    double r = sqrt(x);

    printf("sqrt(-4) = %f\n", r);
    printf("log(0)   = %f\n", log(zero));
    printf("1 / 0.0  = %f\n", 1.0 / zero);

    if (isnan(r)) {
        printf("r não é um número (NaN)\n");
    }
    if (isinf(log(zero))) {
        printf("log(0) é infinito\n");
    }
    return 0;
}
$ gcc -std=c23 -Wall -Wextra nan.c -o nan -lm
$ ./nan
sqrt(-4) = -nan
log(0)   = -inf
1 / 0.0  = inf
r não é um número (NaN)
log(0) é infinito
nan
"Not a number", não é um número: o resultado de uma conta impossível. Qualquer conta com um nan dá nan, e ele "contamina" o resto do programa. (O sinal de menos em -nan não tem significado prático.)
inf
"Infinity", infinito. log(0) é menos infinito; 1.0 / 0.0 é mais infinito. Com double, dividir por zero não derruba o programa (com int, derruba: veja float e double).
isnan(r), isinf(x)
Perguntam "é nan?" e "é infinito?". Não dá para testar com r == NAN: um nan não é igual a nada, nem a ele mesmo.

Melhor que testar depois é evitar a conta impossível antes: if (x >= 0) { … sqrt(x) … }. É o que a missão final faz.


Formas diferentes de fazer a mesma conta

ContaFormasQual usar
Quadradox * x ou pow(x, 2)x * x: mais simples, e funciona com int
Raiz quadradasqrt(x) ou pow(x, 0.5)sqrt: mais clara e mais precisa
Raiz cúbicacbrt(x) ou pow(x, 1.0 / 3)cbrt ("cube root"): funciona também com negativos (pow(-27, 1.0/3) dá nan)
Hipotenusasqrt(a*a + b*b) ou hypot(a, b)hypot, que não estoura com números enormes
Maior de dois doublea > b ? a : b (ternário) ou fmax(a, b)fmax/fmin (do math.h) deixam a intenção clara; para int, use o ternário
Mostrar com 2 casas%.2f ou round(x * 100) / 100%.2f, se é só para a tela

Quando dá errado

referência não definida para "sqrt" (undefined reference to `sqrt')

O que significa: o ligador não achou o código da função. Faltou o -lm, ou ele está antes do arquivo .c no comando. O mesmo vale para pow, round, floor e as outras.

Como consertar: gcc -std=c23 -Wall -Wextra arquivo.c -o programa -lm, com o -lm no fim. No Makefile, a biblioteca vai em LDLIBS = -lm; no CMake, em target_link_libraries(programa m).

error: implicit declaration of function ‘sqrt’
$ gcc -std=c23 -Wall -Wextra semmath.c -o semmath
semmath.c: In function ‘main’:
semmath.c:5:28: error: implicit declaration of function ‘sqrt’ [-Wimplicit-function-declaration]
    5 |     printf("Lado: %.1f\n", sqrt(area));
      |                            ^~~~
semmath.c:2:1: note: include ‘<math.h>’ or provide a declaration of ‘sqrt’
    1 | #include <stdio.h>
  +++ |+#include <math.h>
    2 |
semmath.c:5:28: warning: incompatible implicit declaration of built-in function ‘sqrt’ [-Wbuiltin-declaration-mismatch]
    5 |     printf("Lado: %.1f\n", sqrt(area));
      |                            ^~~~
semmath.c:5:28: note: include ‘<math.h>’ or provide a declaration of ‘sqrt’

O que significa: faltou o #include <math.h>. O gcc até mostra onde pôr a linha (o +++ |+). No editor: call to undeclared library function 'sqrt' e include the header <math.h>.

Como consertar: #include <math.h> no topo. Para abs, o include é <stdlib.h>.

warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’

O que significa: as funções de math.h devolvem double, e o %d é para int. O programa mostra lixo.

Como consertar: use %f (ou %.0f, %.2f), ou converta: (int) pow(2, 10).

warning: using integer absolute value function ‘abs’ when argument is of floating-point type

O que significa: abs é para int; com double, a parte depois da vírgula se perde.

Como consertar: fabs(x), com #include <math.h>.

error: invalid operands to binary % (have ‘double’ and ‘double’)

O que significa: o % só aceita inteiros.

Como consertar: fmod(a, b).

error: ‘M_PI’ undeclared

O que significa: o M_PI não é do C padrão, e com -std=c23 ele não existe.

Como consertar: constexpr double PI = 3.14159265358979323846; (ou #define, em C antigo).

Seno, cosseno ou divisão "errados", sem aviso

O que significa: provavelmente o ângulo está em graus (as funções esperam radianos), ou uma divisão entre dois int perdeu a vírgula antes de chegar à função (ceil(40 / 12) é ceil(3)).

Como consertar: converta graus com * PI / 180; garanta um double na divisão (40.0 / 12 ou (double) ovos / por_caixa).


Referência rápida

Todas recebem e devolvem double e estão no <math.h>, menos as marcadas. Existem versões para float e long double com f e l no fim (sqrtf, sqrtl).

FunçãoO que calculaExemplo
sqrt(x)Raiz quadradasqrt(49.0) → 7
cbrt(x)Raiz cúbicacbrt(27.0) → 3
pow(b, e)b elevado a epow(2, 10) → 1024
fabs(x)Valor absoluto de doublefabs(-2.5) → 2.5
abs(n), labs, llabsValor absoluto de inteiro (<stdlib.h>)abs(-35) → 35
round(x)Inteiro mais próximo (,5 para longe do zero)round(2.5) → 3
lround(x)Como round, mas devolve longlround(2.5) → 3
floor(x)Para baixofloor(-2.4) → −3
ceil(x)Para cimaceil(3.1) → 4
trunc(x)Corta a parte depois da vírgulatrunc(-2.6) → −2
fmod(a, b)Resto da divisão com vírgulafmod(7.5, 2.0) → 1.5
hypot(a, b)√(a² + b²)hypot(3, 4) → 5
fmin(a, b), fmax(a, b)Menor e maior de doisfmax(-5.0, 0.0) → 0
sin, cos, tanSeno, cosseno, tangente (em radianos)sin(PI / 6) → 0.5
asin, acos, atan, atan2(y, x)Os inversos (devolvem radianos)acos(-1.0) → π
exp(x), log(x)ex e logaritmo naturallog(exp(2.0)) → 2
log10(x), log2(x)Logaritmo na base 10 e na base 2log10(1000.0) → 3
isnan(x), isinf(x)É "não número"? É infinito?isnan(sqrt(-1.0)) → verdadeiro
-lmOpção do gcc que liga a biblioteca de matemática (no fim do comando)gcc a.c -o a -lm

Exercícios

1. A raiz

Complete para guardar em r a raiz quadrada de x:

double r = ;

2. A opção do gcc

Complete o comando para o ligador encontrar a sqrt:

gcc -std=c23 -Wall -Wextra raiz.c -o raiz

3. Sempre para cima

Que função dá o número de ônibus necessários para levar 130 alunos em ônibus de 40 lugares (130.0 / 40 = 3,25 → 4)?

(130.0 / 40)

4. O que floor(-2.5) devolve?

5. Por que gcc -lm raiz.c -o raiz dá referência não definida para "sqrt"?

6. Qual linha mostra o resultado de pow(2, 10) corretamente?

Missão final: as raízes da equação

Missão: a lição de casa da Ana é achar as raízes de equações do 2º grau (ax² + bx + c = 0) com a fórmula de Bhaskara. Complete a função raizes, que já calcula o delta (b² − 4ac):

  • se delta < 0, mostre sem raízes reais (não chame a sqrt com número negativo);
  • senão, calcule x1 = (-b + √delta) / (2a) e x2 = (-b - √delta) / (2a) e mostre x1 = …, x2 = …, com duas casas.

A saída tem de ser exatamente esta, sem avisos:

x² - 5x + 6: x1 = 3.00, x2 = 2.00
2x² + 4x - 6: x1 = 1.00, x2 = -3.00
x² + 2x + 5: sem raízes reais
#include <stdio.h>
#include <math.h>

void raizes(double a, double b, double c) {
    double delta = b * b - 4 * a * c;
    // se delta < 0: mostre "sem raízes reais"
    // senão: calcule x1 e x2 e mostre "x1 = ..., x2 = ..."
}

int main(void) {
    printf("x² - 5x + 6: ");
    raizes(1, -5, 6);
    printf("2x² + 4x - 6: ");
    raizes(2, 4, -6);
    printf("x² + 2x + 5: ");
    raizes(1, 2, 5);
    return 0;
}
#include <stdio.h>
#include <math.h>

void raizes(double a, double b, double c) {
    double delta = b * b - 4 * a * c;
    if (delta < 0) {
        printf("sem raízes reais\n");
        return;
    }
    double x1 = (-b + sqrt(delta)) / (2 * a);
    double x2 = (-b - sqrt(delta)) / (2 * a);
    printf("x1 = %.2f, x2 = %.2f\n", x1, x2);
}

int main(void) {
    printf("x² - 5x + 6: ");
    raizes(1, -5, 6);
    printf("2x² + 4x - 6: ");
    raizes(2, 4, -6);
    printf("x² + 2x + 5: ");
    raizes(1, 2, 5);
    return 0;
}

No terminal do Linux, este programa precisa do -lm, porque a sqrt recebe uma variável (delta). Repare também no (2 * a) entre parênteses: sem eles, / 2 * a dividiria por 2 e depois multiplicaria por a (Precedência).


Resumo

  • #include <math.h> traz as funções de matemática. Elas recebem e devolvem double: mostre com %f.
  • No Linux, o gcc precisa de -lm no fim do comando; sem ele, referência não definida para "sqrt". Com números fixos o gcc às vezes calcula sozinho e o erro some, mas não conte com isso. O editor não precisa de -lm.
  • sqrt (raiz), pow (potência; o ^ não é potência), fabs (absoluto de double; abs é para int, no stdlib.h).
  • Arredondar: round (mais próximo), floor (para baixo), ceil (para cima), trunc (corta). Cuidado com a divisão inteira antes da função.
  • fmod é o resto com vírgula; hypot, a distância; seno e cosseno usam radianos.
  • O M_PI não é padrão: use constexpr double PI = 3.14159265358979323846;.
  • Conta impossível dá nan ou inf; teste com isnan/isinf ou, melhor, evite a conta antes.
  • Próxima aula: Números Aleatórios (rand).