CONVERSION D'UN NOMBRE ENTIER EN SA REPRÉSENTATION BINAIRE

cs_Kenavo Messages postés 702 Date d'inscription vendredi 21 mars 2003 Statut Membre Dernière intervention 1 octobre 2009 - 13 avril 2004 à 17:16
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 - 3 mai 2006 à 14:15
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/21707-conversion-d-un-nombre-entier-en-sa-representation-binaire

f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
3 mai 2006 à 14:15
tiens ça m'a fait reflechir,
donc petite variante avec un case of pour IntToBinStr

ce qui nous descend les performances a :

60..65ms pour 100000 appels et 500..520ms pour 1 millions d'appels...
soit un gain de 10 et 100 ms environ ... je savais bien qu'il y avait un probleme avec mes ensembles ...

function IntToBinStr(const I : int64) : string;
var N : integer;
T4 : $0..$f;
PRS : PChar;
begin
if I = -1 then begin
Result := DupeChar('1',64);
exit;
end;
Result := DupeChar('0',64);
if I = 0 then exit;
PRS := PChar(Result);
for N := 15 downto 0 do begin
T4 := (I shr (N*4)) and $f;
Case T4 of
$1 : begin PRS[3]:='1'; end;
$2 : begin PRS[2]:='1'; end;
$3 : begin PRS[2]:='1'; PRS[3]:='1'; end;
$4 : begin PRS[1]:='1'; end;
$5 : begin PRS[1]:='1'; PRS[3]:='1'; end;
$6 : begin PRS[1]:='1'; PRS[2]:='1'; end;
$7 : begin PRS[1]:='1'; PRS[2]:='1'; PRS[3]:='1'; end;
$8 : begin PRS[0]:='1'; end;
$9 : begin PRS[0]:='1'; PRS[3]:='1'; end;
$A : begin PRS[0]:='1'; PRS[2]:='1'; end;
$B : begin PRS[0]:='1'; PRS[2]:='1'; PRS[3]:='1'; end;
$C : begin PRS[0]:='1'; PRS[1]:='1'; end;
$D : begin PRS[0]:='1'; PRS[1]:='1'; PRS[3]:='1'; end;
$E : begin PRS[0]:='1'; PRS[1]:='1'; PRS[2]:='1'; end;
$F : begin PRS[0]:='1'; PRS[1]:='1'; PRS[2]:='1'; PRS[3]:='1'; end;
end;
inc(PRS,4);
end;
end;
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
3 mai 2006 à 13:45
je pense que c'est une erreur du site Gulber ... > est un symbol HTML qui correspond a "<" ou ">" je sais plus...

sinon un test de performances, les gars, vous etes grave a la ramasse :
pour 100000 convertions de la valeur 65535 en binaire

Psycho81 : 800..850 ms raisonnable mais peut mieux faire...

