Hoe kun je in PHP de 2's complement berekenen? Ik krijg het niet voor elkaar.

Voorbeeld: The two's complement of E2 is 1E.

Met een hex-waarde 'E2' wil ik dus '1E' terugkrijgen.

hexdec('E2') geeft 226.
Negatief is dit -226 .
Maar dechex(-226) geeft een compleet verkeerde waarde.

Weet iemand toevallig hoe dit kan?
Zo, nog effe wat in elkaar gesleuteld.

<?php

$getal         = $_GET['getal'] * 1;

$getal_hex     = strtoupper( ltrim( dechex( $getal ), "0" ) );
$getal_bin     = decbin( $getal );

$xor           = ~$getal;

$xor_bin       = ltrim( decbin( $xor ), '1' );

$complement    = $xor + 1;

$bincomplement = ltrim( decbin( $complement ) , '1');

echo 'getal dec      : ' . $getal . '<br />';
echo 'getal hex      : ' . $getal_hex . '<br />';
echo 'getal bin      : ' . $getal_bin . '<br />';

echo '<br />';

echo 'XOR getal dec  : ' . $xor . '<br />';
echo 'XOR getal bin  : ' . $xor_bin . '<br />';

echo '<br />';

echo 'complement + 1 : ' . $complement . '<br />';
echo 'complement bin : ' . $bincomplement . '<br />';

echo '<br />';

echo 'complement dec : ' . bindec( $bincomplement ) . '<br />';
echo 'complement hex : ' . str_pad( strtoupper( dechex( bindec( $bincomplement ) ) ), strlen($getal_hex), '0', STR_PAD_LEFT) . '<br />';

?>


Kijken hier : http://adoptive.esy.es/2compliment/?getal=226
Maak hier op zijn minst een functie van ofzo :/.

Handig is wellicht eerst een definitie, zoals ook in het eerste artikel van @Adoptive wordt gegeven.

