PHP para Iniciantes: Operadores Aritméticos

Agora que você tem uma certa noção sobre como o interpretador funciona (ou pelo menos que ele segue uma ordem que não é a ordem de leitura natural), chegou a hora de entender quais operações temos disponíveis em nosso leque de possibilidades.

Antes de mais nada, entenda que os operadores não representam um limite de possibilidades. É como se fossem nossos instrumentos de trabalho, assim como facas, garfos e outras ferramentas para quem trabalha na culinária. Cada operador tem suas especialidades que nos auxiliam a fazer uma série de execuções para alcançar o resultado que desejamos.

É preciso entender suas limitações, a fundo, se não quiser ter comportamentos inesperados. É por isso que você está aqui, não é mesmo? Então vamos começar logo.

PS.: não vou seguir a ordem da documentação oficial do PHP, pois ela apresenta os operadores pelas ações que fazem, sendo que alguns símbolos aparecem mais de uma vez. Vou focar nos símbolos, beleza?!

Operador +

Esse é, talvez, um dos operadores mais óbvios e que existe em várias linguagens de programação. Algumas linguagens conseguem complicar a facilidade de entendimento inserido outras funções ao símbolo, como a concatenação. Mas não é o caso do PHP.

Com esse operador, você só pode fazer duas coisas: somar e definir uma identidade.

O que raios é definir identidade, Kiko?

É sobre casting. Quando você usa propositalmente um operador aritmético, você está deixando bem claro para o interpretador de que precisa de números, não textos numéricos. Então, você pode facilmente converter uma string para int ou float apenas colocando o símbolo + na frente da variável:

<?php
$int = '123';
$float = '-23.45';
var_dump(+$int); // int(123)
var_dump(+$float); // float(-23.45)

Isso também funciona com booleanos: false vira 0, true vira 1.

Apesar de parecer ser uma boa ideia escrever menos código fazendo casting dessa forma, você pode prejudicar o entendimento da sua escrita. Quem não está imerso no seu produto precisará ler atentamente cada linha pra perceber seus castings. Eu prefiro usar funções como intval ou floatval, há quem prefira fazer o casting com o prefixo (int) ou (float), porém eu acho essa forma de desenvolvimento muito vaga. Enquanto as funções encapsulam o valor a ser convertido, esse casting não faz isso, então você precisará separar direitinho as informações pra ser rapidamente legível.

E a soma, Kiko?

<?php

var_dump(1+1); // int(2)

Ohhhhhhh!!

Ok, parei com a zoeira. Se você leu meu artigo sobre pontos flutuantes e realmente acha que a soma é besteira, recomendo ler novamente. Quer dizer, calma, eu vou dar um resumo caso você sequer tenha visto outros artigos anteriores a esse. Mas o aprofundamento está lá, beleza?!

Soma com pontos flutuantes

A operação nativa de soma do PHP usa estruturas binárias para obter os resultados. Quando os números são inteiros (onde o binário representa o próprio valor), os resultados são precisos. Mas quando vamos para os pontos flutuantes, a história é completamente diferente.

O PHP implementa os pontos flutuantes com o padrão IEEE-754 - é internacional, ok?! -, que regulamenta o comportamento binário dessa estrutura. Consequentemente, as operações precisam lidar com as "falhas" de armazenamento desse tipo de dados. Como são complementos de uma base numérica, o número não é exatamente o que você digita.

Por exemplo, usando uma calculadora de pontos flutuantes no padrão IEEE-754, você pode verificar que o número 30.3 é, na verdade, 30.299999237060546875. É fácil reconhecer que pontos flutuantes com variação decimal sempre vão ter uma margem de erro, certo? E é justamente essa margem que um dia pode te trazer um resultado inesperado se você simplesmente usar operadores aritméticos nativos.

Quando vamos calcular com ponto flutuante, é recomendado usar as funções BCMath, bcadd, bcsub, etc, ao invés do operador de soma, subtração, etc. Apesar de que, em somas, o PHP consegue lidar com as margens de erros. Os problemas vem quando você usa subtração mesmo.

Ainda assim, considere seguir o meu conselho: se tem risco de lidar com float, use BCMath. Ele vai funcionar com inteiros também, então você vai estar muito mais seguro sobre a precisão das contas.

Kiko, e o incremento? Também não usa +?

Sim, mas concorda que não passa de uma soma também? A operação é a mesma. Então podemos continuar pro próximo...

Operador -

Assim como +, o operador - traz referências óbvias a subtração e negação. Assim como as pegadinhas de ponto flutuante que mencionei antes na soma, a subtração também sofre dos mesmos riscos. Lembre-se: pensou em conta matemática com float, use BCMath.

E a negação, Kiko? Qual a pegadinha?

Bom, estamos falando da negação aritmética, não do not booleano. É basicamente:

<?php

$a = 10;
var_dump(-$a); // int(-10)

$a = -10;
var_dump(-$a); // int(10)

Ou seja, ele nega a positividade do número, assim como nas contas matemáticas. Porém, lembra que o operador de identidade + também aceita booleanos? O que aconteceria com a negação numérica de um booleano?

<?php

$a = false;
var_dump(-$a); // int(0)

$a = true;
var_dump(-$a); // int(-1)

Ué??

Como eu falei: não confunda com a negação booleana. Essa negação, como mencionei anteriormente, inverte a positividade de um número. Se o tipo de dado é diferente de numérico, o interpretador tentará fazer o casting para o formato específico.

