Как можно преобразовать в PHP большое / высокоточное число с плавающей точкой? - PullRequest
0 голосов
/ 13 апреля 2019

Как бы ни был обобщен вопрос, мне действительно тяжело изучение, в частности, о том, как конвертировать большие высокопрецизионные значения с плавающей точкой в ​​PHP с использованием BCMath.

Я пытаюсь конвертировать что-то вроде

1234.5678900000

до

4D2.91613D31B

Как я могу это сделать?

Я просто хочу base-10 & rarr; base-16, но преобразование для произвольных оснований с плавающей точкой, вероятно, было бы наиболее полезным ответом и для других.


  • Как преобразовать огромное целое число в гекс в php? включает в себя BC, но только для целых чисел.

  • https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/ исследует числа с плавающей запятой, но только в контексте десятичного <-> двоичного файла. (Он говорит, что расширение кода для других баз - это легко, и, вероятно, это так (используя код из предыдущего пункта), но я не знаю, как обосновать правильность результата, которого я достигну.)

  • Быстрые логарифмы произвольной точности с bcmath также основаны на числах с плавающей запятой, но в контексте переопределения высокой точности log(). (Там есть упоминание о преобразовании баз, но вместе с примечаниями о том, как BC тупо использует PHP в PHP () и теряет точность.)

Другие результаты, которые я обнаружил, просто говорят о собственном принуждении с плавающей точкой в ​​PHP и вообще не имеют отношения к BC.

1 Ответ

1 голос
/ 13 апреля 2019

До 36 базовых преобразований с высокой точностью

Я думаю, что этот вопрос слишком сложен для переполнения стека. Мало того, что вы хотите преобразовать базовые числа с плавающей запятой, что само по себе немного необычно, но это должно быть сделано с высокой точностью. Это, конечно, возможно, но не многие люди найдут решение для того, чтобы валяться и делать что-то, требующее времени Математика базового преобразования не очень сложна, и как только вы поймете это, вы сможете решить это самостоятельно.

Ну, короче говоря, я не смог устоять перед этим и попробовал.

<code><?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 '
';

Вывод:

4D2.91613D31B9B66F9335D249E44FA05143BF727136A400FBA8826AA8EB4634

Это выглядит немного сложно, но на самом деле это не так. Это просто занимает время. Я начал с преобразования целых чисел , затем добавил дроби, и когда все это заработало, я добавил все математические функции BC.

Аргумент $scale представляет количество разыскиваемых десятичных разрядов.

Может показаться немного странным, что я использую три функции для преобразования: toDigits(), toNumber() и baseConv(). Причина в том, что математические функции BC работают с базой 10. Таким образом, toDigits() преобразуется из 10 в другую базу, а toNumber() делает обратное. Чтобы конвертировать между двумя оперантами с произвольной базой, нам нужны обе функции, и в результате получается третья: baseConv().

Это может быть дополнительно оптимизировано, если это необходимо, но вы не сказали нам, для чего это нужно, поэтому оптимизация не была для меня приоритетом. Я просто пытался заставить это работать.

Вы можете получить более высокие базовые конверсии, просто добавив больше символов. Однако в текущей реализации каждый символ должен быть одним символом. С UTF8, который на самом деле не ограничивает вас, но убедитесь, что все является многобайтовой совместимостью (чего в данный момент нет).

ПРИМЕЧАНИЕ: похоже, работает, но я не даю никаких гарантий. Тщательно проверьте перед использованием!

...