如何在PHP中基本转换大/高精度浮点数?

问题描述 投票:0回答:1

作为一个问题的通用,我似乎很难学习如何使用BCMath在PHP中基本转换大型高精度浮点值。

我正在尝试基本转换类似的东西

1234.5678900000

4D2.91613D31B

我怎样才能做到这一点?

我只想要base-10→base-16,但是任意基础浮点数的转换也可能是其他人最有用的答案。


我发现的其他结果只是谈论PHP自己的浮动强制,而根本不涉及BC。

php floating-point precision base-conversion bcmath
1个回答
1
投票

Up to base 36 conversions with high precision

我认为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数学函数都放入其中。

$scale参数表示所需的小数位数。

我使用三个函数进行转换可能看起来有点奇怪:toDigits()toNumber()baseConv()。原因是BC Math函数的基数为10.因此,toDigits()从10转换为另一个基数,而toNumber()则相反。要在两个任意基础操作符之间进行转换,我们需要两个函数,这导致第三个:baseConv()

如果需要,这可以进一步优化,但你没有告诉我们你需要它,所以优化不是我的优先考虑。我只是想让它发挥作用。

只需添加更多符号即可获得更高的基本转换次数。但是,在当前实现中,每个符号需要是一个字符。使用UTF8并不能真正限制你,但要确保所有内容都是多字节兼容的(目前还没有)。

注意:它似乎工作,但我不给任何保证。使用前彻底测试!

© www.soinside.com 2019 - 2024. All rights reserved.