+2

Operações Monetárias no PHP

criado por Claudio Diniz em 30/04/2002 3:26pm
Já há algum tempo venho acompanhando o crescimento do site phpbrasil.com, mas infelizmente estava super ocupado em minhas tarefas "phpzais"... Espero a partir dessa semana contribuir com pelo menos 1 artigo por semana. Vamos lá:

Você terminou de fazer aquele super carrinho de compras... Ficou muito bom: Gráficos super legais, código bem feito, etc... seus produtos já estão no MySQL com as descrições, os preços, etc...

Só que na hora que você tenta fazer uma simples conta em Reais (como por exemplo R$ 45,76 + R$ 23,90) o PHP lhe presenteia com um número mais ou menos assim :

R$ 654,,7.8

Por que isso acontece ? O PHP não reconhece a vírgula do real... Qualquer conta que você tente fazer com uma simples "," no meio pode ter um resultado estranho para você e para seu cliente.

Como resolver ? é muito simples ! Vamos escrever uma função que vai pegar esse valor em Reais, fazer a operação aritmética correspondente e soltar o resultado já formatado em Reais:

<?php

function formataReais($valor1, $valor2, $operacao)
{
    /*     function formataReais ($valor1, $valor2, $operacao)
     *
     *     $valor1 = Primeiro valor da operação
     *     $valor2 = Segundo valor da operação
     *     $operacao = Tipo de operações possíveis . Pode ser :
     *     "+" = adição,
     *     "-" = subtração,
     *     "*" = multiplicação
     *
     */


    // Antes de tudo , arrancamos os "," e os "." dos dois valores passados a função . Para isso , podemos usar str_replace :
    $valor1 = str_replace (",", "", $valor1);
    $valor1 = str_replace (".", "", $valor1);

    $valor2 = str_replace (",", "", $valor2);
    $valor2 = str_replace (".", "", $valor2);


    // Agora vamos usar um switch para determinar qual o tipo de operação que foi definida :
    switch ($operacao) {
        // Adição :
        case "+":
            $resultado = $valor1 + $valor2;
            break;

        // Subtração :
        case "-":
            $resultado = $valor1 - $valor2;
            break;

        // Multiplicação :
        case "*":
            $resultado = $valor1 * $valor2;
            break;

    } // Fim Switch


    // Calcula o tamanho do resultado com strlen
    $len = strlen ($resultado);


    // Novamente um switch , dessa vez de acordo com o tamanho do resultado da operação ($len) :
    // De acordo com o tamanho de $len , realizamos uma ação para dividir o resultado e colocar
    // as vírgulas e os pontos
    switch ($len) {
        // 2 : 0,99 centavos
        case "2":
            $retorna = "0,$resultado";
            break;

        // 3 : 9,99 reais
        case "3":
            $d1 = substr("$resultado",0,1);
            $d2 = substr("$resultado",-2,2);
            $retorna = "$d1,$d2";
            break;

        // 4 : 99,99 reais
        case "4":
            $d1 = substr("$resultado",0,2);
            $d2 = substr("$resultado",-2,2);
            $retorna = "$d1,$d2";
            break;

        // 5 : 999,99 reais
        case "5":
            $d1 = substr("$resultado",0,3);
            $d2 = substr("$resultado",-2,2);
            $retorna = "$d1,$d2";
            break;

        // 6 : 9.999,99 reais
        case "6":
            $d1 = substr("$resultado",1,3);
            $d2 = substr("$resultado",-2,2);
            $d3 = substr("$resultado",0,1);
            $retorna = "$d3.$d1,$d2";
            break;

        // 7 : 99.999,99 reais
        case "7":
            $d1 = substr("$resultado",2,3);
            $d2 = substr("$resultado",-2,2);
            $d3 = substr("$resultado",0,2);
            $retorna = "$d3.$d1,$d2";
            break;

        // 8 : 999.999,99 reais
        case "8":
            $d1 = substr("$resultado",3,3);
            $d2 = substr("$resultado",-2,2);
            $d3 = substr("$resultado",0,3);
            $retorna = "$d3.$d1,$d2";
            break;

    } // Fim Switch

    // Por fim , retorna o resultado já formatado
    return $retorna;
} // Fim da function
?>

E é isso ! Como usar essa função ?

Adição :
Na adição, a ordem dos fatores não altera o produto, então você pode chamar essa função como :
<?php
formataReais("347,89", "67,12", "+");
// ou
formataReais("67,12", "347,89", "+");
?>

Subtração :
Na subtração, diferentemente da adição, a ordem dos fatores altera o produto. Então, se você não quiser ter um número negativo como resultado, use sempre o maior número primeiro:
<?php
// Certo :
formataReais("347,89", "67,12", "-");
// Errado :
formataReais("67,12", "347,89", "-");
?>