Sbe : relevée a plus de 12secondes pour seulement 5000 appels avec un facteur de *10 tout les 500 appels (c'est precis mon analyzer)

Kenavo : pas en forme mon ami relevée a plus de 1700 ms pour seulement 1000 appels avec un facteur de *10 toute les 200 appels

DelphiProg : relevée a plus de 12 secondes pour seulement 5000 appels avec un facteur de *10 toute les 500 appels ... comme la methode de SBE... je suis deçu la...

et je vous parle meme pas des fonctions binaire vers valeur ... c'est la cata...

aller je suis sympa je vous donne les methodes ... qui coté code sont pourtant d'une simplicitée enfantine et surtout etonnante quand a leurs performances...

IntToBinStr (convertis une valeur Int64 en sa representation binaire)
70..80 ms pour 100000 appels, 650..660ms pour 1 millions d'appels ...

BinStrToInt (convertis une representation binaire en une valeur Int64)
28..35ms pour 100000 appels, 135..145 ms pour 1 millions d'appels
(anecdote, quand j'ai vus le ~140 ms ... je me suis dis zut ... elle est pas trop top comparée a l'autre ... mais aprés relecture c'etait le temps pour 1 millions d'appels ... O.o )


{ DupeChar permet de créer une chaine de "Count" caractere "C", trés pratique pour remplir trés vite une chaine de "0" ou de "1" }

function DupeChar(const C : char; Count: Integer): string;
var
PResult: PChar;
begin
SetLength(Result, Count);
PResult := PChar(Result);
while Count > 0 do begin
PResult[0] := C;
Inc(PResult);
Dec(Count);
end;
PResult[0] := #0;
end;

{ convertis un chaine representant une valeur binaire en valeur Int64 }
function BinStrToInt(const S : string) : int64;
var N,B : integer;
begin
result := 0;
B := 1;
For N := Length(S) downto 1 do begin
if S[N] = '1' then
inc(result,B);
inc(B,B); { simple mais efficace on compte 1, 2, 4, 8, 16 etc... }
end;
end;

{ aaah, ici toute l'astuce reside dans le traitement par block de 4 bits }
function IntToBinStr(const I : int64) : string;
var N : integer;
T4 : $0..$f;
PRS : PChar;
begin
if I = -1 then begin
{ on sait que -1 = 11111111 ou encore $FF, High etant $7F et Low $80 }
Result := DupeChar('1',64);
exit;
end;
Result := DupeChar('0',64);
if I = 0 then
exit; {idem, on sait que 0 = 00000000 ou encore $00 }
PRS := PChar(Result); { on PCharize result}
for N := 15 downto 0 do begin
{ on SHRize I a N*4 et on $fize pour ne garder que le demi-octet }
T4 := (I shr (N*4)) and $f;
{ on tris un peu pour savoir quel bit on mets a 1, ici j'ai pas voulus croiser les cas
car c'est un peu inutile, de plus ça augmenterais les instructions }
if T4 in [$1,$3,$5,$7,$9,$B,$D,$F] then
PRS[3] := '1';
if T4 in [$2,$3,$6,$7,$A,$B,$E,$F] then
PRS[2] := '1';
if T4 in [$4,$5,$6,$7,$C,$D,$E,$F] then
PRS[1] := '1';
if T4 in [$8,$9,$A,$B,$C,$D,$E,$F] then
PRS[0] := '1';
{ vus qu'on travail par block de 4 bits on incremente la chaine de 4 }
inc(PRS,4);
end;
end;


et voila ... avec des methodes aussi performante, j'espere que vous serez content...
de plus elle sont facilement adaptable et permettent egalement (grace a leurs performances) d'ajouter un peu de code supplementaire pour traiter les cas 8, 16, 32 bits et plus de 64 ...

la ou vous avez tous fait une erreur c'est les appels trop nombreux a IntToStr ou a d'autre fonctions a l'interieur de vos boucle ... meme la methode a psycho qui fait appel a l'assembleur et completement degradée par IntToStr ... dommage c'etait une bonne idée cette petite fonction GetBit...

Quelque part je suis etonné que les fonctions que j'utilise depuis maintenant longtemps et qui ont été ameliorée au fur et a mesure, soit apparement les plus rapide.
et vous pouvez tester les cas des -1, high(int64), high(integer) ect... les resultats sont bon.

et comme je le rappel plus haut, pour les entiers signés :
-1 = $FF, $FFFF, 11111111
0 = $00, $0000, 00000000
High = $7F, $7FFF, 01111111
Low = $80, $8000, 10000000

amusant non ?
gulbeur Messages postés 2 Date d'inscription mardi 28 février 2006 Statut Membre Dernière intervention 28 mars 2006
28 mars 2006 à 12:29
bonjour SBE,
merci pour ton code mais
tu ecris :
if (Val and Masque)>0 then Etat := '1' else Etat := '0';
mais que veut dire : >0 ?
Mon delphi 6 ne compile pas cela il bloque au &.
merci
a+
gill
cs_sbe Messages postés 2 Date d'inscription mardi 8 avril 2003 Statut Membre Dernière intervention 19 novembre 2005
19 nov. 2005 à 02:46
Bonjour Delphiprog,

Pourrais-tu m'expliquer en quoi mon code ne fonctionne pas avec les nombres négatif ?
J'ai essayé ca :

DecToBin(-1,16) donne 1111111111111111
DecToBin(-2,16) donne 1111111111111110

Tous cela me semble bien correcte.

Merci
@++
Steph

merci
psycho81 Messages postés 84 Date d'inscription mardi 4 mai 2004 Statut Membre Dernière intervention 17 février 2008
16 nov. 2005 à 15:26
Oups, quel sacrilège ! Etant trop habitué à la syntaxe VB j'ai fait une erreur ... grossière.

Ce n'est pas

Result[i] = IntToStr(GetBit(Value,31-i));

mais

Result[i] := IntToStr(GetBit(Value,31-i));


Ca restait compréhensible mais ... := c'est quand même le signe distinctif du Pascal :)
psycho81 Messages postés 84 Date d'inscription mardi 4 mai 2004 Statut Membre Dernière intervention 17 février 2008
16 nov. 2005 à 15:23
Bonjour,
Je n'ai pas saisi le truc du complément à deux (qui, il me semble sert pour les additions et les soustractions en binaire) , cependant, je vous livre ici ma fonction IntToBin

function GetBit(IntegerValue: Integer;IntegerPosition: Integer):Integer;register;
{
Entrées EAX (IntegerValue) EDX (IntegerPosition)
Sorties EAX
}
asm
{ 1C }mov ECX,EDX // La fonction SHR a besoin du paramètre dans CL
{ 3C }shr EAX,CL // On effectue le décalage
{ 1C }and EAX,$01 // On ne garde que le premier bit
end;

function IntToBin(Value:Integer):ShortString;
var
i:integer;
begin
SetLength(Result,32)
for i := 0 to 31 do
begin
Result[i] = IntToStr(GetBit(Value,31-i));
end;
end;

Il subsiste peut être une petite erreur de syntaxe (je ne dispose pas de mon p'tit Delphi à portée de main pour tester le code. La fonction GetBit cependant est garantie fonctionelle ! Lorsque nous avons un entier qui a pour valeur -1 on obtient "normalement" la forme binaire du registre (je n'ai jamais entendu aprler de complément à deux pour une transformation binaire désolé ... donc mon raisonnement pourrait être erroné)

Bonne prog !
cs_Delphiprog Messages postés 4297 Date d'inscription samedi 19 janvier 2002 Statut Membre Dernière intervention 9 janvier 2013 32
26 août 2005 à 22:53
Le code de SBE ne calcule pas correctment la conversion des nombres négatifs alors que, pour le code de Kenavo, le résultat est correct.
Toutefois, dans les deux cas, leurs codes sources sont meilleurs que le mien même s'ils n'implémentent qu'une partie de la solution en ne se préoccupant pas de la surcharge.
c3rb3r3 Messages postés 38 Date d'inscription mardi 17 décembre 2002 Statut Membre Dernière intervention 25 janvier 2006
26 août 2005 à 20:54
Merci SBE, il est sympa ton bout de fonction ;)
cs_sbe Messages postés 2 Date d'inscription mardi 8 avril 2003 Statut Membre Dernière intervention 19 novembre 2005
25 mai 2004 à 02:50
Bonjour,

Il y a quand même beaucoup plus simple pour convertir une valeur décimale en binaire.

function DecToBin(Val: LongInt;NbBit:Integer): String;
var
Masque: Longint;
I: Integer;
Etat: String[1];
begin
Masque := 1;
for I := 0 to NbBit-1 do begin
if (Val and Masque)>0 then Etat := '1' else Etat := '0';
if Result = '' then Result := Etat else Result := Etat + Result;
Masque := Masque shl 1;
end;
end;

SBE
cs_Kenavo Messages postés 702 Date d'inscription vendredi 21 mars 2003 Statut Membre Dernière intervention 1 octobre 2009 5
13 avril 2004 à 17:16
Salut Delphiprog,

Y a juste l'histoire les valeur négatives qui me chagrine ! La représentation d'une valeur négative est le complément à 2 de la valeur absolue.
-1 en binaire sur 32 bits signés s'écrit : 11111111111111111111111111111111 ($FFFFFFFF)
(31 bits significatifs + 1 bit de signe)

Comme il parait compliqué de faire ce complément à 2 sur le resultat chaine, j'ai préféré utiliser la variable Temp pour calculer la représentation non signée de la valeur négative. Euh ! pas très clair ! donc exemple : -1 en 16 bits s'écrit $FFFF en Hexa ou 1111111111111111 en binaire soit 65565 en décimal, soit 2^16(65536) + (-1). C'est cette valeur qui est convertie.

Je te propose cette fonction pour un entier 32 bits :

function IntToBin(const Value: integer; bits: byte = 32): string;
var
Temp: Int64;
i : Integer;
begin
{ Value >0 -> Temp 0 ; Value < 0 -> Temp = 1}
Temp := Integer(value<0);
{ si Value >=0 -> Temp = Value ;
si Value < 0 -> Temp 2^bits - abs(value) 2^bits + Value}
Temp :Temp shl bits + Value; // ça marche pas si bits 64 : débordement
{ selon le principe des divisions successives par 2.
On concatène le dernier reste au résultat obtenu
par la division entière par 2}
for i:=0 to Bits-1 do
begin
Result := IntToStr(Temp mod 2) + Result;
//Le quotient devient le nouveau dividende
Temp := Temp div 2; // ou Temp shr 1;
end;
end;


Kénavo
Rejoignez-nous