就像这个问题一样,我很难过 专门学习如何使用BCMath对PHP中的大型高精度float值进行基本转换。
我正在尝试对类似的内容进行基本转换
1234.5678900000
到
4D2.91613D31B
我该怎么做?
我只想要base-10→base-16,但是对任意基数浮点数的转换也可能对其他人提供最有用的答案。
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation涉及BC,但仅适用于整数。
How to convert a huge integer to hex in php?探索浮点数,但仅在十进制<->二进制的上下文中。 (它说将代码扩展到其他基础很容易,而且可能很容易(使用上一点中的代码),但是我不知道如何通过得出的结果的正确性来进行推理。)
https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/也是基于浮点的,但是在重新实现高精度log()
的情况下。 (不过,这里提到了转换基址,以及有关BC如何笨拙地使用PHP自己的pow()并失去精度的说明。)
我发现的其他结果只是在谈论PHP自身的浮动强制,而与BC根本无关。
答案 0 :(得分:1)
我认为这个问题对于Stack Overflow来说有点太难了。您不仅要对浮点进行基转换,这本身并不常见,而且必须以很高的精度完成。这当然是可能的,但是没有多少人能够解决这个问题,并且花很多时间。基本转换的数学不是很复杂,一旦您理解了它,就可以自己解决。
哦,总而言之,我忍不住要尝试一下。
<?php
function splitNo($operant)
// get whole and fractional parts of operant
{
if (strpos($operant, '.') !== false) {
$sides = explode('.',$operant);
return [$sides[0], '.' . $sides[1]];
}
return [$operant, ''];
}
function wholeNo($operant)
// get the whole part of an operant
{
return explode('.', $operant)[0];
}
function toDigits($number, $base, $scale = 0)
// convert a positive number n to its digit representation in base b
{
$symbols = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$digits = '';
list($whole, $fraction) = splitNo($number);
while (bccomp($whole, '0.0', $scale) > 0) {
$digits = $symbols{(int)bcmod($whole, $base, $scale)} . $digits;
$whole = wholeNo(bcdiv($whole, $base, $scale));
}
if ($scale > 0) {
$digits .= '.';
for ($i = 1; $i <= $scale; $i++) {
$fraction = bcmul($fraction, $base, $scale);
$whole = wholeNo($fraction);
$fraction = bcsub($fraction, $whole, $scale);
$digits .= $symbols{$whole};
}
}
return $digits;
}
function toNumber($digits, $base, $scale = 0)
// compute the number given by digits in base b
{
$symbols = str_split('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ');
$number = '0';
list($whole, $fraction) = splitNo($digits);
foreach (str_split($whole) as $digit) {
$shiftUp = bcmul($base, $number, $scale);
$number = bcadd($shiftUp, array_search($digit, $symbols));
}
if ($fraction != '') {
$shiftDown = bcdiv('1', $base, $scale);
foreach (str_split(substr($fraction, 1)) as $symbol) {
$index = array_search($symbol, $symbols);
$number = bcadd($number, bcmul($index, $shiftDown, $scale), $scale);
$shiftDown = bcdiv($shiftDown, $base, $scale);
}
}
return $number;
}
function baseConv($operant, $fromBase, $toBase, $scale = 0)
// convert the digits representation of a number from base 1 to base 2
{
return toDigits(toNumber($operant, $fromBase, $scale), $toBase, $scale);
}
echo '<pre>';
print_r(baseConv('1234.5678900000', 10, 16, 60));
echo '</pre>';
输出为:
4D2.91613D31B9B66F9335D249E44FA05143BF727136A400FBA8826AA8EB4634
它看起来有点复杂,但并非如此。这只需要时间。我从converting whole numbers开始,然后添加了分数,当所有这些都起作用时,我放入了所有BC Math函数。
$scale
参数表示所需的小数位数。
我使用三个函数进行转换可能看起来有些奇怪:toDigits()
,toNumber()
和baseConv()
。原因是BC Math函数的基数为10。因此,toDigits()
从10转换为另一个基数,而toNumber()
则相反。要在两个任意基运算符之间进行转换,我们需要两个函数,这将导致第三个函数:baseConv()
。
可以根据需要进一步优化,但是您没有告诉我们您需要什么,因此优化并不是我的优先考虑。我只是试图使其工作。
只需添加更多符号,即可获得更高的基本转化率。但是,在当前的实现中,每个符号都必须是一个字符。使用UTF8并不会真正限制您,但是请确保所有内容都是多字节兼容的(目前还不兼容)。
注意:似乎可行,但我不做任何保证。使用前进行彻底测试!