Arithmetic with Arbitrarily Large Integers in PHP

后端 未结 4 550
小蘑菇
小蘑菇 2020-12-07 01:50

Ok, so PHP isn\'t the best language to be dealing with arbitrarily large integers in, considering that it only natively supports 32-bit signed integers. What I\'m trying to

4条回答
  •  萌比男神i
    2020-12-07 02:17

    I implemented the following PEMDAS complaint BC evaluator which may be useful to you.

    function BC($string, $precision = 32)
    {
        if (extension_loaded('bcmath') === true)
        {
            if (is_array($string) === true)
            {
                if ((count($string = array_slice($string, 1)) == 3) && (bcscale($precision) === true))
                {
                    $callback = array('^' => 'pow', '*' => 'mul', '/' => 'div', '%' => 'mod', '+' => 'add', '-' => 'sub');
    
                    if (array_key_exists($operator = current(array_splice($string, 1, 1)), $callback) === true)
                    {
                        $x = 1;
                        $result = @call_user_func_array('bc' . $callback[$operator], $string);
    
                        if ((strcmp('^', $operator) === 0) && (($i = fmod(array_pop($string), 1)) > 0))
                        {
                            $y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string = array_shift($string), $x, $i = pow($i, -1)));
    
                            do
                            {
                                $x = $y;
                                $y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string, $x, $i));
                            }
    
                            while (BC(sprintf('%s > %s', $x, $y)));
                        }
    
                        if (strpos($result = bcmul($x, $result), '.') !== false)
                        {
                            $result = rtrim(rtrim($result, '0'), '.');
    
                            if (preg_match(sprintf('~[.][9]{%u}$~', $precision), $result) > 0)
                            {
                                $result = bcadd($result, (strncmp('-', $result, 1) === 0) ? -1 : 1, 0);
                            }
    
                            else if (preg_match(sprintf('~[.][0]{%u}[1]$~', $precision - 1), $result) > 0)
                            {
                                $result = bcmul($result, 1, 0);
                            }
                        }
    
                        return $result;
                    }
    
                    return intval(version_compare(call_user_func_array('bccomp', $string), 0, $operator));
                }
    
                $string = array_shift($string);
            }
    
            $string = str_replace(' ', '', str_ireplace('e', ' * 10 ^ ', $string));
    
            while (preg_match('~[(]([^()]++)[)]~', $string) > 0)
            {
                $string = preg_replace_callback('~[(]([^()]++)[)]~', __FUNCTION__, $string);
            }
    
            foreach (array('\^', '[\*/%]', '[\+-]', '[<>]=?|={1,2}') as $operator)
            {
                while (preg_match(sprintf('~(? 0)
                {
                    $string = preg_replace_callback(sprintf('~(? 0) ? $string : false;
    }
    

    It automatically deals with rounding errors, just set the precision to whatever digits you need.

提交回复
热议问题