Multiplicação :
Do mesmo jeito da adição , a ordem dos fatores na multiplicação não altera o produto . Você pode colocar o multiplicador tanto no primeiro quanto no segundo valor :
<?php
// Certo :
formataReais("347,89", "6", "*");
// Porem se você tentar multiplicar um valor em real por outro valor de real :
formataReais("347,89", "10,76", "*");
// O número ficará muito grande , e com certeza um erro ocorrerá . Use somente números inteiros como multiplicadores .
?>

Bom, é isso aí ! Muitas coisas ainda podem ser melhoradas nessa função, com certeza, como o reconhecimento do maior número na hora da subtração, multiplicação por números maiores, etc... Mas para um comércio eletrônico, essa função é mais do que útil .

Bom, já sabem: Todos nós somos humanos, todos estamos sujeitos a falhas, e todo bom programador sabe disso... Portanto, qualquer sugestão, adição, comentário, ou crítica, por favor, estarei aqui para ler tudo.

Abraços,
Claudio Diniz

P.S.: Esta função só suporta valores até R$ 999.999,99. Se você estiver planejando vender mais do que 1 milhão em seu site, entre em contato comigo (e me arranja um trampo tambem)

Comentários:

Mostrando 1 - 10 de 16 comentários
Nilson Henze disse:
Estava procurando uma forma de calcular porcentagem de valores e com essa função consegui e me sinto no dever de compartilhar como ficou o código para quem assim como eu, está procurando.

Obs.: Troquei o nome e as variáveis para melhor organização em meu código, porém todo o crédito para o desenvolvedor.

-------------------------------------------------------------------------
<?php
/*
+----------------------------------------------------------------------+
| Autor: |
| Cláudio Diniz <http://phpbrasil.com/profile.php/user/cladiniz> |
| Modificado por: |
| Rodrigo Lima <rodrigofeitosa@gmail.com> |
+----------------------------------------------------------------------+
*/
function valor($v1, $v2, $op) {
$v1 = str_replace (".", "", $v1);
$v1 = str_replace (",", ".", $v1);
$v2 = str_replace (".", "", $v2);
$v2 = str_replace (",", ".", $v2);
switch ($op) {
case "+":
$r = $v1 + $v2;
break;
case "-":
$r = $v1 - $v2;
break;
case "*":
$r = $v1 * $v2;
break;
case "%":
$r = $v1 * $v2 + (1/100 * $v1 * $v2);
break;
}
$ret = number_format($r,2,",",".");
return $ret;
}
//Considerando um valor de 1.522,03 por 3 meses, onde $valor = 1.522,03 e $tempo = 3.

$valor_final = valor("$valor", "$tempo", "%");
echo "$valor_final";
?>
03/12/2012 12:17pm (~12 anos atrás)

Rodrigo Lima disse:
Prezados,

Fiz um update na função baseado nos ricos comentários dos desenvolvedores. Agora a função permite a escalabilidade com a utilização do number_format(), bem com o a soma de valores com uma e/ou duas casas decimais.

Divirtam-se.

<?php

function formataReais($valor1, $valor2, $operacao) {

/*
+----------------------------------------------------------------------+
| Função: formataReais() |
+----------------------------------------------------------------------+
| Descrição: Função que realiza operações monetárias e retorna o |
| resultado formatado na atual moeda brasileira: o Real(R$) |
+----------------------------------------------------------------------+
| Argumentos: |
| function formataReais ($valor1, $valor2, $operacao) |
| $valor1 = Primeiro valor da operação |
| $valor2 = Segundo valor da operação |
| $operacao = Tipo de operações possíveis . Pode ser : |
| "+" = adição, |
| "-" = subtração, |
| "*" = multiplicação |
+----------------------------------------------------------------------+
| Autor: |
| Cláudio Diniz <http://phpbrasil.com/profile.php/user/cladiniz> |
| |
| Data: |
| N/D |
+----------------------------------------------------------------------+
| Última alteração: |
| terça-feira, 24 de outubro de 2006 |
| |
| Modificado por: |
| Rodrigo Lima <rodrigofeitosa@gmail.com> |
+----------------------------------------------------------------------+
*/

// Antes de tudo , arrancamos os "," e os "." dos dois valores passados a função . Para isso , podemos usar str_replace :
$valor1 = str_replace (".", "", $valor1);
$valor1 = str_replace (",", ".", $valor1);

$valor2 = str_replace (".", "", $valor2);
$valor2 = str_replace (",", ".", $valor2);

// Agora vamos usar um switch para determinar qual o tipo de operação que foi definida :
switch ($operacao) {
// Adição :
case "+":
$resultado = $valor1 + $valor2;
break;

// Subtração :
case "-":
$resultado = $valor1 - $valor2;
break;

// Multiplicação :
case "*":
$resultado = $valor1 * $valor2;
break;

} // Fim Switch

$retorna = number_format($resultado,2,",",".");

// Por fim , retorna o resultado já formatado
return $retorna;
} // Fim da function

