(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

AzaMath - Cистемы счисления (включая кастомные) + арифметика произвольной точности на PHP

Источник: habrahabr
samally

Возникла недавно задача, связанная с конвертацией между различными позиционными системами счисления.

В качестве реального применение обычно выступают сервисы для сокращения URL, использующие системы base36/base62 или, например, хранение большого количества огромных чисел в том же base62 для экономии памяти.

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

Получилась AzaMath - библиотека для конвертации между системами счисления (включая кастомные) + удобная арифметика произвольной точности.

Поддерживает PSR-0, легко ставится через composer, 100% покрытие кода тестами.

Системы счисления


Основной задачей библиотеки, на текущий момент, является перевод маленьких, больших и огромных чисел из одной позиционной системы счисления в другую. Поддержка стандартных систем с основанием от 2 до 62 включительно, а также поддержка позиционных систем со своим собственным алфавитом (хоть все символы ASCII). Также желательно поддерживать отрицательные числа и дроби.

Для начала анализ альтернативных реализаций, которые можно использовать.

Вариант 1 - Специальные функции (decbin, bindec, decoct, ...)

Шесть функций, входящих в PHP, для конвертации между самыми распространенными системами счисления (10, 2, 8, 16).
Плюсы Минусы
  • Самый быстрый способ

  • Платформозависимый формат отрицательного числа. Без использования знака '-'. Это не подходит, потому что у нас могут быть числа любого размера
  • Максимальное поддерживаемое число равно константе PHP_INT_MAX
  • Дроби не поддерживаются
  • Игнорирует некорректные символы в исходном числе, но только при конвертации в десятичное число. В обратную сторону уже не работает.
  • Ограниченное число систем счисления


Вариант 2 - base_convert

Стандартная функция PHP, для конвертации между системами счисления от 2 до 36 включительно.
Плюсы Минусы
  • Игнорирует некорректные символы в исходном числе

  • Отрицательные числа не поддерживаются
  • Максимальное поддерживаемое число равно константе PHP_INT_MAX + 1
  • Дроби не поддерживаются
  • Ограниченное число систем счисления
  • Медленнее на 40-70%, чем специальные функции


Вариант 3 - GMP (gmp_strval(gmp_init($number, $x), $y))

При наличии расширения GMP можно использовать функции для изменения системы счисления оттуда. Поддерживаются системы от 2 до 62 и от -2 до -36 (обычные системы 2-36, только на выходе буквы в верхнем регистре).
Плюсы Минусы
  • Отрицательные числа поддерживаются нормально
  • Нет ограничения на размер числа
  • Поддерживаются все стандартные системы счисления (2-62)

  • Дроби не поддерживаются
  • Ошибка конвертации при некорректных символах в исходном числе
  • Медленнее на 70-90%, base_convert
  • Медленнее на 70-215%, чем специальные функции

Свой вариант конвертации был реализован полностью независимо от сторонних расширений и без промежуточного конвертирования в десятичную системы, что позволило ему быть настолько быстрым, насколько это возможно на PHP (это медленно :) - примерно на 6000% медленнее, чем GMP реализация. Поэтому, при возможности, всегда используется более быстрая реализация. Также очень рекомендуется установить расширение GMP, если вдруг понадобится много и регулярно конвертировать числа. Отрицательные числа полностью поддерживаются.

В комплект библиотеки, в качестве примера, входят три нестандартные системы счисления: base32 и base64 c алфавитами известных систем кодирования по стандарту RFC4648, также base64url из того же стандарта - base64 c алфавитом безопасным для использования в URL. Следует учитывать, что это конвертация чисел, а не произвольных строк, поэтому результат base64 будет совершенно отличным от результата функции base64_encode.

Увы, поддержку дробей пока не закончил - не удалось сделать достаточно быстро и пришлось отложить. Возможно сделаю позже, либо, если кому-то это понадобится, с удовольствием приму pull-реквест с этой фичей.

Примеры использования


$res = NumeralSystem::convert('WIKIPEDIA', 36, 10);
echo $res . PHP_EOL; // 91730738691298

$res = NumeralSystem::convert('9173073869129891730738691298', 10, 16);
echo $res . PHP_EOL; // 1da3c9f2dd3133d4ed04bce2

$res = NumeralSystem::convertTo('9173073869129891730738691298', 62);
echo $res . PHP_EOL; // BvepB3yk4UBFhGew

$res = NumeralSystem::convertFrom('BvepB3yk4UBFhGew', 62);
echo $res . PHP_EOL; // 9173073869129891730738691298

// Добавляем новую систему c произвольным алфавитом.
// Каждый символ должен встречаться только один раз.
// Допустимы только ASCII символы.
$alphabet = '!@#$%^&*()_+=-'; // эквивалент base14
$name     = 'StrangeSystem';
NumeralSystem::setSystem($name, $alphabet);

$number = '9999';
$res = NumeralSystem::convertTo($number, $name);
echo $res . PHP_EOL; // $)!$

$res = NumeralSystem::convertFrom($res, $name);
echo $res . PHP_EOL; // 9999

Арифметика произвольной точности


Оперирование над большими числами выполнено в качестве удобной обертки над расширением BCMath. В исходном состоянии BCMath не слишком удобен для реального применения. Одно только наполнение результата нулями до указанной точности (1234.2130000000...) чего стоит.

Поддерживаются все основные арифметические действия, а также возведение в степень, получение квадратного корня, остатка от деления, битовый сдвиг, округление, сравнения и некоторые другие операции.

Все числа внутри представляются в виде строки, но на входе принимаются и типы int/float. При тестах быстро выяснилось, насколько сильно теряется точность с типом данных float. К тому же экспоненциальная запись (12e26, 12e-26) вообще не поддерживается на уровне BCMath. Так что пришлось искать возможность сохранения максимально доступной точности, поэтому все числа с плавающей точкой на входе обрабатываются специальным алгоритмом, сохраняя точность, насколько это возможно.

Примеры использования


// Создаем новое число с указанной точностью вычислений - 20 (по умолчанию 100)
$number = new BigNumber('118059162071741130342591466421', 20);

// Деление
$number->divide(12345678910);
echo $number . PHP_EOL; // 9562792207086578954.49764831288650451382

// Снова деление с округлением с указанной точностью и алгоритмом
// Поддерживаются три алгоритма округления:
//  1) HALF_UP - округление до целого наверх от *.5 (по умолчанию)
//  2) HALF_DOWN - округление до целого вниз от *.5
//  3) CUT, обрезка числа до указанной точности, все далее просто отбрасывается
// Использовать можно, с помощью констант BigNumber::ROUND_* или
// со стандартными константами PHP - PHP_ROUND_HALF_UP, PHP_ROUND_HALF_DOWN
$number->divide(9876543210)->round(3, PHP_ROUND_HALF_DOWN);
echo $number . PHP_EOL; // 968232710.955

// Сравнения чисел
$number = new BigNumber(10);
echo ($number->compareTo(20) < 0) . PHP_EOL; // 1
echo $number->isLessThan(20) . PHP_EOL; // 1

$number = new BigNumber(20);
echo ($number->compareTo(10) > 0) . PHP_EOL; // 1
echo $number->isGreaterThan(10) . PHP_EOL; // 1

$number = new BigNumber(20);
echo ($number->compareTo(20) === 0) . PHP_EOL; // 1
echo $number->isLessThanOrEqualTo(20) . PHP_EOL; // 1

// Фильтрация чисел. Аргументы всех функций также фильтруются.
$number = new BigNumber("9,223 372`036'854,775.808000");
echo $number . PHP_EOL; // 9223372036854775.808

// Возводим в степень и выводим в 62-ричной системе счисления
$number = new BigNumber('9223372036854775807');
$number = $number->pow(2)->convertToBase(62);
echo $number . PHP_EOL; // 1wlVYJaWMuw53lV7Cg98qn

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 15.02.2013 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
IBM DOMINO ENTERPRISE CLIENT ACCESS LICENSE AUTHORIZED USER ANNUAL SW SUBSCRIPTION & SUPPORT RENEWAL
VMware Workstation 14 Player for Linux and Windows, ESD
SmartBear Collaborator - Named User License (Includes 1 Year Maintenance)
SAP® Crystal Presentation Design 2016 WIN INTL NUL
ABBYY Business Card Reader 2.0 for Windows (download), электронный ключ
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
СУБД Oracle "с нуля"
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
Новые материалы
Новости мира 3D-ускорителей
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100