【问题标题】:Why does bcmul return a number with a scale different than the one I specified?为什么 bcmul 返回一个与我指定的比例不同的数字?
【发布时间】:2014-01-12 22:36:54
【问题描述】:

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

【问题讨论】:

    标签: php bcmath


    【解决方案1】:

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

    如果你browse the source code(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) 提交了此问题和补丁。

    【讨论】:

      猜你喜欢
      • 2019-12-12
      • 2012-07-20
      • 2014-04-20
      • 1970-01-01
      • 2018-08-06
      • 1970-01-01
      • 2019-01-12
      • 2015-06-13
      • 2017-05-05
      相关资源
      最近更新 更多