$Xb base $FromBase\n=\n$Y base $ToBase\nTRUE EXACT VALUE\n\n$Yphp\nNative PHP function value = $YStatus\n\nCompare:\n$Y = TRUE EXACT VALUE\n$Yphp = $YStatus"; // ------------------------------------------------------------- // Read input arguments from interface. $arg1 = strtoupper(trim(@$_POST["arg1"])); if ($arg1 == ''){$arg1 = "1";} $arg2 = strtoupper(trim(@$_POST["arg2"])); if ($arg2 == ''){$arg2 = "10";} $arg3 = strtoupper(trim(@$_POST["arg3"])); if ($arg3 == ''){$arg3 = "16";} // Remove any decimal point, positive signs and spaces. // They will be treated as if they never existed. $arg1 = str_replace(".", '', $arg1); $arg1 = str_replace("+", '', $arg1); $arg1 = str_replace(" ", '', $arg1); $TimerStarted = $DigitCounts = ''; // ----------------------------------------------------------- // Check for invalid argument. In this case, must be numeric. // On error, null output (nothing printed). if (1 != 1) {$out = '';} else // ------------------------------------------------------------- // If no error, call the custom function // using the input argument. { // Compute base 10 equivalent of BaseB input value. // arg1 = x // arg2 = From base // arg3 = To base // Test if arguments are valid. $BaseDigits = substr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, $arg2); $ErrorMssg = (bcX_Is_Base_B($arg1, $arg2) === TRUE)? '' : "Invalid X value for base $arg2. At least one invalid digit was detected.

Numbers expressed in base $arg2 can only use the following $arg2 digits:\n$BaseDigits"; // Error if invalid from/to base if ($arg2 < 2 or $arg2 > 36 or $arg3 < 2 or $arg3 > 36) { $ErrorMssg = "ERROR: An invalid from or to base value was given. The base must be in the range from 2 to 36."; } if ($ErrorMssg != '') { $out = ''; $DigitCounts = $ErrorMssg; $TimerStopped = 0; } else { $TimerStarted = MicroTime(TRUE); $b10 = bcBaseB_Int_To_Base10 ($arg1, $arg2); $bbOut = bcBase10_Int_To_BaseB ($b10, $arg3); $TimerStopped = sprintf("%4.3f", MicroTime(TRUE) - $TimerStarted); $TimerStopped = Seconds_To_HMS($TimerStopped, 3); // Count digits in input and output values. $bbInpDigits = strlen($arg1); $bbOutDigits = strlen($bbOut); $DigitCounts = "The $bbInpDigits-digit base $arg2 integer given above EXACTLY equates to the following $bbOutDigits-digit base $arg3 integer:"; $out = "$bbOut"; } } // Set number of text lines for output text area. $TextLines = 5 + floor(strlen($out) / 80); // -------------------------- // Generate HTML page output. print <<< _HTML Integer Base Interconverter - PHP Science Labs

Convert Any Integer (± X) From One Base System Into Another

Enter Integer ±X  (Expressed in any base from 2 to 36)



  Integer X     from base    to base      (base = 2 to 36)


$DigitCounts
Processing time = $TimerStopped





A Problem With the Native PHP Base Conversion Function

Although PHP has a built-in base conversion function, it has a serious accuracy limitation.  No matter how many digits may be in the converted result, the actual accuracy may be only up to 12 or 14 digits at best.  This means that any digits after the 12th or 14th digit are most likely bogus, but nothing clearly indicates this, so we must be cautious when using that function.  The examples below will serve to illustrate this problem.

For example, when the numbers are very large, the accuracy of the result may break down at a certain point and the remaining digits are all junk digits.  However, the native PHP Base_Convert(...) function does not indicate that the converted result is wrong.  Consider this example, converting from base $FromBase to base $ToBase $ExampOut To work around this accuracy limitation, the following two functions were developed to convert an arbitrary-precision integer from any given base (2 to 36) into its base 10 equivalent and vice versa.  The converted result will be exactly correct, to the last digit, even if the result is thousands of digits long!  Just don't get too ridiculous.  These are the functions that performed the computations above.

