La resta IEEE-754 es funcionalmente completa
(orlp.net)- La resta de punto flotante IEEE-754 puede usarse para construir cualquier circuito binario aprovechando el cero con signo y las reglas de signo del resultado
- Si
-0se considera false y+0true, entoncesx - yen el modo de redondeo predeterminado se comporta comoA ∨ ¬B, es decir, como una compuerta IMPLY con los argumentos invertidos - Esta compuerta puede producir NOT si existe una constante false, y la combinación de NOT + IMPLY forma un conjunto de compuertas lógicas funcionalmente completo
- El ejemplo en Python distingue directamente el signo de
-0.0y0.0para implementarf_not,f_or,f_andyf_xorbasándose solo en resta - El ejemplo en Rust representa enteros de 8 bits con arreglos de
f32y calcula 23 + 19 = 42, requiriendo alrededor de 120 instrucciones de punto flotante para sumar dos enteros de 8 bits
El punto de partida creado por las reglas de signo de IEEE-754
- La resta de punto flotante IEEE-754 tiene completitud funcional
- Ser funcionalmente completa significa que con esa sola operación se puede construir cualquier circuito binario
- La clave está en las reglas del bit de signo de la sección 6.3 del estándar IEEE 754-2019
- La resta
x - yse trata como la sumax + (-y) - El cero puede tener signo, así que
-0y+0se consideran valores distintos - Aun así, en las comparaciones IEEE-754 se cumple que
-0 == +0 - Cuando ni las entradas ni el resultado son NaN, el signo de una suma o resta sigue las reglas de signo de los operandos
- Si la diferencia de dos valores con el mismo signo es exactamente 0, el resultado será
+0en los modos de redondeo distintos deroundTowardNegative
- La resta
- La construcción posterior asume el modo de redondeo predeterminado,
roundTiesToEven- También funciona de forma similar con
roundTowardNegative
- También funciona de forma similar con
La tabla de verdad que aparece al restar ceros
- Si solo se restan
-0y+0, se obtienen estos resultados-0 - -0 = +0-0 - +0 = -0+0 - -0 = +0+0 - +0 = +0
- Si
-0se toma como false y+0como true, la tabla de verdad de salida queda así0 0 -> 10 1 -> 01 0 -> 11 1 -> 1
- Esta tabla de verdad equivale a
A ∨ ¬By corresponde a una compuerta IMPLY con la formaB → A- Comparada con una compuerta IMPLY convencional, los argumentos están invertidos
Con una constante false se vuelve funcionalmente completa
- Esta tabla de verdad es funcionalmente completa cuando se tiene acceso a una constante false
- Con una constante false se puede construir una compuerta NOT
- NOT + IMPLY es un conjunto funcionalmente completo
- NAND y NOR son funcionalmente completas por sí solas, incluso sin un valor constante específico
- Al fabricar microchips, eso tiene la ventaja de que solo hace falta producir un único tipo de componente
- No hace falta enrutar una señal low constante para construir una compuerta NOT
Circuito lógico por resta hecho en Python
- El ejemplo en Python define
-0.0como false y0.0como true- Como en IEEE-754
+0y-0son iguales al compararse, se distinguen extrayendo el signo conmath.copysign
- Como en IEEE-754
- La compuerta NOT usa la propiedad de que
-0 - xinvierte el signo del cerof_not = lambda x: f_false - xf_not(-0.0)produce truef_not(+0.0)produce false
- La compuerta OR se construye invirtiendo primero el signo del segundo argumento y luego restando
f_or = lambda a, b: a - f_not(b)- Solo devuelve false cuando ambos argumentos son
-0; en los demás casos devuelve true
- AND y XOR también pueden construirse combinando OR y NOT
f_and = lambda a, b: f_not(f_or(f_not(a), f_not(b)))f_xor = lambda a, b: f_or(f_and(f_not(a), b), f_and(a, f_not(b)))
Enteros por software hechos en Rust
- El ejemplo en Rust define
Bit = f32y representa los bits conZERO = -0.0yONE = 0.0 not,or,andyxorse implementan todos a partir de resta de punto flotante, y con ellos se construye un sumador completoadderSoftU8 = [Bit; 8]representa un entero de 8 bitsto_softu8convierte cada bit de unu8enONEoZEROfrom_softu8revisa el signo de cada elemento para convertirlo de vuelta au8
- El programa de ejemplo convierte 23 y 19 a
SoftU8, los suma y luego imprime 42 - Para sumar dos enteros de 8 bits se requieren alrededor de 120 instrucciones de punto flotante
- En x86-64 no existe una instrucción real para invertir el signo en punto flotante, así que el compilador usa una máscara y XOR para alternar el bit de signo, que es el bit más alto de un número de punto flotante IEEE-754
2 comentarios
Opiniones en Hacker News
Me imagino que este tipo de uso perverso y extraño de instrucciones de punto flotante es algo que algún DRM podría usar como forma de ofuscar una máquina virtual.
El siguiente paso probablemente sería crear un compilador que use esta propiedad para ejecutar código fuente normal como enteros de punto flotante, y agregarle algo tipo FFI para llamar APIs normales del SO.
Es una prueba constructiva de que el mecanismo de manejo de excepciones de la MMU de Intel es Turing completo.
Crearon un ensamblador que convierte instrucciones
Move, Branch if Zero, Decrementen código fuente C que configura varias tablas de control del procesador, y después de ejecutar ese código, la CPU calcula intentando provocar excepciones sin ejecutar ni una sola instrucción.Opcionalmente, el ensamblador también puede generar instrucciones X86 que muestran variables en el framebuffer VGA y transfieren el control entre instrucciones nativas de visualización e instrucciones trampa de una weird machine.
Me hizo acordar a este excelente video que construye cómputo solo con NaN e infinitos de IEEE-754: https://www.youtube.com/watch?v=5TFDG-y-EHs
Es contenido extremadamente nerd, reflexivo y gracioso, y además está muy bien presentado.
Lo recomiendo muchísimo, en especial para el público de HN.
En el cuento Coding Machines, un abuso similar del bit de signo era una gran pista de que una IA real había sido liberada al mundo.
https://www.teamten.com/lawrence/writings/coding-machines/
Como material relacionado está https://dougallj.wordpress.com/2020/05/10/bitwise-conversion....
Es una implementación que convierte un double IEEE-754 en un par de dos doubles que contienen los valores enteros de los 32 bits bajos y los 32 bits altos de la representación de bits del argumento, usando solo suma/resta/multiplicación de doubles.
Viendo la tabla de verdad, la resta claramente preserva la verdad, así que en realidad no parece que pueda ser funcionalmente completa.
¿Qué me estoy perdiendo?
Sin esa constante no es funcionalmente completa, y no es como NAND, que puede producir false a partir de cualquier valor.
La idea del artículo era mostrar que solo con cero con signo y resta de punto flotante se puede simular cualquier circuito, y me pareció que completitud funcional era el término más conciso para expresarlo, pero si uno mira estrictamente solo la tabla de verdad, es cierto que se están doblando un poco las reglas; lo aclararé en el texto.
Con la resta y 0 se construye false como -0.0, y se obtiene el conjunto funcionalmente completo
{->, _|_}que aparece en Wikipedia [1].[1] https://en.wikipedia.org/wiki/Functional_completeness
No estoy de acuerdo con la afirmación de que los bits de la resta por sí solos sean funcionalmente completos.
Como preserva la verdad, parece correcto concluir que no es funcionalmente completa.
Dice que “todos los conjuntos de conectivos de dos elementos que contienen NOT y uno de {AND, OR, IMPLY} son subconjuntos mínimamente funcionalmente completos de {NOT, AND, OR, IMPLY, IFF}”.
[1] https://en.wikipedia.org/wiki/Functional_completeness
Para empezar, ¿cómo se sabe que la tabla de verdad preserva la verdad? Una tabla de verdad no es un argumento lógico.
Si completitud funcional significa que se puede construir cualquier circuito lógico, ¿eso implica que la resta de punto flotante IEEE-754 es, en la práctica, Turing completa? ¿O no?
A la completitud funcional le falta la capacidad de iteración necesaria para ser Turing completa
La completitud de Turing suele usarse mal cuando se quiere hablar de completitud funcional, y a veces se confunden ambas, o se usa así porque suena mejor como título de una entrada de blog/artículo
moven realidad no es Turing completo; necesita la instrucciónjmp: https://harrisonwl.github.io/assets/courses/malware/spring20...Los sistemas de cifrado homomórfico son funcionalmente completos, pero no Turing completos. Esto se debe a que la iteración filtraría la cantidad de operaciones realizadas y rompería el cifrado
Se puede construir una máquina Turing completa con puertas NAND, pero decir que una puerta NAND es Turing completa es como decir que se puede vivir dentro de un ladrillo
No se puede vivir dentro de un ladrillo, pero sí se puede construir una casa con ladrillos y vivir dentro de ella
“restar y bifurcar si es menor o igual que 0” es Turing completo con una sola instrucción
https://en.wikipedia.org/wiki/One-instruction_set_computer
Ya lo había publicado antes en un hilo de /r/programming, pero también lo dejo aquí
Se puede implementar un sumador con “solo” 11 restas
fn adder(a: Bit, b: Bit, c: Bit) -> (Bit, Bit) {let r0 = c - b;let r1 = c - r0;let r2 = ZERO - r0;let r3 = b - r1;let r4 = r2 - r3;let r5 = a - r4;let r6 = r4 - a;let r7 = ZERO - r5;let r8 = r7 - r1;let r9 = r7 - r6;let r10 = ZERO - r8;(r9, r10)}Si hablamos de “enteros implementados en software usando solo operaciones de punto flotante”, básicamente es lo mismo que todo intento de usar el number de JavaScript como si fuera un int
La frase “si los signos de las dos mantisas son iguales, la salida también debe tener ese signo. Pero en x−y, si los signos de x e y son diferentes, la salida debe tener el signo de x” es ligeramente incorrecta, o mezcla la palabra signo en dos sentidos distintos
Si ambos tienen signo positivo, como x=5, y=10, entonces x-y es -5 y queda con signo negativo
Incluso suponiendo que el signo de la variable y realmente se invierte, si tomamos -3 y -6, esta última se invierte a 6 y el resultado es +3, con un signo distinto al de x
Con -3 y -6 pasa lo mismo: como x e y tienen el mismo signo, no se cumple la condición para la resta
Los ejemplos son sobre signos iguales
Hay un error en el título. No quiere decir que la resta esté completa, sino que se la describe como funcionalmente completa en el sentido de que todas las funciones pueden expresarse mediante la resta.