I have a fairly simple function in Delphi which takes a string and produces a hashed integer based on that string:
function TfrmMain.HashElf(const Buf; BufSize : LongInt) : LongInt;
var
Bytes : TByteArray absolute Buf;
I, X : LongInt;
begin
Result := 0;
for I := 0 to BufSize - 1 do begin
Result := (Result shl 4) + Bytes[I];
X := Result and $F0000000;
if (X <> 0) then Result := Result xor (X shr 24);
Result := Result and (not X);
end;
end;
I'm converting it to PHP but the results are not the same. This is what i've got in PHP:
function HashElf($Buf, $BufSize){
$Bytes = str_split($Buf);
for ($i= 0; $i<$BufSize;$i++){
$Result = ($Result << 4) + Ord($Bytes[$i]);
$X = $Result & (0xF0000000);
if ($X<>0){$Result = $Result ^ ($X>>24);}
$Result = ($Result & (~ $X));
}
return $Result;
}
if you pass in the string teststring to the Delphi function you get 195831015 however PHP returns 72559895. I noticed the difference only becomes apparent after 7 characters. If the test string is just test the results are identical.
PHP seems to have some difficulty with shifting a negative integer to the right for example the folowing line:
if ($X<>0){$Result = $Result ^ ($X>>24);}
changed to shift left $X<<24 produces the same values as Delphi for the variable X, but the results are still different.
Am i missing something really obvious here?
EDIT: The output of the two functions are:
Delphi
Char: t Result: 116 X: 0
Char: e Result: 1957 X: 0
Char: s Result: 31427 X: 0
Char: t Result: 502948 X: 0
Char: s Result: 8047283 X: 0
Char: t Result: 128756644 X: 0
Char: r Result: 181058242 X: 1879048192
Char: i Result: 212577321 X: -1610612736
Char: n Result: 180011582 X: -1073741824
Char: g Result: 195831015 X: -1610612736
PHP
Char: t $Result: 116 $X: 0
Char: e $Result: 1957 $X: 0
Char: s $Result: 31427 $X: 0
Char: t $Result: 502948 $X: 0
Char: s $Result: 8047283 $X: 0
Char: t $Result: 128756644 $X: 0
Char: r $Result: 181058242 $X: 1879048192
Char: i $Result: 212577417 $X: -1610612736
Char: n $Result: 180013310 $X: -1073741824
Char: g $Result: 195858503 $X: -1610612736
So its not until character "i" that php starts to get off track with the calculations
EDIT2:
Added PHP function to do a logical right shift instead of arithmetic shift:
function lshiftright($var,$amt)
{
$mask = 0x40000000;
if($var < 0)
{
$var &= 0x7FFFFFFF;
$mask = $mask >> ($amt-1);
return ($var >> $amt) | $mask;
}else{
return ($var >> $amt);
}
}
This now works! Also thanks Ignacio for the mask idea :)
Are you sure Delphi is right and PHP is wrong?
Delphi's shl and shr apparently can behave unpredictably with signed integers. See: http://www.merlyn.demon.co.uk/del-bits.htm#SAR. Dr. Stockton seems to imply there are two types of shift operations: arithmetic shift (keeping the sign) and logical shifts.
The docs ( http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/expressions_xml.html ) aren't very clear on the effect of shl/shr on signed integers. They do however mention that shr/shl by one is only comparable to divions/multiplication by 2 for unsigned integers.
I couldn't find what Dr. Stockton (from the first link) calls the logical shift operations, but it would seem logical :-) to try changing the delphi implementation to use an unsigned 8-byte type (DWORD comes to mind) and see what effect that has.
Mask the bits you want.
if ($X<>0){$Result = ($Result ^ ($X>>24)) & 0xFF;}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With