4

我似乎在 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(). 我很清楚这个函数可用于将数字格式化为指定的精度。我只是想知道为什么这些函数的返回值有不同的比例,而不是如何修复它们以便它们这样做。

4

1 回答 1

5

PHP 的 BCMath 函数中有一个错误bcmul。它在 PHP 5.5.7 中仍然存在,这是撰写本文时的最新稳定版本。

如果您浏览源代码(PHP 5.5 的 BCMath recmul.c),您将看到相关函数:

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

而且,正如预期的那样,该函数返回一个小数点后两位而不是四位的值。与其他 BCMath PHP 函数不同(例如,参见 PHP 5.5 的 BCMath doaddsub.c的第 63-98 行),在此函数的逻辑中没有任何地方附加尾随零。


我已经向 PHP 错误跟踪系统 ( #66364 ) 提交了这个问题和一个补丁。

于 2013-12-28T03:47:24.207 回答