?>
24/10/2006 9:37am (~18 anos atrás)

Galera , como faço pra calcular valores em php? Eu uso <?
echo "R$ ".number_format($resultado, '2', ',', '.');
?> para mostrar no formato money mas quando faço o cálculo subtração o decimal só fica inteiro, não mostra as frações, e outra, para inserir no banco sql server preciso que o número vá para o banco assim : 10000000.25 e não assim: 10.000.000,25. Alguém pode me ajudar?
07/04/2006 9:11am (~18 anos atrás)

Bom, a função number_format() funciona perfeitamente. Além do que é melhor sempre fazer calculos com o formato numérico padrão do PHP e formatar com o number_format() apenas para a exibição.

Mas, como eu sou jovem, gosto de complicação...

Na parte que retira pontos e virgulas:

list ($valor1, $centavos) = split (',', $valor1);

if (strlen($centavos)<2) $centavos *= 10;

$valor1 = str_replace(".", "", $valor1);
$valor1 = $valor1 . $centavos;

Isso vai retirar os centavos do $valor1 e multiplicar por 10 caso tenha apenas uma casa decimal. Assim poderá ser enviada à função tanto 12,5 como 12,50 sem problemas. Depois se retira os pontos e concatena os centavos. Pensando em 12,5 ficaria: 1250

Faria o mesmo com o $valor2

Assim será possível fazer os operações de adição, subtração e multiplicação sem problemas.

Eu também substituiria o ultimo swicth pelo código abaixo:

//Retirando os centavos do $resultado
$centavos = substr("$resultado",-2,2);

//Dividindo por 100 e arredondando ppor baixo. Com isso ficaria apenas a parte antes do ponto
$resultado= $resultado / 100;
$resultado= floor($resultado);

//Colocando os pontos a cada 3 digitos for($n=3;$n<strlen($resultado);$n=$n+4) {
$resultado = substr_replace($resultado, '.', -$n, 0);
}

//Concatenando a parte antes da virgula com a parte após a virgula
$resultado = $resultado . "," . $centavos;


Acho que também dá pra melhorar incluindo a opração de divisão ("/"), usando a função round($resultado, 2) e novamente retirando a virgula com o str_replace().

Mas com certeza usar o number_format() é muito melhor.
08/12/2005 5:33am (~19 anos atrás)

Gostei bastante do artigo, mas concordo com a maioria... ele é meio complicado, poderia ser mais simples... e tem outra... fazendo dessa forma, dificulta o trabalho de controle de erros do formulário com javascript, por exemplo, que bloquearia a inserção de textos... esse tipo de função não dá pra usar mais... já q a vírgula é texto...

[]´s
26/03/2003 6:29am (~21 anos atrás)

Alex Monte disse:
$english_format_number = number_format($number);
21/06/2002 2:16pm (~22 anos atrás)

Alex Monte disse:
saudações, nunca programei em php mais acho q essa funcao poderia resolver ou nao qual seria o problema dela ??
<?php$english_format_number = number_format($number);?>

21/06/2002 2:14pm (~22 anos atrás)

Tenho um valor monetario ex.: 2045,0000
que trasforma-lo em 2045,00
08/06/2002 2:17pm (~22 anos atrás)

Lyma disse:
Um função para formatar a saída de um valor numérico é a "number_format()".

Eu a utilizo desta forma, para valores em Reais:

$Rvalor = R$ ' . number_format($valor, 2, ',', '.');

Isto coloca vírgula nas duas casas decimais (centavos) e ponto no milhar, produzindo o formato "R$ #.###,##".

Note: Não utilize $Rvalor para cálculos, pois pode causar erros.

Espero ter ajudado.

(post do Max no rau-tu)

Não desmerecendo o artigo, que é realmente muito bom para demonstrar que sempre existem MUITOS caminhos no PHP.
10/05/2002 7:35am (~22 anos atrás)

Rafael Kunst disse:
Certamente com a função number_format fica mais simples, porém a função postada pelo claudio eh otima para mostrar outro caminho, mostrando como funcionam os mecanismos da linguagem. E com certeza pode ser melhorada.
05/05/2002 7:09pm (~22 anos atrás)

Novo Comentário:

(Você pode usar tags como <b>, <i> ou <code>. URLs serão convertidas para links automaticamente.)