Introdução
Há algum tempo eu fiz um script em SED que escrevia por extenso um dado número inteiro. Para criar esse script, tive que quebrar um pouco a cabeça, devido a limitação do SED em não fornecer operações aritméticas, mas somente operações em string. Acabou que “criei” um método geral de conversão, que é muito semelhante ao que mostrarei hoje. A vantagem desse método é que ele trata o número de entrada como uma string, nos permitindo converter números extremamente grandes.
Hoje aprenderemos um algoritmo que tem por finalidade escrever por extenso um dado número de entrada.
Primeira parte: um trio por extenso
Primeiramente, vamos analisar como escrever por extenso um número menor que 1000, ou seja, todos os números na faixa [000, 999]. Como não há como gerar os valores por extenso por meio de algoritmos, o único jeito de recuperar esses valores é armazendo-os em algum lugar, tipo uma matriz. Em C, essa matriz é similar a:
/* A palavra dummy é para preencher uma posição para que cada linha tenha sempre 10 colunas. */ matrizextenso[4][10] = { {"dummy", "um", "dois", "três", ... , "nove"}, {"dez", "onze, "doze", "treze", ...., "dezenove", {"dummy", "dummy", "vinte", "trinta", ..., "noventa"}, {"dummy", "cento", "duzentos", "trezentos", ..., "novecentos"} }
Usando essa matriz, podemos facilmente escrever por extenso um número de três algarismos. Considere o seguinte número: 456. Escrevemos esse número por extenso somente acessando a matriz:
ex: 456 matrizextenso[0][6] = seis matrizextenso[2][5] = cinquenta matrizextenso[3][4] = quatrocentos
Observe que os algarismos do número indexam as colunas da matriz enquanto que as linhas são indexadas “manualmente”.
Para números que tem o ‘1’ na casas das dezenas, a indexação é ligeiramente diferente. Considere o número 712:
ex: 712 matrizextenso[1][2] = doze matrizextenso[3][4] = setecentos
A diferença é que o acesso pelo índice 1 substitui os acessos pelos índices 0 e 2.
Para números com algarismos zeros, a indexação naquela posição não deve ser feita.
ex: 103 matrizextenso[0][3] = três matrizextenso[3][1] = cento
O índice 2 não foi usado devido o número conter ‘0’ nas casas das dezenas. Outro exemplo:
ex: 002 matrizextenso[0][2] = dois
Infelizmente temos algumas exceções que devem ser tratadas separadamente. São elas: o número 100 (cem) e o 000 (zero).
Com essas regras, podemos montar um pequeno algoritmo para escrever um número de três dígitos por extenso:
definição: escrever_trio_extenso A entrada é um número da forma: cdu (as letras representam cada algarismo) A saída é guardada na variável saída Se trio == "100" : saída = "cem" senão se trio == "000" : saída = "zero" senão: se c != '0' : saída = matrizextenso[3][c] se d == '1': saída = saída + matrizextenso[1][u] senão: se d != '0' : saída = saída + matrizextenso[2][d] se u != '0' : saída = saída + matrizextenso[0][u] saída = adicionar_conjuncao_e_(saída)
Por fim, devemos adicionar a conjunção ‘e’, como em vinte ‘e’ três. Esse processo não é tão simples quanto parece, principalmente em linguagens que não suportam muitas operações em strings (ex: C). Aqui usei a função adicionar_conjuncao_e() que faz esse papel e sua implementação será ocultada.
Exemplo:
número: 406 Seguindo o algoritmo: saída = matrizextenso[3][4] = quatrocentos saída = saída + matrizextenso[0][6] = quatrocentos seis saída = adicionar_conjuncao_e(saída) = quatrocentos e seis
Qualquer número por extenso: um esboço
Um número grande por extenso nada mais é que conjuntos de três algarismos por extenso. Isso é, para obter esse número maior por extenso, basta dividi-lo em grupos de três e transformar cada grupo separadamente. Veja:
número: 123456789 Divida o número em trios: 789 : setecentos e oitenta e nove 456 : quatrocentos de cinquenta e seis 123 : cento e vinte e três
A saída final consiste na adição das classes (próximo tópico) e da conjunção ‘e’ ou vírgula. É bem mais prático considerar a divisão em trios de modo invertido, como mostra o exemplo, por facilitar as operações.
Segunda Parte: classes
O que chamo de classe aqui é o nome dado a cada elemento do conjunto formado pelas palavras: mil, milhão, bilhão, trilhão, etc. Mais uma vez, como não há como gerar o nome dessas classes dinamicamente, teremos que armazená-las em algum lugar, como um vetor.
*classextenso[] = {"dummy", "mil", "milh", "bilh", "trilh", ...., "vigesilh"}
Os nomes dessas classes foram obtidos da referência [1].
Os nomes das classes estão sem variação em número (plural e singular) para não precisarmos criar um vetor para classes no singular e outro no plural. O próximo passo do algoritmo será como decidir quando usar singular ou plural.
Antes de tudo, vejamos um exemplo:
número: 123 456 789 012 345 345 : trezentos e quarenta e cinco 0 012 : doze 1 789 : setecentos e oitenta e nove 2 456 : quatrocentos e cinquenta e seis 3 123 : cento e vinte e três 4
O número na última coluna nada mais é que um contador de posições. Esse número indexará no vetor de classes qual classe será usada naquele momento. Isso é, se usarmos esse número como índice no vetor classextenso, obteremos a classe desejada. Assim:
número: 123 456 789 012 345 345 : trezentos e quarenta e cinco 012 : doze classextenso[1] : mil 789 : setecentos e oitenta e nove classextenso[2] : milh 456 : quatrocentos e cinquenta e seis classextenso[3] : bilh 123 : cento e vinte e três classextenso[4] : trilh
OBS: Para contador igual a zero, nenhuma classe é mapeada (posição “dummy” do vetor).
Um algoritmo simples para obter as classes é:
Para cada trio do número em ordem inversa: contador = 0 Se contador > 0 e trio > 0 saída = classextenso[contador] contador = contador + 1
Terceira parte: plural ou singular
Algo que dificulta em muito a escrita por extenso é a variação de número das classes, pois ora ou outra temos que decidir se usamos, por exemplo, “milhões” ou “milhão”. Em geral, decidir entre esses dois casos é simples: se o trio em questão for igual a ‘1’ usa-se singular, caso contrário usamos plural. Observe que só colocamos plural para classes acima de mil, pois a classe mil sempre vem no singular.
Com isso, para decidirmos se usamos plural ou singular, fazemos:
Para cada trio do número em ordem inversa: contador = 0 Se contador > 0 e trio > 0 saída = classextenso[contador] se contador > 1 se trio > 1 : saída = saída + "ões" senão : saída = saída + "ão" contador = contador + 1
O algoritmo nos diz que só acessamos as classes se o contador for maior que 0, e adicionamos plural ou singular se o contador for maior que 1 (classes acima de mil).
Exemplos:
número: 1789415234001 001 : um 0 234 : duzentos e trinta e quatro 1 : classextenso[1] = mil 415 : quatrocentos e quinze 2 : classextenso[2] = milh + "ões" (415 != 1) 789 : setecentos e oitenta e nove 3 : classextenso[3] = bilh + "ões" (789 != 1) 001 : um 4 : classextenso[4] = trilh + "ão" (001 == 1)
Juntando tudo, temos:
um duzentos e trinta e quatro mil quatrocentos e quinze milhões setecentos e oitenta e nove bilhões um trilhão
Agora só basta juntar as linhas para formar a saída completa. Essa junção é feita pelo conectivo ‘e’ ou pela vírgula, e isso nos leva ao próximo passo.
Quarta Parte: junção dos extensos
De acordo com o gerador de extensos em [2], temos dois modos de juntar os extensos dos trios. Uma é usando ‘e’ e outra, a vírgula.
Basicamente, para cada trio não nulo de um número (exceto o último) e começando do trio mais à direita: se o trio à esquerda for o “000”, usamos o ‘e’. Se o trio dado for menor que 100 também usamos o ‘e’. Caso contrário, para trios maiores ou iguais a 100, usamos a ‘,’.
Espero não ter complicado muito. A seguir um exemplo para esclarecer essa regra:
Exemplo:
número: 1000420055333 001 000 420 055 333 | | | | | | | v | | | ',', pois 055 != 0 e 333 >= 100 | | v | | 'e', pois 420 != 0 e 055 < 100 | v | 'e', pois 000 == 000 v 'e', pois 001 != 000 e 001 < 100
Último passo: algoritmo geral
Agora vamos juntar tudo que vimos e elaborar um algoritmo geral para a escrita por extenso.
saída= extensofinal= contador=0 Para cada trio do número em ordem inversa: se trio > 0 saída = escrever_trio_extenso(trio) se contador > 0 saída = saída + " " + classextenso[contador] se contador > 1: se trio > 1 : saída = saída + "ões" senão : saída = saída + "ão" se nao_e_ultimo_trio() se trio_a_esquerda_eq_zero() saída = " e " + saida senão se trio >= 100 saída = ", " + saída senão saída = " e " + saída extensofinal = saída + extensofinal contador = contador + 1 imprima(extensofinal)
Note que há três funções nesse algoritmo. A escrever_trio_extenso() é a função definida para escrever um trio por extenso. A função nao_e_ultimo_trio() retorna verdadeiro se o trio analisado não é o último trio do número – o trio mais à esquerda. A outra função, trio_a_esquerda_eq_zero(), retorna verdadeiro se o trio à esquerda do trio atual não for zero. Essas duas funções só existem devido a regra de junção de extensos e suas implementações serão ocultadas aqui.
Esse algoritmo pode ser implementado na maioria das linguagens de programação, especialmente àquelas que fornecem um alto poder de tratamento de strings (ex: regex). Para algumas linguagens, algumas restrições devem ser acrescentadas no algoritmo, por exemplo em C, em que o acesso ao vetor classsextenso deve ser controlado para não gerar erro de leitura em memórias não acessíveis.
O script: dextenso.sh
A seguir um shell script que utiliza o algoritmo anterior para escrever um número de entrada por extenso.
#!/bin/bash # [dextenso.sh] # Retorna um número de entrada por extenso. # # [Autor] # Marcos Paulo Ferreira (Daemonio) # undefinido gmail com # https://daemoniolabs.wordpress.com # # [Codificação] # Script : UTF-8 # Saída : UTF-8 # # [Uso] # $ chmod +x dextenso.sh # $ ./dextenso.sh <numero> # # ex: # $ ./dextenso.sh 48000069 # quarenta e oito milhões e sessenta e nove # # Versão 1.0, by daemonio @ Sun Jun 24 15:19:30 BRT 2012 # # # Variáveis globais # # Array com os números em extenso. # OBS: A palavra dummy é só um marcador de # lugar que assegura uma indexação correta. trioextenso=( "dummy" "um" "dois" "três" "quatro" "cinco" "seis" "sete" "oito" "nove" "dez" "onze" "doze" "treze" "quatorze" "quinze" "dezesseis" "dezessete" "dezoito" "dezenove" "dummy" "dummy" "vinte" "trinta" "quarenta" "cinquenta" "sessenta" "setenta" "oitenta" "noventa" "dummy" "cento" "duzentos" "trezentos" "quatrocentos" "quinhentos" "seiscentos" "setecentos" "oitocentos" "novecentos" ) # Array com as classes em extenso. classextenso=( "dummy" "mil" "milh" "bilh" "trilh" "quatrilh" "quintilh" "sextilh" "septilh" "octilh" "nonilh" "decilh" "undecilh" "duodecilh" "tredecilh" "quatordecilh" "quindecilh" "sexdecilh" "setedecilh" "octodecilh" "novedecilh" "vigesilh" ) # # Funções # # Escreve um trio por extenso. function escrever_trio_extenso { local trio=$1 local saida= local u= d= c= t= # Obtém cada algarismo do trio. c=${trio:0:1}; d=${trio:1:1}; u=${trio:2:1} case "$trio" in 100) saida='cem' ;; [0-9]1[0-9]) # Números da forma: x1x. t=$((10+$u)); saida=${trioextenso[$t]} [ "$c" != '0' ] && t=$((30+$c)) && saida=${trioextenso[$t]}' '$saida ;; *) # Qualquer número, exceto 100 e os da forma x1x. [ "$u" != '0' ] && saida=${trioextenso[$u]} [ "$d" != '0' ] && t=$((20+$d)) && saida=${trioextenso[$t]}' '$saida [ "$c" != '0' ] && t=$((30+$c)) && saida=${trioextenso[$t]}' '$saida ;; esac # Adiciona a conjução 'e'. Basicamente insere # a string " e " no lugar dos espaços que não # estejam no início ou no fim. saida=$(sed 's/^ *//;s/ *$//;s/ / e /g'<<<"$saida") echo "$saida" } # Retorna verdadeiro se o trio dado # não é o último. Para isso, ela recebe # o número dado no primeiro parâmetro e # o contador no segundo. function nao_e_ultimo_trio { local numero=$1 local contador=$2 (( $contador < ((${#numero}/3)-1) )) } # Retorna verdadeiro se o trio à esquerda # do trio dado não é zero. Para isso, ela # recebe o número dado no primeiro # parâmetro e o contador no segundo. function trio_esquerda_eq_zero { local numero=$1 local contador=$2 local t=$(( ${#numero} - 3- 3*(contador+1))) (( 10#${numero:$t:3} == 0 )) } # Função principal. Ela representa o algoritmo final # visto no post. function dextenso { local numero=$1 local contador=0 local saida= local extensofinal= local t= # Retira os zeros iniciais numero=$(sed 's/^0*//'<<<$numero) # Número zero. [ -z $numero ] && echo zero && return 0 # Padding para que a quantidade de algarismos desse # número seja múltipla de três. t=$((3-${#numero}%3)) [ $t != 3 ] && numero=$(echo '000' | cut -c1-$t)$numero # Para cada trio na ordem inversa do número... for trio in $(echo "$numero" | rev | sed 's/.../&\n/g') do # Obtém a ordem normal do trio usando o comando # rev novamente. trio=$(rev<<< "$trio") if [ "$trio" -ne 0 ] then # Extenso de um trio. saida=$(escrever_trio_extenso $trio) # Classes + plural. if [ "$contador" -gt 0 ] then saida="$saida"' '"${classextenso[$contador]}" if [ "$contador" -gt 1 ] then [ "$trio" -gt 1 ] && saida="$saida"'ões' || saida="$saida"'ão' fi fi # Junção. if nao_e_ultimo_trio $numero $contador then if trio_esquerda_eq_zero $numero $contador then saida=" e "$saida elif [ $trio -ge 100 ]; then saida=", "$saida else saida=" e "$saida fi fi # Monta a saída. extensofinal="${saida}${extensofinal}" fi let contador++ done # Fim do algoritmo. Mostra a saída. echo "$extensofinal" } function show_help { echo 'dextenso - by daemonio' echo '[uso] ./dextenso.sh <numero>' echo exit 1 } # # Main # # Checa parâmetros. [ -z "$1" ] && show_help # Retorna '2' se o parâmetro passado # não é um número. [[ "$1" =~ ^[0-9]*$ ]] || exit 2 # Chama a função, passando o número de # entrada como parâmetro. dextenso $1 #EOF
Exemplos:
$ chmod +x dextenso.sh $ ./dextenso.sh 78991100101 setenta e oito bilhões, novecentos e noventa e um milhões, cem mil, cento e um $ ./dextenso.sh 1000450002578 um trilhão e quatrocentos e cinquenta milhões e dois mil, quinhentos e setenta e oito $ ./dextenso.sh 1989 um mil, novecentos e oitenta e nove
Conclusão
A vantagem do método apresentado é que ele considera o número de entrada como uma string, e graças a isso, não estamos limitados às restrições que determinas linguagens impõem no tratamento de números, como tamanho máximo para não ocorrer overflow.
Vale ressaltar mais uma vez que o método apresentado foi inteiramente baseado na saída do gerador do link [2], e é provável que eu tenha deixado alguma coisa passar (ou me baseado na referência errada). Se você encontrou algum erro na saída do script, por favor o relate nos comentários.
Referências
[1] Escalas curta e longa by wipedia (Acessado em: Junho/2012)
http://pt.wikipedia.org/wiki/Escalas_curta_e_longa
[2] Calculadora de Números Decimais por Extenso by Matemática Didática (Acessado em: Junho/2012)
http://www.matematicadidatica.com.br/CalculadoraNumerosDecimaisPorExtenso.aspx
Pingback: Código De Número Por Extenso Em Java | Daemonio Labs
Pingback: Biblioteca em C: Números por Extenso | Daemonio Labs
Muito bom o Post, estou começando o curso de TADS e vai me ajudar bastante. Mas o link da biblioteca não esta disponível poderia indicar outro link pra baixá-lo?
Olá Laiton,
que bom que tenha gostado. A biblioteca você pode encontrá-la aqui:
são dois arquivos (.h e .c) mais um arquivo de teste.
Qualquer problema só avisar. Abraços
Fantástico era isso que eu precisava parabéns!
Obrigado, André.
Pingback: Classe Python de Números Por Extenso | Daemonio Labs
Um algorítimo muito bom, parabéns.
Verifiquei que há implementação deste algorítimo, no site de vocês, em C, phyton e Java. Como precisava em VB6, eu mesmo implementei.
Segue abaixo a função em VB6. OBS: Eu preferi usar todas as “partes” do algorítimo em um função unica e não usei exatamente a mesma nomenclatura. No geral, a lógica é a mesma e está muito parecido com o original.
[ ]s
OBS: O HTML removeu o sinal “Menor Maior”. onde se lê por exemplo
(d “0”), leia-se (d != “0”). O sinal “!=” nao pode ser usado no VB6 para representar “diferente”.
MUITO BOM Sidney. Coloquei seu código no pastebin:
http://pastebin.com/tcnE4CPF
não sei se ele está correto. Apenas coloquei o sinal de diferente (sinal de menor + sinal de maior) nas linhas 45, 54 e 70. Veja se é isso mesmo. Se tiver errado eu corrijo.
O wordpress consome automaticamente algumas tags em html por isso o problema.
Abraços
tem ele em C sem o usa da biblioteca?
Olá,
a biblioteca é só para modular o programa para deixá-lo mais flexível e de fácil manutenção. O código da biblioteca também
se encontra na postagem.
Muita gente tem dificuldade de compilar mais de um arquivo nos compiladores Windows, se esse for seu caso, copie todos os códigos (biblioteca (.c) + o principal) e cole em um só arquivo e depois mande compilar. Isso é só uma dica, não deve ser considerada como regra, pois o mais comum é compilar vários fontes em um dado projeto.