PHP - How to base_convert() up to base 62
Asked Answered
T

6

10

I need a base_convert() function that works from base 2 up to base 62 but I'm missing the math I need to use, I know that due to the limitations of PHP I need to make use of bcmath, which is fine.

Functions like these convert a number to and from base 10 to another base up to 62, but I want to implement the same functionality of base_convert(), e.g.: a only one function that can convert between arbitrary bases.

I've found a function that seems to do this, but it gives me the feeling of having some redundant and slow code and I would like to tweak it a little bit if I knew German, which I don't. =(

Here is a more readable version of the function:

function bc_base_convert($value, $quellformat, $zielformat)
{
    $vorrat = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    if (min($quellformat, $zielformat) < 2)
    {
        trigger_error('Bad Format min: 2', E_USER_ERROR);
    }

    if (max($quellformat, $zielformat) > strlen($vorrat))
    {
        trigger_error('Bad Format max: ' . strlen($vorrat), E_USER_ERROR);
    }

    $dezi = '0';
    $level = 0;
    $result = '';
    $value = trim(strval($value), "\r\n\t +");
    $vorzeichen = '-' === $value{0} ? '-' : '';
    $value = ltrim($value, "-0");
    $len = strlen($value);

    for ($i = 0; $i < $len; $i++)
    {
        $wert = strpos($vorrat, $value{$len - 1 - $i});

        if (FALSE === $wert)
        {
            trigger_error('Bad Char in input 1', E_USER_ERROR);
        }

        if ($wert >= $quellformat)
        {
            trigger_error('Bad Char in input 2', E_USER_ERROR);
        }

        $dezi = bcadd($dezi, bcmul(bcpow($quellformat, $i), $wert));
    }

    if (10 == $zielformat)
    {
        return $vorzeichen . $dezi; // abkürzung
    }

    while (1 !== bccomp(bcpow($zielformat, $level++), $dezi));

    for ($i = $level - 2; $i >= 0; $i--)
    {
        $factor = bcpow($zielformat, $i);
        $zahl = bcdiv($dezi, $factor, 0);
        $dezi = bcmod($dezi, $factor);
        $result .= $vorrat{$zahl};
    }

    $result = empty($result) ? '0' : $result;

    return $vorzeichen . $result;
}

Can anyone explain me the above function or give me some lights on the process of direct conversion between arbitrary bases?

Tactful answered 21/12, 2009 at 3:34 Comment(0)
S
16

As of PHP 5.3.2 both bc_math and gmp now support bases up to 62, so you can just do:

echo gmp_strval(gmp_init($mynumber, $srcbase), $destbase);

or the bc_math equivalent.

Shavian answered 10/8, 2011 at 16:17 Comment(3)
That's nice to know, however I can't find the BC Math equivalents.Tactful
base_convert() doesn't allow up to base 62Meister
Shameless self plug, I've wrote an utility that handles arbitrary numbers and bases using GMP, sources are on GitHub: github.com/thunderer/NumbaseBrainwork
S
10

Please dont ask me where i got it from, i just remeber that its based of some examples i found on the web...

  function charset_base_convert ($numstring, $fromcharset, $tocharset) {
     $frombase=strlen($fromcharset);
     $tobase=strlen($tocharset);
     $chars = $fromcharset;
     $tostring = $tocharset;

     $length = strlen($numstring);
     $result = '';
     for ($i = 0; $i < $length; $i++) {
         $number[$i] = strpos($chars, $numstring{$i});
     }
     do {
         $divide = 0;
         $newlen = 0;
         for ($i = 0; $i < $length; $i++) {
             $divide = $divide * $frombase + $number[$i];
             if ($divide >= $tobase) {
                 $number[$newlen++] = (int)($divide / $tobase);
                 $divide = $divide % $tobase;
             } elseif ($newlen > 0) {
                 $number[$newlen++] = 0;
             }
         }
         $length = $newlen;
         $result = $tostring{$divide} . $result;
     }
     while ($newlen != 0);
     return $result;
  }
Seating answered 12/1, 2011 at 12:10 Comment(2)
Very useful for going beyond the 62 character limit!Pyuria
After a couple of days of searching how to convert a number string (since PHP parses bigint to float and gets unprecise), this is the function that makes a conversion which can be done backwards as well. E.g. converting $number = "1255276776369394619"; to b4JlnAoHP37 and vice versa. Great!Conn
I
1

The easiest approach for any translation problems, from numeric base to human languages, is to translate via an intermediate format.

function bc_base_convert($num, $from, $to) {
    return bc_convert_to(bc_parse_num($num, $from), $to);
}

Now all you need to write are bc_convert_to and bc_parse_num. If the platform distinguishes numeric types, you'll need to take this in to account. Also, floating point numbers require special consideration because a number may have a finite representation in one base, but not another (e.g. 1/3 is 0.13 but 0.333...10, and 1/1010 is .0001100110011...2).

As for a generalized explanation of how conversion works, consider how positional base systems work. A numeral of the form "anan-1...a1a0" in a base b represents the number "an*bn + an-1*bn-1 + ... + a1*b1 + a0*b0". Conversion basically works by evaluating the expression in the context of another base β.

Involuntary answered 21/12, 2009 at 3:42 Comment(2)
I'm sorry but I don't get it, you mean I can't convert lets say from base 61 to base 5 directly?Tactful
You could, but it's easier to use a platform native format as an intermediate form.Involuntary
R
1

Most of the examples I found on the internet and in this answers use BC Math functions. If you do not want to use use BC Math functions, you can have a look at this library: http://www.lalit.org/lab/base62-php-convert-number-to-base-62-for-short-urls/

  • It doesn’t use BC Math functions so works without the use of BC Math library.
  • It uses the native base_convert functions when the base is below 36 for faster execution.
  • The output number is backward compatible with the native base_convert function.
  • Can be used to convert to and from arbitrary bases between 2-64.
Rebate answered 8/12, 2011 at 11:49 Comment(0)
T
0

I wrote about using the BCMath functions for decimal/binary conversion here: http://www.exploringbinary.com/base-conversion-in-php-using-bcmath/ . You could easily modify that code to convert to different bases.

For example, in the case of converting integers, modify routines dec2bin_i() and bin2dec_i(). Rename them and add a base parameter -- something like dec2base_i($base,$decimal_i) and base2dec_i($base,$num_i), change the hardcoded '2' to the variable $base, convert the numeric remainders to/from characters of the base, and rename the variables.

Now, to convert between arbitrary bases, use decimal as an intermediate and call both those new functions. For example, convert base 42 number "123" to base 59 by calling $dec = base2dec_i('42','123') followed by $b59 = dec2base_i(59,$dec).

(You could also make a combined function that does it in one call.)

Tripper answered 21/12, 2009 at 14:29 Comment(1)
Note that base_convert() itself -- internally -- uses an intermediate base, as I noted here: exploringbinary.com/…Tripper
R
-1

This function output the same than GNU Multiple Precision if possible…

<?php

function base_convert_alt($val,$from_base,$to_base){
static $gmp;
static $bc;
static $gmp62;
if ($from_base<37) $val=strtoupper($val);
if ($gmp===null) $gmp=function_exists('gmp_init');
if ($gmp62===null) $gmp62=version_compare(PHP_VERSION,'5.3.2')>=0;
if ($gmp && ($gmp62 or ($from_base<37 && $to_base<37)))
return gmp_strval(gmp_init($val,$from_base),$to_base);
if ($bc===null) $bc=function_exists('bcscale');
$range='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
if ($from_base==10)
$base_10=$val;
else
{
$n=strlen(($val="$val"))-++$ratio;
if ($bc) for($i=$n;$i>-1;($ratio=bcmul($ratio,$from_base)) && $i--)
$base_10=bcadd($base_10,bcmul(strpos($range,$val[$i]),$ratio));
else for($i=$n;$i>-1;($ratio*=$from_base) && $i--)
$base_10+=strpos($range,$val[$i])*$ratio;
}
if ($bc)
do $result.=$range[bcmod($base_10,$to_base)];
while(($base_10=bcdiv($base_10,$to_base))>=1);
else
do $result.=$range[$base_10%$to_base];
while(($base_10/=$to_base)>=1);
return strrev($to_base<37?strtolower($result):$result);
}


echo base_convert_alt('2661500360',7,51);

// Output Hello
Rodman answered 8/3, 2017 at 20:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.