Het 2s complement (niet compl[color=#ff0000]i[/color]ment) van een getal is dat getal met alle bits geïnverteerd (0 wordt 1 en andersom), plus één.

Als je weet hoe iets werkt of wat iets betekent dan kun je een beter waardeoordeel vormen of datgene wat er uitrolt ook daadwerkelijk klopt...

Toegepast op jouw vraagstuk, E2 is binair:
1110 0010

Geïnverteerd wordt dit:
0001 1101

En plus 1 wordt dit:
0001 1110


Wat equivalent is aan 1E.

En het is wel zo beleefd om wat terugkoppeling te geven als mensen de moeite nemen om te reageren.
Hey thanks!

Ik had het dus bijna, alleen $bincomplement = ltrim( decbin( $complement ) , '1'); dat deed ik niet.

Waarom moet dit eigenlijk? (hier ging het bij mij wel op mis, want ik kreeg FFFFFFFF1e.

Is deze code dan niet genoeg/hetzelfde?

$dec = (int) 226;
$negative = $dec * -1;
echo dechex( bindec( ltrim( decbin( $negative ) , '1') ) );
echo "<br/>";



Uhm, het ltrimmen van je most significant bits lijkt mij geen goed idee?

En als dat zou kloppen, hoe zou dat dan moeten werken?

En waar komt vermenigvuldigen met -1 vandaan?!

De negatie van een bitreeks verkrijg je door de bits te flippen, dit kun je (o.a.) met de tilde-operator (~ - de NOT-operator) doen. Wat heeft een min-teken hier mee te maken?

Dit is overigens geen XOR, want de XOR operator (^) werkt op twee operanden.

Over spraakverwarring en slecht lezen gesproken :/.\

EDIT: om terug te komen op je oorspronkelijke vraagstelling:
As PHP's integer type is signed, but dechex() deals with unsigned integers, negative integers will be treated as though they were unsigned.

Dat is mogelijk een verklaring voor het gedrag wat je ervaart wanneer je een negatief getal voert aan dechex().

EDIT #2: bij negatieve getallen hangt het wellicht ook van de precisie van het getaltype af wat het 2s complement is, dus eigenlijk kun je bij negatieve getallen niet zeggen wat het (bijbehorende) 2s complement is? Het lijkt mij handiger om alleen positieve getallen te accepteren, of om voor te schrijven wat dan de precisie zou moeten zijn?

EDIT #3: okay, na wat experimenteren lijkt mij dit een degelijke aanpak: het handigste is om gewoon alles naar een binaire schrijfwijze te converteren. Vervolgens zet je een bitmasker in in combinatie met XOR om de bits te inverteren. En tot slot tel je hier (weer in het decimale stelsel) 1 bij op. Vervolgens retourneer je het resultaat in de gewenste base-x notatie, in jouw geval hexadecimaal (het 16-tallige stelsel). Jouw functie wordt dus zoiets als:
<?php
function twosComplementHex($hex) {
    $binary = base_convert($hex, 16, 2); // from hex to binary
    $mask = str_repeat('1', strlen($binary)); // bitmask for inverting bits
    $not = bindec($binary) ^ bindec($mask); // XOR bits, basically inverting bits of input
    return base_convert($not + 1, 10, 16); // add 1 and return result as hex
}
echo twosComplementHex('e2'); // levert '1e'
?>

Maar wellicht loont het de moeite om dit wat verder door te testen.
Thomas van den Heuvel op 15/01/2018 19:07:52

Uhm, het ltrimmen van je most significant bits lijkt mij geen goed idee?

En als dat zou kloppen, hoe zou dat dan moeten werken?


Dit kwam uit het andere voorbeeld.


En waar komt vermenigvuldigen met -1 vandaan?!

De negatie van een bitreeks verkrijg je door de bits te flippen, dit kun je (o.a.) met de tilde-operator (~ - de NOT-operator) doen. Wat heeft een min-teken hier mee te maken?


ik begreep dat 2's complement de negatieve versie was van hetzelfde getal. Dus 1 / -1 , 4 / -4 etc. dus vandaar.



EDIT: om terug te komen op je oorspronkelijke vraagstelling:
[quote]As PHP's integer type is signed, but dechex() deals with unsigned integers, negative integers will be treated as though they were unsigned.

Dat is mogelijk een verklaring voor het gedrag wat je ervaart wanneer je een negatief getal voert aan dechex().

EDIT #2: bij negatieve getallen hangt het wellicht ook van de precisie van het getaltype af wat het 2s complement is, dus eigenlijk kun je bij negatieve getallen niet zeggen wat het (bijbehorende) 2s complement is? Het lijkt mij handiger om alleen positieve getallen te accepteren, of om voor te schrijven wat dan de precisie zou moeten zijn?
[/quote]


EDIT #3: okay, na wat experimenteren lijkt mij dit een degelijke aanpak: het handigste is om gewoon alles naar een binaire schrijfwijze te converteren. Vervolgens zet je een bitmasker in in combinatie met XOR om de bits te inverteren. En tot slot tel je hier (weer in het decimale stelsel) 1 bij op. Vervolgens retourneer je het resultaat in de gewenste base-x notatie, in jouw geval hexadecimaal (het 16-tallige stelsel). Jouw functie wordt dus zoiets als:
<?php
function twosComplementHex($hex) {
    $binary = base_convert($hex, 16, 2); // from hex to binary
    $mask = str_repeat('1', strlen($binary)); // bitmask for inverting bits
    $not = bindec($binary) ^ bindec($mask); // XOR bits, basically inverting bits of input
    return base_convert($not + 1, 10, 16); // add 1 and return result as hex
}
echo twosComplementHex('e2'); // levert '1e'
?>

Maar wellicht loont het de moeite om dit wat verder door te testen.


Ok, en waarom gebruik je nu XOR ipv de ~ operator? Verder is het wel een duidelijk functie bedankt voor alle uitleg. Binaire rekenen is lang geleden voor me merk ik.




ik begreep dat 2's complement de negatieve versie was van hetzelfde getal. Dus 1 / -1 , 4 / -4 etc. dus vandaar.

Mja, maar je bent stiekem bezig met signed getallen, dus wat -4 is hangt waarschijnlijk af van de maximale lengte van zo'n getal in het geheugen. -4 ziet er op de ene pjoeter anders uit dan op een ander pjoeter.
EDIT: het hangt er ook een beetje vanaf hoe je jouw hexadecimale getallen gaat gebruiken. Het 2s complement van een getal is mogelijk dubbelzinnig, of liever gezegd, is mogelijk niet altijd hetzelfde? Op grond waarvan is in jouw geval het 2s complement van e2 altijd 1e?
Deze zut is voor mij ook te lang geleden :p.

Ok, en waarom gebruik je nu XOR ipv de ~ operator?

Omdat de NOT-operator niet lijkt te werken op binaire noch hexadecimale getallen (of liever gezegd strings, een bitreeks is gewoon een string blijkbaar volgens var_dump()). Als ik de NOT pak van de binaire of hexadecimale variant van E2 dan rollen er allemaal arabische karakters uit. Dit omdat er dan karakters worden geïnverteerd in plaats van bits. Voor het correct werken met de XOR operator heb ik weer wel getallen nodig.

Merk op dat een XOR met een bitmask effectief hetzelfde doet als de NOT, omdat de waarheidstabellen van A XOR B en NOT A hetzelfde zijn. Ik werk dus wel met een XOR, maar het is in feite een NOT operatie.
A    B    A XOR B    NOT A
0    1    1          1
1    1    0          0

Hierbij is A het bit onder beschouwing en B je bitmask (altijd 1).

(dit stond in wezen al in het commentaar van de code: bitmask for inverting bits en XOR bits, basically inverting bits of input, maar hier alsnog het bewijs hiervoor :p)

Reageren