¿Cómo convertir de números a letras?

Tabla de contenidos

Un reto que normalmente se encuentra al desarrollar, es el ¿Cómo convertir de números a letras?. El cual es un requerimiento habitual en sistemas administrativos y/o financieros.

A continuación se presenta una solución escrita en C#, tomando algunas ventajas del lenguaje disponibles a partir de la versión 7.  Básicamente, lo que se hace es dividir el número es sus diferentes partes: millones, miles, centenas, decenas y unidades, considerando que el español tiene sus particularidades y utilizando recursividad.

Consideraciones

Todos los números se conforman utilizando una serie de palabras, las cuales se repiten en los millones y miles, por lo que se manejan por medio de arreglos. Se podría utilizar una solución más elegante por medio de diccionarios; sin embargo, para esta versión se utilizarán arreglos.

El método pertenecerá a una clase estática y tendrá 4 parámetros: el número en decimal, la moneda a utilizar (colones, dólares, pesos, etc.), si el resultado se expresa en mayúsculas y si se procesan los decimales. Este último se pone en falso cuando el método se llama a sí mismo y es el parámetro encargado de indicar cuando poner la moneda y los decimales en la hilera final.

C# 7.0

Dentro del código se usan las facilidades de la versión 7 de C#, permitiendo expresiones numéricas tables como enteros / 1_000_000 que facilitan la lectura del código.

Restricciones

  • No se contempla en manejo de números negativos.
  • La conversión maneja hasta millones.

Algoritmo para convertir de números a letras

  • Se separa el número a convertir en sus partes, enteras y decimales. Se trabaja con la parte entera y se utiliza la parte decimal al final, cuando hay que indicar
  • Se valida que el parámetro de moneda no sea nulo, de otro modo dispara un error.
  • Si el número es mayor al millón
    • Se establece la palabra millón si es uno solo y millones si es más de uno.
    • Se llama al mismo método con el número entero dividido entre 1 millón y se agrega el texto.
    • Se agrega el texto millón o millones, determinado en el primer paso.
    • Se restan los millones a la parte entera del número original
  • Si el número es mayor a mil
    • Se agrega la palabra mil si es uno solo y sino se llama al mismo método con el número entero dividido entre mil y se agrega al texto.
    • Si en la descripción ya había texto se agrega un espacio en blanco.

Descarga

El código completo se encuentra disponible en Gists; sin embargo, a continuación se adjunta una copia del mismo.

public static class NumerosLetras {

    static string[] unidades = new string[] {
      "cero", "un", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve", "diez",
      "once", "doce", "trece", "catorce", "quince", "dieciseis", "diecisiete", "dieciocho", "diecinueve", "veinte" };
    static string[] decenas = new string[] {
      "cero", "diez", "veinti", "treinta", "cuarenta", "cincuenta", "sesenta", "setenta", "ochenta", "noventa", "cien" };
    static string[] centenas = new string[] {
      "cero", "ciento", "doscientos", "trescientos", "cuatrocientos", "quinientos", "seiscientos", 
      "setecientos", "ochecientos", "novecientos", "mil" };

    /// <summary>
    /// Convertir un número a su equivalente en palabras
    /// </summary>
    /// <param name="numero">Número decimal a convertir</param>
    /// <param name="moneda">Moneda a utilizar</param>
    /// <param name="mayusculas">El resultado se expresa en mayúscula</param>
    /// <param name="procesaDecimales">Se deben procesar los decimales</param>
    /// <returns>La hilera correspondiente al numero</returns>
    public static string Convertir(
      decimal numero,
      string moneda,
      bool mayusculas = true,
      bool procesaDecimales = true
    ) {
      string letras = string.Empty;
      string espacio;

      int enteros = (int)Math.Floor(numero);
      int decimales = (int)(Math.Round(numero - enteros, 2) * 100);

      if (string.IsNullOrWhiteSpace(moneda)) {
        throw new ArgumentNullException(nameof(moneda));
      }

      if (enteros / 1_000_000 > 0) {
        string textoMillones;
        if (enteros / 1_000_000 == 1) {
          textoMillones = "millón";
        } else {
          textoMillones = "millones";
        }

        letras = $"{Convertir(enteros / 1_000_000, moneda, mayusculas, procesaDecimales: false)} {textoMillones}";
        enteros %= 1_000_000;
      }

      if (enteros / 1_000 > 0) {
        espacio = letras.Length > 0 ? " " : "";
        if (enteros / 1_000 == 1) {
          letras += $"{espacio}{centenas[10]}";
        } else {
          letras += $"{espacio}{Convertir(enteros / 1_000, moneda, mayusculas, procesaDecimales: false)} {centenas[10]}";
        }
        enteros %= 1_000;
      }

      if (enteros / 100 > 0) {
        espacio = letras.Length > 0 ? " " : "";
        if (enteros / 100 == 1 && enteros % 100 == 0) {
          letras += $"{espacio}{decenas[10]}";
        } else {
          letras += $"{espacio}{centenas[enteros / 100]}";
        }
        enteros %= 100;
      }

      if (enteros <= 20) {
        if (enteros > 0) {
          espacio = (letras.Length > 0) ? " " : "";
          letras += $"{espacio}{unidades[enteros]}";
        }
      } else {
        espacio = letras.Length > 0 ? " " : "";
        if (enteros / 10 == 2) {
          letras += $"{espacio}{decenas[enteros / 10]}{Convertir(enteros % 20, moneda, mayusculas, procesaDecimales: false)}";
        } else if (enteros % 10 == 0) {
          letras += $"{espacio}{decenas[enteros / 10]}";
        } else {
          letras += $"{espacio}{decenas[enteros / 10]} y {Convertir(enteros % 10, moneda, mayusculas, procesaDecimales: false)}";
        }
      }

      if (string.IsNullOrWhiteSpace(letras)) {
        letras = "cero";
      }

      if (procesaDecimales) {
        letras += $" {moneda} con {decimales:00}/100"; ;
      }

      return mayusculas ? letras.ToUpper() : letras.ToLower();
    }  // Convertir

  }  // class