Link to home
Start Free TrialLog in
Avatar of Eddie Shipman
Eddie ShipmanFlag for United States of America

asked on

Delphi->PHP Conversion .

I have a need for some Integer to base36 conversions.

I have the code in Delphi but need a PHP version now.

Here is the Delphi code:

const XX = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ;
Function Base36ToInt (const S: string): Int64;
var i: integer;
begin
  Result := 0;
  For i := 1 to length(S) do
    Result := Result * 36 + Pred(Pos(UpCase(S[i]), XX)) ;
end;
 
Function IntegerToBase36 (I: Int64): string;
begin
  Result := '';
  Repeat
    Result := XX[Succ(I mod 36)] + Result ;
    I := I div 36;
  Until I=0;
end;


Using base_convert DOES NOT work the same and returns a
different result. I need the code above converted directly.

Here is sample data:

Integer passed: 5555444433331111
String Result: 177XYGYLBFLZ4

Using this PHP code:
function base36toint($s){
  $x = base_convert($s, 36, 10);
  return $x;
}

function inttobase36($s) {
  $x = base_convert($s, 10, 36);
  return $x;
}

results in this: 1ip8nxua3s7

Another developer using the exact same PHP code
gets a different result than I, why?

Avatar of _GeG_
_GeG_

either your example or your delphi code is wrong, I checked, both my custom conversion and base_convert return the same result:
function Base36ToInt ($s){
      static $x=array('1'=>1, '2'=>2, '3'=>3, '4'=>4, '5'=>5, '6'=>6, '7'=>7, '8'=>8, '9'=>9,
             '0'=>0, 'a'=>10, 'b'=>11, 'c'=>12, 'd'=>13, 'e'=>14, 'f'=>15, 'g'=>16, 'h'=>18,
             'i'=>18, 'j'=>19, 'k'=>20, 'l'=>21, 'm'=>22, 'n'=>23, 'o'=>24, 'p'=>25, 'q'=>26,
             'r'=>27, 's'=>28, 't'=>29, 'u'=>30, 'v'=>31, 'w'=>32, 'x'=>33, 'y'=>34, 'z'=>35);
      $r=0;
      $s=strtolower($s);
      for ($i=0;$i<strlen($s);$i++){
            $r=bcadd( bcmul($r, 36), $x[$s{$i}]);
      }
      return $r;
}
echo Base36ToInt('177XYGYLBFLZ4')."\n";
echo base_convert('177XYGYLBFLZ4', 36, 10);

result:
5688775099731057664
5688775099731058624
ooops, now I see they are not the same :(
Must be a rounding error in base_convert, how ugly!
ok, a little more testing:
on linux php 4.3.8 there is no error
on windows php 5.0 there is a rounding error, very ugly!
ASKER CERTIFIED SOLUTION
Avatar of Marcus Bointon
Marcus Bointon
Flag of France image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Eddie Shipman

ASKER

Squinky:
Using your code:
<?PHP
/**
* Convert a number (as a string) in base 10 to any base
*
* e.g. dec2base('32', 16) => '20';
* Uses BCMath extension, so number can be as big as you like
* @param string $value A number in base 10, expressed as a string
* @param integer $base The base to convert to
* @param mixed $digits Optional string representing the characters used in the numbering system
*/
function dec2base($dec, $base, $digits = FALSE) {
     if($base < 2 or $base > 256) die("Invalid Base: .$base\n");
     bcscale(0);
     $value = '';
     if(!$digits) $digits = digits($base);
     while($dec > $base - 1) {
          $rest = bcmod($dec,$base);
          $dec = bcdiv($dec,$base);
          $value = $digits[$rest].$value;
     }
     $value=$digits[intval($dec)].$value;
     return (string) $value;
}

/**
* Convert a number (as a string) in any base to base 10
*
* e.g. base2dec('20', 16) => '32';
* Uses BCMath extension, so number can be as big as you like
* @param string $value A number in any base, expressed as a string
* @param integer $base the base that this number is in
* @param mixed $digits Optional string representing the characters used in the numbering system
*/
function base2dec($value, $base, $digits = FALSE) {
     if($base < 2 or $base > 256) die("Invalid Base: .$base\n");
     bcscale(0);
     if($base < 37) $value = strtolower($value);
     if(!$digits) $digits = digits($base);
     $size = strlen($value);
     $dec = '0';
     for($loop=0; $loop < $size; $loop++) {
          $element = strpos($digits, $value[$loop]);
          $power = bcpow($base, $size-$loop-1);
          $dec = bcadd($dec, bcmul($element, $power));
     }
     return (string) $dec;
}

/**
* Return a string containing chars to use in a given base's numbering system
*
* e.g. digits(16) => '0123456789abcdef'
* @param integer $base
* @return string
*/
function digits($base) {
     if($base > 64) {
          $digits = '';
          for($loop=0; $loop < $base; $loop++) {
               $digits .= chr($loop);
          }
     } else {
          $digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_';
     }
     $digits = substr($digits, 0, $base);
     return (string) $digits;
}

  $s = "5555444433331111";
  echo "Input#: ".$s;
 
  $encrypt = dec2base($s,36);
  echo " <br>Encrypted: ".$encrypt;
 
  $decrypt = base2dec($encrypt,10);
  echo " <br>Decrypted:  ".$decrypt;

?>

Here are the results:
Input#: 5555444433331111
Encrypt: 1ip8nxua3s7
Decrypt: 10080000307

Did I do something wrong???

yes:

$decrypt = base2dec($encrypt, 36);

I just happen to have made these functions do conversions to and from base-10, rather than between arbitrary bases. base2dec always produces a base-10 number, but you need to tell it what the input base is. You've got a weird result because you told it that '1ip8nxua3s7' is a base-10 number, and it doesn't check that what you submit is actually a valid number in that base.
Also, you may want to change the order of upper and lower case letters in the digits function to retain compatibility with your Delphi function - I chose to make lower case numbers represent smaller numbers.
I completely took out the capital letters and the - and _, we will never be using a negative number.

I used other routines for the Delphi conversion (see below).

Many thanks.

Delphi code used:
function Base36ToInt(const S: String): Int64;
var
  i: integer;
begin
  Result := 0;
  for i := 1 to Length( S ) do
  begin
    if S[i] in ['0'..'9'] then
    begin
      Result := Result * 36 + ( Ord( S[i] ) - Ord( '0' ) );
    end
    else
    begin
      Result := Result * 36 + ( Ord(S[i] ) - Ord( 'a' ) + 10);
    end;
  end;
end;

function IntegerToBase36 (I: Int64): String;
var
  N: Int64;
  M: integer;
begin
  Result := '';
  repeat
    M := I mod 36;
    if M < 10 then
    begin
      Result := Char( Ord( '0' ) + M ) + Result;
    end
    else
    begin
      Result := Char( Ord( 'a' ) + M - 10 ) + Result;
    end;
    I := I div 36;
  until I = 0;
end;