En el desarollo con Lua 5.1, el tipo de dato number es el estándar para representar valores numéricos. Sin embargo, debido a su naturaleza basada en coma flotante, existe un límite crítico de seguridad al trabajar con enteros de gran magnitud. Para solucionar esto en entornos de Unity, xLua proporciona una implementación específica para enteros de 64 bits (uint64).
El límite de precisión en el tipo number
Por defecto, Lua 5.1 utiliza el estándar IEEE 754 de doble precisión para sus números. Esto implica una estructura de 8 bytes (64 bits) distribuidos de la siguiente forma:
- Bit de signo: 1 bit (determina si el valor es positivo o negativo).
- Exponente: 11 bits (define el rango del número).
- Mantisa (Fracción): 52 bits (define la precisión del número).
El "entero máximo seguro" se alcanza cuando la mantisa está completamente llena y el exponente está configurado para representar valores enteros sin decimales. Este valor es 2^53 - 1, equivalente a 9,007,199,254,740,991. Cualquier operación que supere este umbral corre el riesgo de perder precisión, ya que no hay suficientes bits en la mantisa para representar cada unidad de forma única.
Consecuencias de superar el límite de seguridad
Cuando un cálculo excede el valor máximo seguro, Lua no lanza un error de desbordamiento, sino que comienza a redondear a la representación representable más cercana. Esto provoca que diferentes operaciones matemáticas resulten en el mismo valor final, como se observa en el siguiente ejemplo:
local basePrecisa = 9007199254740991
-- Intentamos incrementar el valor más allá del límite seguro
for incremento = 1, 6 do
local resultado = basePrecisa + incremento
print(string.format("Resultado: %.0f", resultado))
end
Al ejecutar este código, notarás que los resultados empiezan a duplicarse o a saltarse números correlativos. Esto sucede porque el salto mínimo representable en coma flotante se vuelve mayor que 1 después de cruzar el umbral de los 53 bits.
Implementación de uint64 en xLua
Para aplicaciones que requieren identificadores únicos de 64 bits (como IDs de usuario de bases de datos o balances de moneda virtual), xLua incluye una librería en C (i64lib.c) que permite manipular enteros de 64 bits reales. Esta librería se carga automáticamente en el entorno de xLua mediante LuaAPI.luaopen_i64lib.
A continuación, se detallan las operaciones principales disponibles en la interfaz de uint64:
uint64.parse(str): Convierte una cadena de texto en un objeto uint64. Es fundamental usar strings para evitar la pérdida de precisión previa en Lua.uint64.compare(a, b): Realiza una comparación numérica entre un uint64 y otro valor.uint64.divide(a, b)/uint64.remainder(a, b): Operaciones de división y módulo.uint64.tostring(val): Convierte el valor de 64 bits a una cadena legible.
Además, xLua sobrecarga los metamétodos aritméticos tradicionales para que los objetos uint64 funcionen de forma natural con los operadores estándar (+, -, *, /, ==, %, ^).
Ejemplo práctico de transición a uint64
Si tu lógica detecta que un número está a punto de superar el límite de precisión, es recomendable delegar el cálculo a la librería de 64 bits para mantener la integirdad de los datos:
local umbralSeguridad = 9007199254740991
local contadorActual = 9007199254740990
for offset = 1, 8 do
local sumaTemporal = contadorActual + offset
if sumaTemporal >= umbralSeguridad then
-- Convertimos a uint64 para preservar la precisión
local bigInt = uint64.parse(string.format("%.0f", contadorActual))
local resultadoExacto = bigInt + uint64.parse(tostring(offset))
print("[Exacto uint64]: " .. uint64.tostring(resultadoExacto))
else
-- Operación estándar de Lua
print("[Estándar Number]: " .. string.format("%.0f", sumaTemporal))
end
end
Este enfoque garantiza que, una vez que la precisión del tipo nativo de Lua se ve comprometida, el sistema conmute a una representación robusta de 64 bits, evitando errores lógicos en sistemas críticos como inventarios o sistemas de posicionamiento global.