CONVERSION D'UN NOMBRE ENTIER EN SA REPRÉSENTATION BINAIRE
cs_Kenavo
Messages postés702Date d'inscriptionvendredi 21 mars 2003StatutMembreDernière intervention 1 octobre 2009
-
13 avril 2004 à 17:16
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 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.
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 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és4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 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és2Date d'inscriptionmardi 28 février 2006StatutMembreDernière intervention28 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és2Date d'inscriptionmardi 8 avril 2003StatutMembreDernière intervention19 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és84Date d'inscriptionmardi 4 mai 2004StatutMembreDernière intervention17 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és84Date d'inscriptionmardi 4 mai 2004StatutMembreDernière intervention17 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és4297Date d'inscriptionsamedi 19 janvier 2002StatutMembreDernière intervention 9 janvier 201332 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és38Date d'inscriptionmardi 17 décembre 2002StatutMembreDernière intervention25 janvier 2006 26 août 2005 à 20:54
Merci SBE, il est sympa ton bout de fonction ;)
cs_sbe
Messages postés2Date d'inscriptionmardi 8 avril 2003StatutMembreDernière intervention19 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és702Date d'inscriptionvendredi 21 mars 2003StatutMembreDernière intervention 1 octobre 20095 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;
3 mai 2006 à 14:15
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;
3 mai 2006 à 13:45
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 ?
28 mars 2006 à 12:29
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
19 nov. 2005 à 02:46
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
16 nov. 2005 à 15:26
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 :)
16 nov. 2005 à 15:23
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 !
26 août 2005 à 22:53
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.
26 août 2005 à 20:54
25 mai 2004 à 02:50
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
13 avril 2004 à 17:16
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