На Си не подскажу, но ты без труда перепишешь код ниже.
"mb_{что-то}" в пхп - работа с мультибайтовыми строками, UTF-8.
"bc{что-то}" в пхп - работа с числами как со строками без float потерь
Перевод дробной части организован как обратный процесс. Каждый разряд дробной части умножается на основание системы счисления (целая часть умножения - номер символа в системе счисления), каждый шаг целая часть отбрасывается и дробная снова умножается и так до тех пор, пока не умножится нацело, или пока не достигнут лимит числа требуемых разрядов.
Тесты:
<?php
$res = _base_convert_floor(100, '0ABCDEFGHIJKLMNOPQRSTUVWXYZ', '01');
$res2 = _base_convert_floor($res, '01', '0ABCDEFGHIJKLMNOPQRSTUVWXYZ', 0);
var_dump($res); // string(1) "D"
var_dump($res2); // string(3) "100"
$res = _base_convert_frac('.75', '01', '0123456789');
$res2 = _base_convert_frac($res, '0123456789', '01');
var_dump($res); // string(2) ".11"
var_dump($res2); // string(2) ".75"
$res = _base_convert('-10.75', '01', '0123456789');
$res2 = _base_convert($res, '0123456789', '01');
var_dump($res); // string(7) "-1010.11"
var_dump($res2); // string(5) "-10.75"
$res = _base_convert_floor('КУКУЁПТА', '0123456789', 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ');
$res2 = _base_convert_floor($res, 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ', '0123456789');
var_dump($res); // string(1) "495086673543"
var_dump($res2); // string(1) "КУКУЁПТА"
// @gzhegow > бонус, колонка "как в экселе"
$res = _base_convert_floor(27, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '0123456789', -1);
$res2 = _base_convert_floor($res, '0123456789', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 0, -1);
var_dump($res); // string(2) "AA"
var_dump($res2); // string(2) "27"
Исходник:
<?php
function _base_convert_floor($floor,
string $baseCharsTo = null, string $baseCharsFrom = '0123456789',
int $baseShiftTo = 0, int $baseShiftFrom = 0
) : string
{
$floor = strval($floor);
$baseTo = mb_strlen($baseCharsTo);
$baseFrom = mb_strlen($baseCharsFrom);
if (! $len = mb_strlen($floor)) return '';
if ($baseTo < 2) throw new \InvalidArgumentException('Conversion assume `baseCharsTo` to be at least 2 letters: ' . $baseCharsTo);
if ($baseFrom < 2) throw new \InvalidArgumentException('Conversion assume `baseCharsFrom` to be at least 2 letters: ' . $baseCharsFrom);
$baseCharsFromIndex = array_flip(mb_str_split($baseCharsFrom));
for ( $i = 0; $i < $len; $i++ ) {
if (! isset($baseCharsFromIndex[ mb_substr($floor, $i, 1) ])) {
throw new \InvalidArgumentException('The `floor` contains char outside `baseCharsFrom`: ' . $floor[ $i ]);
}
}
$baseChars10 = '0123456789';
if ($baseCharsFrom === $baseCharsTo) {
return $floor;
} elseif ($baseCharsFrom === $baseChars10) {
$result = [];
$div = $floor;
if (0 > bccomp(bcadd($div, $baseShiftTo, 0), 0, 0)) {
throw new \RuntimeException('Unable to convert cause of `baseShiftTo`: ' . $div);
}
do {
$div = bcadd($div, $baseShiftTo, 0);
$mod = bcmod($div, $baseTo, 0);
$div = bcdiv($div, $baseTo, 0);
$result[] = mb_substr($baseCharsTo, (int) $mod, 1);
} while ( bccomp($div, 0, 1) );
$result = implode('', array_reverse($result));
} elseif ($baseCharsTo === $baseChars10) {
$result = '0';
for ( $i = 1; $i <= $len; $i++ ) {
$idx = $baseCharsFromIndex[ mb_substr($floor, $i - 1, 1) ];
$idx = bcsub($idx, $baseShiftFrom, 0);
$pow = bcpow($baseFrom, $len - $i);
$sum = bcmul($idx, $pow, 0);
$result = bcadd($result, $sum, 0);
}
} else {
$result = $floor;
$result = _base_convert_floor($result, $baseChars10, $baseCharsFrom, 0, $baseShiftFrom);
$result = _base_convert_floor($result, $baseCharsTo, $baseChars10, $baseShiftTo, 0);
}
return $result;
}
function _base_convert_frac(string $frac,
string $baseCharsTo = null, string $baseCharsFrom = '0123456789',
int $scale = null
) : string
{
$frac = ltrim($frac, '.');
$baseTo = mb_strlen($baseCharsTo);
$baseFrom = mb_strlen($baseCharsFrom);
if (! $len = mb_strlen($frac)) return '';
if ($baseTo < 2) throw new \InvalidArgumentException('Conversion assume `baseCharsTo` to be at least 2 letters: ' . $baseCharsTo);
if ($baseFrom < 2) throw new \InvalidArgumentException('Conversion assume `baseCharsFrom` to be at least 2 letters: ' . $baseCharsFrom);
$baseCharsFromIndex = array_flip(mb_str_split($baseCharsFrom));
for ( $i = 0; $i < $len; $i++ ) {
if (! isset($baseCharsFromIndex[ mb_substr($frac, $i, 1) ])) {
throw new \InvalidArgumentException('The `frac` contains char outside `baseCharsFrom`: ' . $frac[ $i ]);
}
}
$scale = $scale ?? $len;
$scale = max($scale, 0);
$baseChars10 = '0123456789';
if ($baseCharsFrom === $baseCharsTo) {
return $frac;
} elseif ($baseCharsFrom === $baseChars10) {
$result = [];
$mul = bcadd('0.' . $frac, 0, $scale);
$limit = $scale;
while ( $limit-- ) {
$mul = bcmul($mul, $baseTo, $scale);
$floor = bcadd($mul, 0, 0);
$mul = bcsub($mul, $floor, $scale);
$result[] = mb_substr($baseCharsTo, (int) $floor, 1);
if (0 === bccomp($mul, 0, $scale)) break;
}
$result = implode('', $result);
} elseif ($baseCharsTo === $baseChars10) {
$result = '0';
for ( $i = 1; $i <= $len; $i++ ) {
$idx = $baseCharsFromIndex[ mb_substr($frac, $i - 1, 1) ];
$pow = bcpow($baseFrom, -$i, $scale);
$sum = bcmul($idx, $pow, $scale);
$result = bcadd($result, $sum, $scale);
}
$result = explode('.', $result, 2)[ 1 ] ?? '0';
} else {
$result = $frac;
$result = _base_convert_frac($result, $baseChars10, $baseCharsFrom);
$result = _base_convert_frac($result, $baseCharsTo, $baseChars10);
}
return '.' . $result;
}
function _base_convert($num,
string $baseCharsTo = null, string $baseCharsFrom = '0123456789',
int $scale = null,
int $baseShiftTo = 0, int $baseShiftFrom = 0
)
{
$isNegative = 0 === mb_strpos($num, '-');
$numAbs = ltrim($num, '-');
[ $numFloor, $numFrac ] = explode('.', $numAbs, 2) + [ '0', '' ];
$scale = $scale ?? mb_strlen($numFrac);
$result = [];
if (mb_strlen($numFloor)) $result[] = _base_convert_floor($numFloor, $baseCharsTo, $baseCharsFrom, $baseShiftTo, $baseShiftFrom);
if (mb_strlen($numFrac)) $result[] = _base_convert_frac($numFrac, $baseCharsTo, $baseCharsFrom, $scale);
$result = implode('', $result);
if ($isNegative) {
$result = '-' . $result;
}
return $result;
}