The first-level functions used in this program handle integers only.

This is the first of the two base conversion functions used in this program.  It converts any arbitrary-precision integer from any given base (2 to 36) into its base 10 equivalent.

   function bcBaseB_Int_To_Base10 ($XBIntStr, $FromBase)
{
   $XB = strtoupper(trim($XBIntStr));
   $B  = $FromBase;

   if ($B < 2 or $B > 36) {return FALSE;}

   $sign = (substr($XB, 0,1) == '-')? '-' : '';

   if ($sign == '-') {$XB = substr($XB, 1, strlen($XB));}

   $X10 = 0;
   $piB = 1;
   $XBDigitsCount = strlen($XB);

   $DigitsSpectrum = substr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, $B);

   for ($i=0;   $i < $XBDigitsCount;   $i++)
  {
   $diB = substr($XB, $XBDigitsCount - $i - 1, 1);

   $di10 = strpos($DigitsSpectrum, $diB); if($di10 === FALSE) {return $di10;}

   $Xi10 = bcmul($piB, $di10);
   $X10  = bcadd($X10, $Xi10);
   $piB  = bcmul($piB, $B);
  }
   return "$sign$X10";
}


The following function converts an arbitrary-precision base 10 integer into its equivalent in any alternate base (from 2 to 36).  When used together, these functions allow easy two-way translation of an integer from any one base into another.

We use the first function, defined above, to convert the given integer argument into its base 10 equivalent and then we use the second function, defined below, to convert the base 10 value into the alternate base equivalent.

Since we are using arbitrary-precision arithmetic, all computations will be exactly accurate to the last digit, which could extend out to multiple thousands of digits.

   function bcBase10_Int_To_BaseB ($X10IntStr, $ToBase)
{
   $w = trim($X10IntStr);  if (!is_numeric($w)) {return FALSE;}

   $sign = (substr($w, 0,1) == "-")? "-" : "";

   if ($sign == "-") {$w = substr($w, 1, strlen($w));}

   if (bccomp($w, 0) == 0) {return "0";}

   $B = $ToBase;  if (!is_numeric($B) or $B < 2 or $B > 36) {return FALSE;}

   $DigitsSpectrum = substr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, $B);

   $XB = "";

   while (bccomp($w, 0) > 0)
         {
          $XB  = substr($DigitsSpectrum, bcmod($w, $B), 1) . $XB;
          $w = bcdiv($w, $B);
         }

   return $sign . $XB;
}

