PHP可靠计算立方根

时间:2014-10-30 07:50:18

标签: php math floating-point-precision

这似乎不是一件容易的事,但我很难解决这个问题:

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

当我使用标准的PHP数学

floor( pow( 10648, 1/3))

它返回21而不是22.这是错误的,因为21 ^ 3给出了92161.原因是由于有限的浮点精度,pow(10648,1 / 3)不完全返回22,而是返回21.9993112732。 您可以使用以下代码段进行检查:

$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;
}

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

感谢。

2 个答案:

答案 0 :(得分:2)

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

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;
}

答案 1 :(得分:1)

如果您需要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;作为默认情况,但这取决于您。