使用最小所需的小数位数格式化浮点数

时间:2015-08-24 09:20:05

标签: php floating-point number-formatting floating-point-precision

我希望使用重现它所需的最小小数位数来格式化浮点数。

PHP具有number_format()函数,用于呈现具有指定小数位数的数字。但是,如果我使用它来格式化具有非常高的小数位数的0.1,我得到:

print rtrim(number_format(0.1, 1000, '.', ''), '0');
// 0.1000000000000000055511151231257827021181583404541015625

(float)"0.1" === 0.1起,第16位后的额外55511151...小数无效。

我可以使用循环,如下所示:

function format_float($float) {
    $decimals = 1;
    do {
        $result = number_format($float, $decimals, '.', '');
        $decimals++;
    } while ((float)$result !== $float);
    return $result;
}

print format_float(0.1) . "\n"; // 0.1
print format_float(1/3) . "\n"; // 0.3333333333333333
print format_float(1E-50) . "\n"; // 0.00000000000000000000000000000000000000000000000001

但肯定有一种更简单,更有效的方法吗?

3 个答案:

答案 0 :(得分:1)

正确地打印二进制浮点数的最小十进制数是非常复杂的努力。目前最先进的是grisu family of algorithms。有关所涉问题的详细解释,请参阅Steele and White的经典论文。

答案 1 :(得分:1)

@Jesse的代码对我不起作用,因此构建此代码:

function formatFloat(float $num, int $dec = 0) : string {

    // format to maximum decimal places, and make positive
    $abs = number_format(abs($num), 17, '.', '');

    // is it less than 0?
    if (!floor($abs)) {

        // look through each number until we find one that is not 0
        foreach (str_split(substr($abs, 2)) AS $i => $item) {
            if ($item) {
                break;
            }
        }

        // add defined decimal places
        $dec += $i + 1;
    }

    // format the output
    return number_format($num, $dec);
}

它可以处理大于1的数字(负数),并允许您指定找到第一个有效数字后将浮点格式设置为多少小数位。它也表现更好。

答案 2 :(得分:0)

这就是我提出的:

function format_float($num) {
    $dec = $num == 0 ? 0 : ceil(-log10(abs($num)));
    $dec = max(1, $dec + 15 /* magic number */);
    $res = number_format($num, $dec, '.', '');

    // sometimes we need one more decimal
    if ((float)$res !== $num) {
        $res = number_format($num, $dec + 1, '.', '');
    }

    list($l, $r) = explode('.', $res, 2);
    return "$l." . (rtrim($r) ?: '0');
}

它假设所需的小数位数为15 - log10($num)16 - log10($num),根据我的测试,这似乎在实践中有效。它至少比我的暴力循环效率更高。