Vimos nos artigos de tipos de dados primitivos que boolean converte para int:

  • false vira 0;
  • true vira 1.

Logo, o código acima é o mesmo que fazer:

<?php

$a = 0;
var_dump(-$a); //int(0), afinal, o inteiro zero é sempre positivo

$a = 1;
var_dump(-$a); //int(-1)

Ficou mais claro? Enfim, então vamos ao próximo símbolo.

Operador *

Sei que na escola aprendemos multiplicação usando o símbolo x, mas, na computação, x é apenas uma letra ASCII. Poderíamos usar como símbolo de multiplicação: SIM, mas poderia prejudicar muita coisa. Por exemplo:

$rex1 seria a variável rex1 ou $re vezes 1?

Então, é de comum acordo que o ideal é utilizar caracteres que não são aceitos nas validações de nomenclatura de variáveis. Por isso, o mais próximo de x é * e isso representa a multiplicação.

Logo: $a*3 é $a vezes 3.

Além disso, esse operador também é usado para operações exponenciais, bastando usar dois símbolos * consecutivamente:

$a**3 é $a elevado a 3.

Ok, Kiko. Multiplicação faz sentido, mas por que exponencial?

Essa eu não vou saber responder. Até tentei pesquisar, por curiosidade, mas não encontrei um motivo lógico para simbolizar dessa forma. Porém, sei que não poderíamos usar o E do float pelo mesmo motivo que não usamos x. Também não usamos ^ pois já é um operador bitwise, logo já estava reservado.

Como exponencial é uma multiplicação em cadeia do mesmo valor, faz sentido referenciar com o mesmo símbolo, não? Embora isso seja apenas uma suposição e, como falei, não tenho argumento algum pra defender isso.

Você usaria dessa forma, Kiko?

Depende. Em contas simples, sim. Mas em contas mais complexas, prefiro deixar claro as limitações das funções com a função pow, sabendo precisamente qual parte da conta vai ser o expoente e tal. Sem dicas dessa vez.

Operador /

Falando em operação aritmética, essa barra define o operador de divisão. Por exemplo, $a / $b é $a dividido por $b. Faz muito sentido pois nós escrevemos frações no papel com uma barra separando os números, exceto que é um número em cima e outro embaixo, confere? Nesse caso, nossa escrita é pro lado mesmo.

Ok... Você falou "Falando em operação aritmética". Pro que mais a / serviria, Kiko?

Fazer comentários, ué? Se colocar um / a mais você irá destruir o seu código, rs.

$a // $b = $a.

Operador %

A operação mais interessante da matemática (-sqn). Módulo é o resto da divisão de um número por outro, o que simbolizamos com %. Significa: $a % $b é o resto da divisão de $a por $b. Se não existisse esse operador, você precisaria fazer uma série de continhas para calcular o resto. Falando matematicamente:

O resto da divisão de 10 por 7 é 3, porque:

  • a parte inteira de 10/71;
  • 1*77;
  • 10-73!

Mas como exatamente seria essa conta no PHP, Kiko?

Mais ou menos assim:

<?php

function modulo($a, $b) { //10, 7
    $divisao = floor($a / $b); // 1
    $totalB = $b * $divisao; // 7
    $resto = $a - $totalB; // 10 - 7 = 3
    return intval($resto);
}

var_dump(modulo(10, 7)); // int(3)

Nós podemos reduzir tudo isso pra:

<?php

var_dump(10%7); // int(3)

Mas onde que eu vou usar isso, Kiko?

Ah, tem muitas aplicações. A mais comum, é mapear um contador infinito a um número limitado de opções. Por exemplo:

<?php

$opcoes = ["Kaique", "Kiko"]; // você pode me chamar assim

for ($indice = 0; $indice < 20; $indice++) {
    echo $opcoes[$indice % count($opcoes)] . PHP_EOL;
}

$indice irá de 0 até 19, mas os únicos índices possíveis são 0 e 1. A solução é usar o resto da divisão:

  • 0 % 2 = 0;
  • 1 % 2 = 1;
  • 2 % 2 = 0;
  • 3 % 2 = 1;
  • ... e assim sucessivamente, alternando entre 0 e 1.

Dessa forma, conseguimos limitar o $indice ao limite do tamanho do array $opcoes sem alterar o valor de nenhum deles. Legal?

Um detalhe sobre módulo: ele não faz mudança de positividade. O sinal do resultado dependerá somente do dividendo, isto é, $a % <qualquercoisa> vai dar positivo, -$a % <qualquercoisa> vai dar negativo. Simples assim.

Outro detalhe: o resultado de módulos é sempre inteiro. Se por alguma necessidade obscura você precisa trabalhar com módulo de pontos flutuantes, o recomendado na documentação oficial do PHP é utilizar a função fmod(). É a mesma parada que venho falando sobre BCMath: floats são especiais.

ENFIM

Por hoje é só! Curtiu? Comenta e compartilha! No próximo artigo iremos falar sobre Operadores de Atribuição, que é bem simples e pode ser mesclado com outros operadores (inclusive com alguns mencionados neste artigo). Serve, basicamente, para otimizar a escrita de instruções. Mas, na prática, não faz diferença alguma para o interpretador. Ele vai executar as mesmíssimas instruções... Eu te conto mais detalhes lá, beleza? Chega mais que vai ser bom!

Inté!