Aller au contenu

%BITNOT

Inversion des bits

Renvoie la valeur dont tous les bits sont inversés : les 0 deviennent des 1 et inversement, ce qui sert surtout à retirer un indicateur avec %BITAND.

Syntaxe

%BITNOT(expression)
expression
Un entier (signé ou non) ou une zone caractère.
Résultat
La valeur de l'opérande avec tous ses bits inversés. La taille du résultat dépend de celle de l'opérande.

À quoi ça sert

Pour retirer un droit d'un mot de bits, on ne peut pas simplement soustraire (le droit n'est peut-être pas là). La méthode sûre : %bitand(droits : %bitnot(ECRITURE)). On construit un masque où tout est à 1 sauf le bit à retirer, et le ET efface ce bit sans toucher aux autres.

Exemple 1 : Retirer un droit

**free
// %BITNOT : retirer un droit en combinant %BITAND et %BITNOT.
ctl-opt dftactgrp(*no) actgrp(*new);

dcl-c LECTURE  1;
dcl-c ECRITURE 2;
dcl-c SUPPRESS 4;

dcl-s droits uns(10) inz(7);       // lecture + ecriture + suppression

snd-msg 'Depart          : [' + %char(droits) + ']';
snd-msg 'NOT ecriture    : [' + %char(%bitnot(ECRITURE)) + ']';

// Retirer le droit d'ecriture : ET avec le complement
droits = %bitand(droits : %bitnot(ECRITURE));
snd-msg 'Sans ecriture   : [' + %char(droits) + ']';

// Le retirer une deuxieme fois ne change rien
droits = %bitand(droits : %bitnot(ECRITURE));
snd-msg 'Encore          : [' + %char(droits) + ']';

// Retirer deux droits d'un coup
droits = %bitand(droits : %bitnot(%bitor(LECTURE : SUPPRESS)));
snd-msg 'Sans lect/suppr : [' + %char(droits) + ']';

*inlr = *on;

Résultat réel, compilé et exécuté sur IBM i 7.5 :

Depart          : [7]
NOT ecriture    : [18446744073709551613]
Sans ecriture   : [5]
Encore          : [5]
Sans lect/suppr : [0]

Le mot de droits vaut 7. Retirer l'écriture donne 5 ; le refaire ne change rien. On peut retirer plusieurs droits d'un coup en inversant un %bitor de masques : retirer lecture et suppression du 5 laisse 0.

Regardez la deuxième ligne : le complément de la constante 2 est un très grand nombre, c'est le piège décrit plus bas.

Exemple 2 : Le type décide du résultat

**free
// %BITNOT : le resultat depend du type et de la taille de l'operande.
ctl-opt dftactgrp(*no) actgrp(*new);

// API C standard : convertit des octets en texte hexadecimal
dcl-pr cvthc extproc('cvthc');
  cible pointer value;
  source pointer value;
  longueur int(10) value;
end-pr;

dcl-s petit  uns(3)  inz(5);
dcl-s moyen  uns(10) inz(5);
dcl-s signe  int(10) inz(5);
dcl-s octet  char(1) inz(x'0F');
dcl-s res1   char(1);

snd-msg 'uns(3)  5 -> [' + %char(%bitnot(petit)) + ']';
snd-msg 'uns(10) 5 -> [' + %char(%bitnot(moyen)) + ']';
snd-msg 'int(10) 5 -> [' + %char(%bitnot(signe)) + ']';

// Deux inversions redonnent la valeur de depart
snd-msg 'NOT NOT 5 -> [' + %char(%bitnot(%bitnot(moyen))) + ']';

res1 = %bitnot(octet);
snd-msg 'char x0F  -> ' + hex(%addr(res1) : 1);

*inlr = *on;

dcl-proc hex;
  dcl-pi *n varchar(16);
    adresse pointer value;
    longueur int(10) value;
  end-pi;
  dcl-s texte char(16);
  cvthc(%addr(texte) : adresse : longueur * 2);
  return %subst(texte : 1 : longueur * 2);
end-proc;

Résultat réel, compilé et exécuté sur IBM i 7.5 :

uns(3)  5 -> [250]
uns(10) 5 -> [4294967290]
int(10) 5 -> [-6]
NOT NOT 5 -> [5]
char x0F  -> F0

La même valeur 5 donne trois résultats selon la zone : 250 pour un uns(3) (8 bits), 4294967290 pour un uns(10) (32 bits) et -6 pour un int(10) signé. Appliqué deux fois, %bitnot redonne 5. Sur un octet caractère, 0F devient F0.

Le piège

Le résultat dépend de la taille de l'opérande. Sur une constante comme ECRITURE (2), le complément est calculé sur 64 bits non signés et vaut 18446744073709551613. Combiné par %bitand avec une zone de 32 bits, cela reste correct (le retrait du droit donne bien 5), mais si vous affichez ou stockez le complément lui-même, il faut une zone assez grande et non signée. Dans un entier signé, l'inversion donne un nombre négatif (-6 pour 5).