ElBlo
Enteros × Flotantes
Un truco que hace uso y abuso de los números subnormales.
Allá por el año 2020, un compañero docente me mostró una curiosidad que encontró en uno de sus grupos de trabajo práctico: hacer operaciones de punto flotante con números enteros daba resultados correctos para ciertos números.
No, no hablo de multiplicar enteros por 0 😒.
El siguiente ejemplo en ASM debería dejarlo más claro:
section .text
global _start
_start:
mov eax, 42
movd xmm0, eax ; xmm0 tiene el entero 42.
mov eax, 10
cvtsi2ss xmm1, eax ; xmm1 tiene el float 10.0f
; Multiplicacion de punto flotante.
; Uno de los operandos es entero y el otro punto flotante.
mulss xmm0, xmm1
movd eax, xmm0 ; eax == 420 (entero)
ret
Y un ejemplo en C, aunque sea comportamiento indefinido:
#include<stdio.h>
#include<stdint.h>
union {
int32_t a;
float b;
} val;
int main() {
val.a = 15;
val.b *= 0.2f;
printf("val: %d\n", val.a); // imprime 3
return 0;
}
Esto funciona para todos los enteros menores a $2^{23}$.
Representación Punto Flotante 101
Los 23 bits menos significativos de un número en punto flotante de precisión simple nos sirven para identificar la mantisa, los siguientes 8 bits nos dicen cuál va a ser el exponente y luego tenemos un bit para el signo. Un número positivo con exponente mayor a 0 se calcula como: $2^{exponente - 127}\ (1 + \frac{mantisa}{2^{23}})$.
Números Subnormales
Cuando un número en punto flotante tiene exponente $0$, se dice que es un número subnormal. Se escribe como $2^{-126} \frac{mantisa}{2^{23}}$
Los números enteros menores a $2^{23}$, si los interpretamos como un float, van a ser números positivos subnormales, ya que su exponente será 0. Su mantisa será exactamente igual al número.
Entonces, tenemos nuestro número, por ejemplo $15$, y queremos dividirlo por $3.0$. Si interpretamos nuestro $15$ como punto flotante obtenemos $2^{-126} \frac{15}{2^{23}}$
Así que la cuenta que queremos hacer es:
$$ \frac{2^{-126}\frac{15}{2^{23}}}{3} $$
Didiviendo ${15}$ por $3$ nos queda:
$$ 2^{-126} \frac{3}{2^{23}} $$
Y este es un número que si lo escribimos como punto flotante, tiene el bit de signo en $0$, el exponente en $0$ (es subnormal), y la mantisa vale $3$, haciendo que el número se corresponda con la representación del entero $3$.
Esto vale siempre y cuando el resultado de la operatoria pueda escribirse como un número subnormal.
¿Qué uso le podemos dar a esto? No muchos, esto es más bien una curiosidad, y estamos bastante limitados por el hecho de que tanto el número original como el resultado deben ser menores a $2^{23}$. Para colmo, las unidades de punto flotante de los procesadores suelen tener implementaciones muy lentas cuando trabajan con números subnormales, del orden de los 100 ciclos de procesador por operación1.