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