【问题标题】:PHP Reliable Calculating of Cube RootPHP 可靠计算立方根
【发布时间】:2014-10-30 07:50:18
【问题描述】:

这似乎很简单,但我很难解决这个问题:

我想根据给定的经验值 (exp) 计算等级。因此,我使用立方根公式并向下舍入到下一个整数。当 exp 正好达到level^3 时,达到下一个级别。级别的数量是无限的,所以我会避免使用预先计算好的查找表。

当我使用标准 php 数学时

floor( pow( 10648, 1/3))

它返回 21 而不是 22。这是错误的,因为 21^3 给出了 92161。原因是由于浮点精度有限 pow(10648, 1/3) 返回的不是 22,而是返回 21.9993112732。 你可以用下面的sn-p查看一下:

$lvl = pow( 10647, (float) 1 / 3);
print number_format( $lvl, 10);

这是我的解决方法。但我不确定这是否是防弹的:

public static function getLevel($exp) {    
    $lvl = floor(pow($exp, (float) 1 / 3));    // calculate the level
    if (pow($lvl + 1, 3) == $exp) {            // make check
        $lvl++;                                // correct 
    }
    return $lvl;
}

在检查时它看起来有点脆弱。所以问题仍然存在: 是否有一种可靠、高效和防弹的方法来计算立方根(正数)。

谢谢。

【问题讨论】:

  • 为什么不用round() 而不是floor()
  • round() 在某些情况下也会失败。以7为例。它应该返回级别 1,但 round( pow( 7, (float) 1 / 3)) 返回 2(错误)
  • 真正的问题是1/3不能用浮点数精确表示。
  • 用 0.333334 代替 1/3 怎么样?它适用于 10647 和 7。

标签: php math floating-point-precision


【解决方案1】:

我认为这是您的代码需要的唯一修改:

public static function getLevel($exp) {    
    $lvl = floor(pow($exp, (float) 1 / 3));    
    if (pow($lvl + 1, 3) <= $exp) {   // compare with <= instead of ==         
        $lvl++;                                 
    }
    return $lvl;
}

【讨论】:

    【解决方案2】:

    如果您需要 100% 可靠的结果,您可能应该使用GMP library 进行任意精度计算。

    gmp_root 函数应该可以满足您的需求。您需要启用 GMP 扩展的 PHP 5.6 或更高版本。

    $num = gmp_init(10648);
    $third_root = gmp_root($num, 3);
    
    var_dump(gmp_strval($third_root));  // string(2) "22"
    

    如果 GMP 库对您来说不方便,并且您可以保证您的号码具有整数根,那么您可以尝试以下操作:

    function getLevel($base, $root = 3.0) {
        $exact = pow($base, 1.0 / $root);
        $ceil  = ceil($exact);
        $floor = floor($exact);
    
        if (pow($exact, $root) == $base) { return $exact; }
        if (pow($ceil,  $root) == $base) { return $ceil;  }
        if (pow($floor, $root) == $base) { return $floor; }
    
        // Default: no integer root
        return FALSE;
    }
    

    它会检查结果的确切值、floorceil 以找出正确答案。如果不是三者之一,则该数字没有整数根,默认为FALSE

    这是example of it in action

    var_dump(getLevel(10648, 3)); // 22^3 => float(22)
    var_dump(getLevel(16807, 5)); //  7^5 => float(7)
    
    var_dump(getLevel(1,  3)); // Should always return 1 => float(1)
    var_dump(getLevel(1, 99)); // Should always return 1 => float(1)
    
    var_dump(getLevel(7)); // Has no integer 3rd root => bool(false)
    

    当然,您可以将函数 return $floor;return $ceil; 作为默认情况,但这取决于您。

    【讨论】:

      猜你喜欢
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-15
      • 2012-12-15
      • 1970-01-01
      • 2010-11-14
      相关资源
      最近更新 更多