- La soustraction en virgule flottante IEEE-754 permet de construire n’importe quel circuit binaire grâce au zéro signé et aux règles de signe du résultat
- Si l’on considère
-0comme false et+0comme true, alors en mode d’arrondi par défaut,x - yse comporte commeA ∨ ¬B, c’est-à-dire comme une porte IMPLY avec les arguments inversés - Avec une constante false, cette porte permet de construire un NOT, et la combinaison NOT + IMPLY forme alors un ensemble de portes logiques fonctionnellement complet
- L’exemple Python distingue directement le signe de
-0.0et0.0pour implémenterf_not,f_or,f_and,f_xoruniquement à partir de soustractions - L’exemple Rust représente des entiers 8 bits avec des tableaux de
f32, calcule 23 + 19 = 42, et nécessite environ 120 instructions en virgule flottante pour additionner deux entiers 8 bits
Le point de départ créé par les règles de signe IEEE-754
- La soustraction en virgule flottante IEEE-754 possède la complétude fonctionnelle
- Être fonctionnellement complet signifie qu’avec cette seule opération, on peut construire n’importe quel circuit binaire
- Le point clé réside dans les règles sur le bit de signe de la section 6.3 de la norme IEEE 754-2019
- La soustraction
x - yest traitée comme la sommex + (-y) - Le zéro peut porter un signe, donc
-0et+0sont traités comme deux valeurs distinctes - En revanche, dans les comparaisons IEEE-754,
-0 == +0est vrai - Tant que les entrées et le résultat ne sont pas NaN, le signe d’une somme ou d’une différence suit les règles de signe des opérandes
- Si la différence de deux valeurs de même signe vaut exactement 0, le résultat est
+0dans tous les modes d’arrondi saufroundTowardNegative
- La soustraction
- La construction qui suit suppose le mode d’arrondi par défaut,
roundTiesToEven- Elle fonctionne de façon similaire avec
roundTowardNegative
- Elle fonctionne de façon similaire avec
La table de vérité obtenue en soustrayant des zéros
- Si l’on ne soustrait que
-0et+0, on obtient les résultats suivants-0 - -0 = +0-0 - +0 = -0+0 - -0 = +0+0 - +0 = +0
- Si l’on prend
-0pour false et+0pour true, la table de vérité de sortie devient0 0 -> 10 1 -> 01 0 -> 11 1 -> 1
- Cette table de vérité est équivalente à
A ∨ ¬B, soit une porte IMPLY de formeB → A- Par rapport à une porte IMPLY classique, les arguments sont inversés
Avec une constante false, on obtient la complétude fonctionnelle
- Cette table de vérité devient fonctionnellement complète dès lors qu’on a accès à une constante false
- Avec une constante false, on peut construire une porte NOT
- NOT + IMPLY forme un ensemble fonctionnellement complet
- Les portes NAND et NOR sont fonctionnellement complètes à elles seules, sans constante particulière
- Lors de la fabrication de micropuces, cela présente l’avantage de ne produire qu’un seul type de composant
- Il n’est pas nécessaire de router un signal low constant pour construire une porte NOT
Un circuit logique par soustraction en Python
- L’exemple Python définit
-0.0comme false et0.0comme true- En IEEE-754,
+0et-0sont égaux à la comparaison ; on les distingue donc en extrayant le signe avecmath.copysign
- En IEEE-754,
- La porte NOT exploite le fait que
-0 - xinverse le signe du zérof_not = lambda x: f_false - xf_not(-0.0)devient truef_not(+0.0)devient false
- La porte OR est construite en inversant le signe du deuxième argument avant la soustraction
f_or = lambda a, b: a - f_not(b)- Elle ne vaut false que lorsque les deux arguments valent
-0, et true dans tous les autres cas
- AND et XOR peuvent eux aussi être construits par composition de OR et 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)))
Des entiers logiciels construits en Rust
- L’exemple Rust pose
Bit = f32, et représente les bits avecZERO = -0.0etONE = 0.0 not,or,andetxorsont tous implémentés à partir de soustractions en virgule flottante, puis utilisés pour créer un additionneur completadderSoftU8 = [Bit; 8]sert à représenter un entier 8 bitsto_softu8convertit chaque bit d’unu8enONEouZEROfrom_softu8vérifie le signe de chaque élément pour reconstruire unu8
- Le programme d’exemple convertit 23 et 19 en
SoftU8, les additionne, puis affiche 42 - L’addition de deux entiers 8 bits nécessite environ 120 instructions en virgule flottante
- Sur x86-64, il n’existe pas d’instruction réelle d’inversion de signe en virgule flottante ; le compilateur utilise donc un masque et un XOR pour basculer le bit de signe, c’est-à-dire le bit de poids fort d’un nombre IEEE-754
2 commentaires
Avis de Hacker News
On peut imaginer que ce genre de détournement bizarre d’instructions en virgule flottante soit le genre de chose qu’un DRM pourrait utiliser pour obscurcir une machine virtuelle.
L’étape suivante serait sans doute de créer un compilateur qui exploite cette propriété pour exécuter du code source ordinaire sous forme d’entiers en virgule flottante, puis d’y ajouter quelque chose comme une FFI pour appeler les API d’un OS classique.
C’est une preuve constructive que le mécanisme de gestion des exceptions de la MMU Intel est Turing-complet.
Un assembleur a été écrit pour transformer les instructions
Move, Branch if Zero, Decrementen source C configurant plusieurs tables de contrôle du processeur ; une fois ce code exécuté, le CPU effectue le calcul en essayant de déclencher des exceptions, sans exécuter une seule instruction.En option, l’assembleur peut aussi générer des instructions x86 qui affichent des variables dans le framebuffer VGA et passent le contrôle entre les instructions d’affichage natives et les instructions de trap de la weird machine.
Ça me fait penser à cette excellente vidéo qui construit un calcul uniquement avec des NaN et des infinis IEEE-754 : https://www.youtube.com/watch?v=5TFDG-y-EHs
Du contenu extrêmement nerd, réfléchi et drôle, avec une très bonne présentation.
Je la recommande vivement, en particulier au public de HN.
Dans la nouvelle Coding Machines, une exploitation similaire du bit de signe était le grand indice qu’une véritable IA avait été lâchée dans le monde.
https://www.teamten.com/lawrence/writings/coding-machines/
Voir aussi https://dougallj.wordpress.com/2020/05/10/bitwise-conversion...
C’est une implémentation qui convertit un double IEEE-754 en une paire de doubles contenant les valeurs entières des 32 bits de poids faible et des 32 bits de poids fort de la représentation binaire de l’argument, en n’utilisant que l’addition, la soustraction et la multiplication de doubles.
En regardant la table de vérité, la soustraction est clairement préservante du vrai, donc elle ne semble pas pouvoir être réellement fonctionnellement complète.
Qu’est-ce que je rate ?
Sans cette constante, elle ne l’est pas, contrairement à NAND qui peut produire false à partir de n’importe quelle valeur.
Le but de l’article était de montrer qu’avec seulement le zéro signé et la soustraction en virgule flottante, on peut simuler n’importe quel circuit ; j’ai trouvé que la complétude fonctionnelle était le terme le plus concis pour l’exprimer, mais si l’on s’en tient strictement à la table de vérité, c’est un léger détournement des règles, et je le préciserai dans l’article.
Avec la soustraction et 0, on construit false sous la forme -0.0, et on obtient l’ensemble fonctionnellement complet
{->, _|_}mentionné sur Wikipédia [1].[1] https://en.wikipedia.org/wiki/Functional_completeness
Je ne suis pas d’accord avec l’idée que les bits de soustraction seuls soient fonctionnellement complets.
Le raisonnement selon lequel, puisqu’ils préservent le vrai, ils ne sont pas fonctionnellement complets me semble correct.
Elle dit que « tout ensemble de connecteurs binaires contenant NOT et l’un de {AND, OR, IMPLY} est un sous-ensemble minimal fonctionnellement complet de {NOT, AND, OR, IMPLY, IFF} ».
[1] https://en.wikipedia.org/wiki/Functional_completeness
D’abord, comment savoir que la table de vérité est préservante du vrai ? Une table de vérité n’est pas un argument logique.
Si la complétude fonctionnelle signifie que l’on peut construire n’importe quel circuit logique, est-ce que cela veut dire que la soustraction en virgule flottante IEEE-754 est, en pratique, Turing-complète ? Ou non ?
La complétude fonctionnelle n’inclut pas la capacité d’itération nécessaire à la complétude de Turing
La complétude de Turing est souvent employée à tort quand on veut parler de complétude fonctionnelle ; les deux sont parfois confondues, ou bien le terme est utilisé parce qu’il sonne mieux dans le titre d’un billet de blog ou d’un article
movn’est en fait pas Turing-complet ; il faut une instructionjmp: https://harrisonwl.github.io/assets/courses/malware/spring20...Les systèmes de chiffrement homomorphe sont fonctionnellement complets, mais pas Turing-complets. L’itération révélerait le nombre d’opérations effectuées, ce qui casserait le chiffrement
On peut construire une machine Turing-complète avec des portes NAND, mais dire qu’une porte NAND est Turing-complète, c’est comme dire qu’on peut vivre à l’intérieur d’une brique
On ne peut pas vivre dans une brique, mais on peut construire une maison avec des briques et y vivre
« soustraire et brancher si inférieur ou égal à 0 » est une instruction unique Turing-complète
https://en.wikipedia.org/wiki/One-instruction_set_computer
Je l’avais déjà posté autrefois dans un fil /r/programming, mais je le remets ici
On peut implémenter un additionneur avec « seulement » 11 soustractions
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 l’on parle d’« entiers implémentés en logiciel uniquement avec des opérations en virgule flottante », c’est en gros comme toutes les tentatives d’utiliser le type number de JavaScript comme un int
La phrase « si les deux significandes ont le même signe, la sortie doit aussi avoir ce signe. Mais dans x−y, si x et y ont des signes différents, la sortie doit avoir le signe de x » est soit légèrement erronée, soit elle mélange deux sens du mot signe
Si x=5 et y=10, donc tous deux positifs, x-y vaut -5 et a donc un signe négatif
Même si l’on suppose que le signe de la variable y est effectivement inversé, en prenant -3 et -6, le second devient 6 et le résultat est +3, donc d’un signe différent de x
Il en va de même pour -3 et -6 : x et y ont le même signe, donc la condition concernant la soustraction n’est pas satisfaite
Les exemples portent sur des signes identiques
Il y a une erreur dans le titre. Cela ne veut pas dire que la soustraction est achevée, mais qu’elle est dite fonctionnellement complète au sens où toutes les fonctions peuvent être exprimées à l’aide de la soustraction.