$ProgramVersion - Jay Tanner - $cYear
_HTML; // **************************************************************************** /* ======================================================================== This function will convert an arbitrary precision positive base 10 X value into its equivalent in any base from 2 to 36. The X value is NOT restricted to integers and also allows fractional values. ARGUMENTS: $XBase10 = Base 10 numeric string to be to converted into its BaseB equivalent. $BaseB = Base (2 to 36) in which to interpret input digit string. ERRORS: On error FALSE is returned. An error occurs if either of the arguments is non-numeric or out of valid range. This is a level 2 function. ======================================================================== */ function bcX_Base_10_To_Base_B ($xBase10, $BaseB, $TruncationLimit=0) { $p = 2000; // Decimal precision control factor (Don't ask). $q = $TruncationLimit; // Decimal truncation limit. $x = trim($xBase10); $sign = (substr($x,0,1) != "-")? "" : "-"; $x = str_replace("-","", $x); if (strpos($x, ".") === FALSE){$x .= ".0";} list($i, $f) = preg_split("[\.]", $x); $f = "0.$f"; $IntPartOut = bcXBase_10_Int_To_Base_B ($i, $BaseB); $w = bcmul(bcPow($BaseB, $p), $f); $fp = bcXBase_10_Int_To_Base_B ($w, $BaseB); $zeros = str_repeat("0", $p - strlen($fp)); if ($q > 0) {$fp = substr($fp, 0, $q-0);} $FracPartOut = ".$zeros$fp"; $FracPartOut = rtrim(rtrim($FracPartOut,'0'), '.'); return "$sign$IntPartOut$FracPartOut"; } /* ======================================================================== This boolean function tests to see of the given argument is a valid base B number. A digit spectrum for the given base is created. Then, if any of the digits given in the argument are not found within the digit spectrum, FALSE is returned to indicate the error. If no error found, then TRUE is returned. ======================================================================== */ function bcX_Is_Base_B ($NumArgStr, $BaseB) { // Read numeric string and remove any decimal or numerical sign. $X = str_replace(".", '', strtoupper(trim($NumArgStr))); $X = str_replace("-", '', $X); $X = str_replace("+", '', $X); $DigitsSpectrum = substr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, $BaseB); // Cycle through every digit. Error if any digit is not found // in DigitsSpectrum string and FALSE is returned. Otherwise, // TRUE is returned. $DigitsCount = strlen($X); for ($i=0; $i < $DigitsCount; $i++) { $digit = substr($X, $i, 1); if (strpos($DigitsSpectrum, $digit) === FALSE) {return FALSE;} } return TRUE; } /* ======================================================================== This function will convert an arbitrary precision integer in any base from 2 to 36 into its equivalent in base 10. ARGUMENTS: $Xb = Base B integer string to be converted into its base 10 equivalent. $FromBase = Base of input digit string (2 to 36). ERRORS: On error FALSE is returned. An error occurs if either of the arguments is invalid. ======================================================================== */ function bcBaseB_Int_To_Base10 ($XbStr, $FromBase) { // Read input arguments. $Xb = strtoupper(trim($XbStr)); $b = trim($FromBase); // ERROR if base of outside of range 2 to 36. if ($b < 2 or $b > 36) {return FALSE;} // Remember any numerical sign and work with absolute value. // The numerical sign will be restored at the end. $sign = (substr($Xb, 0,1) == '-')? '-' :''; if ($sign == '-') {$Xb = substr($Xb, 1, strlen($Xb));} // Count digits in base b integer X. $XbDigitsCount = strlen($Xb); // Initialize base 10 summation accumulator // and initial power (= 1) of the base. $X10 = 0; $PowB = 1; // Define the valid digits spectrum for the given base. $DigitsSpectrum = substr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, $b); // Perform loop to compute the base 10 // equivalent of the base b integer Xb. for ($i=0; $i < $XbDigitsCount; $i++) { // Get current base b digit from Xb. $db = substr($Xb, $XbDigitsCount - $i - 1, 1); // Get base 10 numerical value (= string index) of digit. $d10 = strpos($DigitsSpectrum, $db); // If FALSE is returned, it means that the base b digit was // not found within the valid base b DigitsSpectrum string. if ($d10 === FALSE) {return FALSE;} // Compute and accumulate value of current base 10 term. $X10 = bcadd($X10, bcmul($PowB, $d10)); // Update power of base for next cycle. $PowB = bcmul($PowB, $b); } return $sign . $X10; } // End of bcBaseB_Int_To_Base10(...) /* ======================================================================== This function will convert an arbitrary precision positive decimal integer into its equivalent in any given base from 2 to 36. ARGUMENTS: $Base10IntStr = Base-10 integer string to be to converted into its BaseB equivalent. $BaseB = Base of converted output (2 to 36). ERRORS: On error FALSE is returned. An error occurs if either of the arguments is non-numeric or out of valid range. ======================================================================== */ function bcBase10_Int_To_BaseB ($x10IntStr, $BaseB) { // --------------------------------------- // Error if either argument is non-numeric // or if base of out of range (2 to 36). if (!is_numeric($x10IntStr) or !is_numeric($BaseB)) {return FALSE;} if ($BaseB < 2 or $BaseB > 36) {return FALSE;} // ------------------------------------------ // Define available digits for bases 2 to 36. $BaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; $w = ''; $x = trim($x10IntStr); // Remember any numerical sign and work with absolute value. // The numerical sign will be restored at the end. $sign = (substr($x, 0,1) == '-')? '-' :''; if ($sign == '-') {$x = substr($x, 1, strlen($x));} if (bccomp($x, "0") == 0) {return "0";} while (bccomp($x, "0") > 0) { $w = substr($BaseDigits, bcmod($x, $BaseB), 1) . $w; $x = bcdiv($x, $BaseB); } return $sign . $w; } // End of bcBase10_Int_To_BaseB(...) /* ======================================================================== Quick utility function to convert ±seconds of arc or time to ±HMS resolved to up to 3 decimals. The default setting is to the nearest second. No symbols are attached to the returned HMS values. The value of the hours may exceed ±24h No error checking is done. ======================================================================== */ function Seconds_To_HMS ($SecondsArg, $NumDecimals=0, $PosSign='') { // ----------------------------------------------- // Construct output format string for decimal part // based on the (NumDecimals) argument. $ssOutputFormat = "%2.$NumDecimals" . "f"; // -------------------------------------------------------------- // Read output numerical sign symbol. A positive value will have // a plus (+) symbol attached if this value is '+', otherwise it // will NOT have any plus symbol. Negative values will not be // effected by this setting. $SignOutput = trim($PosSign); // -------------------------------------------- // Remember numerical sign of seconds argument. $sign = ($SecondsArg < 0)? '-' : '+'; if ($SignOutput == '') {$sign = $SignOutput;} // ------------------------------------------ // Compute HMS elements equivalent to seconds // and rounded-off to the nearest specified // number of decimals (0 to 3). $DegHrs = abs($SecondsArg) / 3600; $dh = floor($DegHrs); $minutes = 60*($DegHrs - $dh); $mm = floor($minutes); $seconds = 60*($minutes - $mm); $ss = ($NumDecimals == 0)? floor($seconds + 0.5) : $seconds; // ------------------------------------ // Patch for that blasted '60s problem. if ($ss == 60) {$ss=0; $mm++;} if ($mm == 60) {$mm=0; $hh++;} // ---------------------------------- // Format the DHMS values for output. $dh = sprintf("%02d", $dh); $mm = sprintf("%02d", $mm); $ss = sprintf($ssOutputFormat, $ss); // ---------------------------------------------------------------- // Put ss value of integer part into double-digit format if ss < 10 // The value 9.123 would be converted into 09.123 $ss = ($ss < 10)? "0" . trim($ss) : trim($ss); // --------------------------------- // Construct and return the angle or // time elements string. return "$sign$dh" . ":$mm" . ":$ss"; } // End of Seconds_To_HMS(...) /* ======================================================================== This function generates a string of any number of random digits in any given base from 2 to 36. ERRORS: No error checking is done. ======================================================================== */ function Random_Digits_Base_B ($NumDigits=1, $base=10) { // --------------- // Read arguments. $n = trim($NumDigits); $b = trim($base); // ------------------------------------------- // Define available digits for the given base. $DigitsSpectrum = substr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, $b); // ---------------------------------------- // Define accumulated digits output string. $RandDigits = ''; // ------------------------------------------- // Construct an n-digit random sequence in the // given base, one digit at a time using a // random 50/50 digit alternation method. // There must be at least one digit. for ($i=0; $i < $n; $i++) { if (MT_Rand() % 2 == 0) {$RandDigits .= substr($DigitsSpectrum, MT_Rand(0, $b-1), 1);} else {$RandDigits = substr($DigitsSpectrum, MT_Rand(0, $b-1), 1) . $RandDigits;} } return $RandDigits; } // End of Random_Digits_Base_B(...) ?>