PHP - Find the number of zeros in a decimal number

后端 未结 3 1292
伪装坚强ぢ
伪装坚强ぢ 2021-01-23 22:48

Let\'s say we have 0.00045. I want to find a way to count the number of \"significant\" zeros after the decimal point (3 in this case). I\'ve been trying to implement st

相关标签:
3条回答
  • 2021-01-23 23:20
    strspn($num, "0", strpos($num, ".")+1)
    

    strspn finds the length of a sequence of zeroes. strpos finds the position of the decimal point, and we start from 1 position past that.

    However, this doesn't work for 0.0000062 because it gets converted to scientific notation when converted to a string: 6.200000e-6. When the number ends with e-<exponent>, the number of zeroes is <exponent>-1. You can check for this with a regular expression.

    0 讨论(0)
  • 2021-01-23 23:35

    Match the first consecutive zeroes in the mantissa:

    $number = 123.0000000240003;
    
    preg_match("/^(0+)/", explode('.', $number)[1], $matches);
    
    echo strlen($matches[0]); // echoes 7
    
    0 讨论(0)
  • 2021-01-23 23:41

    I've decided to investigate some different possibilities in handling this task.

    I must say that Barmar's solution is easy to read and well suited for floating point numbers that do not suffer from impacts of scientific notation.

    As a thought experiment, I've decided to craft a couple of regular expressions to do the same job. (*The regex approaches will still hold up even if you pass an integer value to them.)

    1. With preg_match_all(), I am using the "continue" metacharacter (\G) to match the first .0 then zero or more 0 that immediately follow.
    2. With preg_match(), I can more simply match the dot, then "forget it" with \K (restart the fullstring match), then match 1 or more zeros from that position. Unfortunately this requires the additional call of str_len(). I generally only favor preg_ calls when it results in fewer function calls than non-regex approaches.

    Inspired by https://codereview.stackexchange.com/q/219601/141885, I wanted to write a function to count the zeros after the decimal point using only arithmetic (guess & check). While it doesn't suffer from scientific notation like the others, it is imperfect and there are limits to floats/integers on different operating systems / versions / environments / yatta-yatta (I don't pretend to know all of the ins-and-outs, but I am aware that there are differences). Go ahead and try some of these approaches with your project data and feel free to leave comments about what works and doesn't work. ...and if you know know why I invite you to educate me :)

    Finally, there are many posts on StackOverflow (here's one) that recommend using the standard php library BCMath.

    Code: (Demo)

    function mathematical_zeros_after_dot($float) {
        $float = abs($float);  // remove any signs
        $float -= (int)$float;  // remove whole numbers from float
        if ($float == 0) {
            return "Rendered as 0";
        }
        $max = 20;
        for ($x = 0; $x < $max; ++$x) {  // for loop with a hard limit to avoid infinite loop
            $float *= 10;
            if ($float >= 1) {
                return $x;
            }
        }
        return "$max {exceeded}";
    }
    
    $floats = [
        25.000000000022,         // 10
        0.0000062,               // 5
        0.020320,                // 1
        .505000,                 // 0
        0,                       // 0
        .000507,                 // 3
        -.002009,                // 2
        1000,                    // 0
        0.00,                    // 0
        1                        // 0
        -1.0000000000004000004,  // 12
        981.0000000000000000000004000004  // 21
    ];
    
    foreach ($floats as $float) {
        echo "(Math) {$float} has " , mathematical_zeros_after_dot($float) , " zero(s)\n";
        echo "Barmar {$float} has " , strspn($float, "0", strpos($float, ".")+1) , " zero(s)\n";
        echo "(PMA) {$float} has " , preg_match_all('~(?:\.|\G(?!^))0~', $float) , " zero(s)\n";
        echo "(PA) {$float} has " , (preg_match('~\.\K0+~', $float, $match) ? strlen($match[0]) : 0) , " zero(s)\n";
    
    }
    

    Output:

    (Math) 25.000000000022 has 10 zero(s)
    Barmar 25.000000000022 has 10 zero(s)
    (PMA) 25.000000000022 has 10 zero(s)
    (PA) 25.000000000022 has 10 zero(s)
    (Math) 6.2E-6 has 5 zero(s)
    Barmar 6.2E-6 has 0 zero(s)
    (PMA) 6.2E-6 has 0 zero(s)
    (PA) 6.2E-6 has 0 zero(s)
    (Math) 0.02032 has 1 zero(s)
    Barmar 0.02032 has 1 zero(s)
    (PMA) 0.02032 has 1 zero(s)
    (PA) 0.02032 has 1 zero(s)
    (Math) 0.505 has 0 zero(s)
    Barmar 0.505 has 0 zero(s)
    (PMA) 0.505 has 0 zero(s)
    (PA) 0.505 has 0 zero(s)
    (Math) 0 has Rendered as 0 zero(s)
    Barmar 0 has 0 zero(s)
    (PMA) 0 has 0 zero(s)
    (PA) 0 has 0 zero(s)
    (Math) 0.000507 has 3 zero(s)
    Barmar 0.000507 has 3 zero(s)
    (PMA) 0.000507 has 3 zero(s)
    (PA) 0.000507 has 3 zero(s)
    (Math) -0.002009 has 2 zero(s)
    Barmar -0.002009 has 2 zero(s)
    (PMA) -0.002009 has 2 zero(s)
    (PA) -0.002009 has 2 zero(s)
    (Math) 1000 has Rendered as 0 zero(s)
    Barmar 1000 has 3 zero(s)
    (PMA) 1000 has 0 zero(s)
    (PA) 1000 has 0 zero(s)
    (Math) 0 has Rendered as 0 zero(s)
    Barmar 0 has 0 zero(s)
    (PMA) 0 has 0 zero(s)
    (PA) 0 has 0 zero(s)
    (Math) -3.9990233346998E-13 has 12 zero(s)
    Barmar -3.9990233346998E-13 has 0 zero(s)
    (PMA) -3.9990233346998E-13 has 0 zero(s)
    (PA) -3.9990233346998E-13 has 0 zero(s)
    (Math) 981 has Rendered as 0 zero(s)
    Barmar 981 has 0 zero(s)
    (PMA) 981 has 0 zero(s)
    (PA) 981 has 0 zero(s)
    
    0 讨论(0)
提交回复
热议问题