Почему bcmul возвращает число со шкалой, отличной от указанной мной?

Я не могу найти ничего в документации php.net, которая объясняет следующие результаты:

$ php -r 'var_dump(bcsub("0.3", "0.2", 4));'
string(6) "0.1000"
$ php -r 'var_dump(bcmul("0.3", "0.2", 4));'
string(4) "0.06"

Результат вычитания - это именно то, что я и ожидал (я указал 4-значную шкалу, и он дал мне один результат). Результат умножения - нет (я указал 4-значную шкалу, но в результате я получил 2-значную шкалу). Почему разница?

Примечание: я уже знаю, как использовать number_format() и я тоже знаю что 0.06 === 0.0600 математически. Меня интересует только понимание того, почему BC Math, похоже, действует по-разному в зависимости от масштаба результата.

Примечание № 2: Как упоминалось выше, number_format() не является ответом на этот вопрос, и ответы, использованные в упомянутом "дублирующем вопросе", все советуют использовать number_format(), Я прекрасно знаю, что эту функцию можно использовать для форматирования числа с заданной точностью. Мне просто любопытно узнать, ПОЧЕМУ возвращаемые значения для этих функций имеют разные масштабы, а НЕ как их исправить, чтобы они это делали.

1 ответ

Решение

В PHP есть функция BCMath bcmul, Он все еще присутствует в PHP 5.5.7, последней стабильной версии на момент написания этой статьи.

Если вы просматриваете исходный код (BCMath recmul.c в PHP 5.5), вы увидите соответствующую функцию:

void
bc_multiply (bc_num n1, bc_num n2, bc_num *prod, int scale TSRMLS_DC)
{
  bc_num pval; 
  int len1, len2;
  int full_scale, prod_scale;

  /* Initialize things. */
  len1 = n1->n_len + n1->n_scale;
  len2 = n2->n_len + n2->n_scale;
  full_scale = n1->n_scale + n2->n_scale;
  prod_scale = MIN(full_scale,MAX(scale,MAX(n1->n_scale,n2->n_scale)));

  /* Do the multiply */
  _bc_rec_mul (n1, len1, n2, len2, &pval, full_scale TSRMLS_CC);

  /* Assign to prod and clean up the number. */
  pval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS );
  pval->n_value = pval->n_ptr;
  pval->n_len = len2 + len1 + 1 - full_scale;
  pval->n_scale = prod_scale;
  _bc_rm_leading_zeros (pval);
  if (bc_is_zero (pval TSRMLS_CC))
    pval->n_sign = PLUS;
  bc_free_num (prod);
  *prod = pval;
}

Примечание. Слово "масштаб" относится к количеству цифр после разделителя.

Посмотрите на линию, где prod_scale назначен. Когда вы вызываете bcmul("0.3", "0.2", 4) Идя по коду, мы видим: prod_scale = MIN(2,MAX(4,MAX(1,1)));, так prod_scale присваивается значение 2,

И, как и ожидалось, функция возвращает значение с двумя, а не четырьмя цифрами после десятичного знака. В отличие от других функций PHP BCMath (например, см. Строки 63-98 в BCMath doaddsub.c в PHP 5.5), нигде в логике этой функции не добавляются завершающие нули.


Я отправил эту проблему и исправление в систему отслеживания ошибок PHP ( # 66364).

Другие вопросы по тегам