Converting Non-Integer Base 10 Values Into Another Base
Yo, Syntax
I'm not sure what your level of experience in mathematics is, but I'll try to explain the process of converting a non-integer base *0 number into its equivalent in another base. It can't always be done exactly as in the case of integers.
For higher precision, arbitrary precision operators must be used.
To convert a non-integer value from base *0 into base B, it is easier to do if we treat each part of the number separately. That is, convert the integer value first, separately from the decimal value.
When you multiply any base *0 value by a power of *0 (the base), notice that only the decimal point changes position, but the sequence of digits remains constant.
For example, multiplying 0.*4*5* by *00000 gives *4*5* as a result. The sequence of digits will remain the same. This rule also applies to other bases and is what makes it possible to convert non-integer values into another base.
For example, in the case of pi:
The base *0 value of the constant pi to 64 decimals can be defined by the PHP code:
define ("pi", "*.*4*5*265*58*7**2*846264**8*27*502884**7*6*****75*05820*74*445*2*");
The task will be to convert pi into its base 2 equivalent.
We first split the base *0 input argument into its separate integer and fractional parts ($I, $F) respectively.
Let
[code]
$I = "*";
$F = "0.*4*5*265*58*7**2*846264**8*27*502884**7*6*****75*05820*74*445*2*";
[/code]
Let's use P = *00
and Q = (Base ^ P) = 2^*00 = *26765060022822*40*4*670*205*76
This Q value can be computed by the PHP code:
[code]
$Q = bcPow("2", *00); // = *26765060022822*40*4*670*205*76
[/code]
Now compute
w = F *5; Q (truncated, not rounded, to an integer value)
[code]
$w = $bcMul($F, $Q); // = *7*4*00*2***00**5***824*4*7824
[/code]
Convert the base *0 integer ($w) into base 2, which gives the fractional digit sequence
F = *00*0000******0**0*0*0*000*000*0000*0**0*000**0000*000**0*00**000*00**000**00**000*0*000*0***00000
The length of F is *8 digits in this case.
There may be one or more zeros following the decimal point and prior to the converted fractional string computed above.
To determine how many zeros (if any) follow the decimal point:
[code]
$NumZeros = $P - StrLen($F);
[/code]
In this case it works out to
NumZeros = (*00 - *8) = 2
This means we attach 2 zeros to the beginning of $F (leftmost end).
Which gives
00*00*0000******0**0*0*0*000*000*0000*0**0*000**0000*000**0*00**000*00**000**00**000*0*000*0***00000
Now simply take the converted integer part, attach a decimal point and then attach the fractional part after it to obtain
**.00*00*0000******0**0*0*0*000*000*0000*0**0*000**0000*000**0*00**000*00**000**00**000*0*000*0***00000
which is the required pi value expressed in base 2, to about *00 decimals, with a slight rounding fuzz factor at the end.
Like many base *0 fractional values, pi cannot be expressed exactly in any integer base. There will always be a fuzzy rounding error of some sort.
The rule is, the higher the power of the base used as a multiplier for the fractional part, the farther the accuracy of the converted decimal value extends.
The following function converts a base *0 integer into another base.
[code]
function bcBase*0_To_BaseR($ArgN, $ArgR)
{
/*
This function performs a simple base conversion for
base *0 integers into any other base from 2 to 62.
Author: Jay Tanner &#*6*;2007
PHP v4.4.4
Released under provisions of GPL v*
http://www.gnu.org/licenses
---------
ARGUMENTS
$ArgN = Positive base *0 integer string argument.
$ArgR = Radix argument (base) of output integer string.
The radix can range from 2 to 62, but it is
possible to extend it further.
------
ERRORS
An error results if any argument is invalid,
in which case, boolean FALSE is returned.
*/
// Define digits available for the base conversions.
// NUMBERS ARE CONSIDERED CASE SENSITIVE WITH THE EXCEPTION OF DIGITS 0-*.
$digits = "0*2*45678*abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Read input arguments and truncate
// to integer values if necessary.
$N = bcAdd(trim($ArgN), "0", 0);
$R = bcAdd(trim($ArgR), "0", 0);
// Error if radix (base) is outside valid range.
if ($R < 2 || $R > 62) {return FALSE;}
// Handle special case where N=0
if ($N == 0) {return 0;}
// Compute radix R equivalent to
// original base *0 argument.
$out = "";
while ($N != 0)
{
// Compute and fetch current base R digit.
$d = SubStr($digits, bcMod($N, $R), *);
// Collect current base R digit in
// leftmost output string position.
$out = "$d$out";
// Update $N value.
// Done when $N==0
// Otherwise, repeat
// using new $N value.
$N = bcDiv($N, $R, 0);
}
// Done.
return $out;
} // End of bcBase*0_To_BaseR()
[/code]
The following function is for converting non-integer base *0 numbers into another base. It requires the function defined above.
[code]
function bcBase*0_To_BaseB ($Base*0ArgStr, $ToBaseArg)
{
/*
This function converts a non-integer base *0 value into
its equivalent in any other integer base.
Author: Jay Tanner &#*6*;2007
PHP v4.4.4
Released under provisions of GPL v*
http://www.gnu.org/licenses
----------
DEPENDENCY
This function requires the bcBase*0_To_BaseR() integer
conversion function.
*/
// ---------------------
// Read input arguments.
$x = trim($Base*0ArgStr);
$b = trim($ToBaseArg);
// ---------------------------------------------------
// Compute 200th power of base. For higher precision,
// increase the 200 to a higher value.
$q = "200";
$p200 = bcPow($b, $q);
// --------------------------------------------------------------
// Separate base *0 x argument into integer and fractional parts.
$dp = StrPos($x, "."); if ($dp === FALSE) {$x .= ".0"; $dp = StrPos($x, ".");}
$xInt = SubStr($x, 0, $dp); if ($xInt == "") {$xInt = "0";}
$xFrx = "0." . SubStr($x, $dp+*, StrLen($x));
// --------------------
// Compute new X value.
$X = bcMul($p200, $xFrx);
// --------------------------------------------------------
// Compute converted fractional digits minus decimal point.
$IntOut = bcBase*0_To_BaseR($xInt, $b);
$FrxOut = bcBase*0_To_BaseR($X, $b);
// ---------------------------------------
// Get length of fractional string result.
$FrxL = StrLen($FrxOut);
// -----------------------------------
// Compute number of zeros immediately
// following the decimal point.
$nZ = 200 - $FrxL;
// -------------------------------
// Construct string of ($nZ) zeros
// to follow the decimal point.
$zeros = Str_Repeat("0", $nZ);
// ------------------------------------
// Attach fractional sequence to zeros.
$FrxOut = "$zeros$FrxOut";
// --------------------------------------
// Truncate fractional part to 50 digits.
$FrxOut = SubStr($FrxOut, 0, 50);
// --------------------------------------
// Done. Return converted integer part
// attached to converted fractional part.
return "$IntOut.$FrxOut";
} // End of bcBase*0_To_BaseB()
[/code]
I hope this isn't too complicated.
LOL
The functions could use some beta testing, since there are probably some bugs to be worked out. The error checking is not extensive.
USAGE:
To use the function to convert 2.7** into binary,
[code]
print bcBase*0_To_BaseB ("2.7**", 2);
[/code]
The value returned will be:
*0.*0**0**0*0000***00*0*0**000000*00000**000*00*00**0
to 50 decimals.
The value 0.2* (base *0) converted into base 5 is:
0.*0************************************************
to 50 decimals
DANGER Will Robinson - DANGER
[QUOTE=SyntaX******;228*5]Ahhh, perfectly explained. I thought that could work, and understood it worked in base*0, but my insufficient math skills told me not to waist my time trying something that might not work.
Next week I will code this into the base converter using string representations of integer values for larger decimal precision. I'll give 2^*024 on the left and 2^*024 on the right side of the decimal.
I definitely agree with your selection of digits:
$digits = "0*2*45678*abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Its logical to most of us, but the ASCII standard F's things up jumping from 0-* to :;>=< ... I think the only real use for anything from character sets from **-255 would be for encryption purposes. I'm coming up with my own, uncrackable encryption methods so it may be very useful to me, but for everyday purposes... useless.
Thanks again for the conversion example![/QUOTE]
After some experimentation, I found a potentially serious flaw in using the letters in the way I did for the base conversions.
The base conversion logic is OK and consistent. The problem lies in the ASCII codes used for the characters and the way the computer interprets them.
This only becomes a problem if you intend to use the computer to sort a list of numbers made in bases that use alpha characters.
The ASCII codes of the uppercase letters are greater than the ASCII codes of the corresponding lowercase letters because uppercase letters come before lowercase letters in the ASCII code spectrum and will be sorted ahead of them. This is contrary to the numerical values we apply to the characters when used for numbers rather than words.
This is not good.
Since letter case is important in this instance, it leads to a list of numbers not sorting correctly in proper order if they contain any mix of upper/lowercase letters.
There are ways around this, the simplest being to switch places with upper/lowercase letters in the allowed digits string. Then all hexadecimal numbers would ALWAYS use uppercase A-Z.
Just change
$digits = "0*2*45678*abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
to
$digits = "0*2*45678*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
and the numerical sorting problem is solved, regardless of the base used.
It seems that uppercase following lowercase is a more logical way to do it, but it works the opposite way in ASCII and that can effect anything that involves ASCII sorting algorithms.
I just thought anyone using my particular method should know about this little glitch. It could lead to erroneous sorting problems if not taken into account in some circumstances.