Php 任意精度数字格式/货币格式?

Php 任意精度数字格式/货币格式?,php,string-formatting,currency,number-formatting,arbitrary-precision,Php,String Formatting,Currency,Number Formatting,Arbitrary Precision,是否有一种任意精度的替代方法可以将字符串而不是浮点作为参数 我并不是打算用数万亿的货币单位进行计算,但在经历了在不滥用浮动的情况下正确处理货币算术的麻烦之后,如果有一个函数,即使用户决定给它一些无意义的数据,也不会在大约15位后吐出随机数,那就太好了。或者,嘿,也许有人想买两条口香糖 我不太愿意使用正则表达式,因为我希望利用money_格式的本地化 编辑-找到可行的解决方案;请参见下面的试试这个类试试这个类是从PHP网站上的commenter提交的函数拼凑而成的。编辑以使用任意精度参数 clas

是否有一种任意精度的替代方法可以将字符串而不是浮点作为参数

我并不是打算用数万亿的货币单位进行计算,但在经历了在不滥用浮动的情况下正确处理货币算术的麻烦之后,如果有一个函数,即使用户决定给它一些无意义的数据,也不会在大约15位后吐出随机数,那就太好了。或者,嘿,也许有人想买两条口香糖

我不太愿意使用正则表达式,因为我希望利用money_格式的本地化


编辑-找到可行的解决方案;请参见下面的

试试这个类

试试这个类

是从PHP网站上的commenter提交的函数拼凑而成的。编辑以使用任意精度参数

class format {
    function money($format, $number) 
    { 
        // Takes plain-format, arbitrary-length decimal string (eg: '123456789123456789.123456')
        // Returns localized monetary string, truncated at the hundredth value after the decimal point.
        // (eg: $ 123,456,789,123,456,789.12)
        $regex  = '/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?'. 
                  '(?:#([0-9]+))?(?:\.([0-9]+))?([in%])/'; 
        if (setlocale(LC_MONETARY, 0) == 'C') { 
            setlocale(LC_MONETARY, ''); 
        } 
        $locale = localeconv(); 
        preg_match_all($regex, $format, $matches, PREG_SET_ORDER); 
        foreach ($matches as $fmatch) { 
            $value = (string) $number;
            $flags = array( 
                'fillchar'  => preg_match('/\=(.)/', $fmatch[1], $match) ? 
                               $match[1] : ' ', 
                'nogroup'   => preg_match('/\^/', $fmatch[1]) > 0, 
                'usesignal' => preg_match('/\+|\(/', $fmatch[1], $match) ? 
                               $match[0] : '+', 
                'nosimbol'  => preg_match('/\!/', $fmatch[1]) > 0, 
                'isleft'    => preg_match('/\-/', $fmatch[1]) > 0 
            ); 
            $width      = trim($fmatch[2]) ? (int)$fmatch[2] : 0; 
            $left       = trim($fmatch[3]) ? (int)$fmatch[3] : 0; 
            $right      = trim($fmatch[4]) ? (int)$fmatch[4] : $locale['int_frac_digits']; 
            $conversion = $fmatch[5]; 

            $positive = true; 
            if ($value[0] == '-') { 
                $positive = false; 
                $value  = bcmul($value, '-1');
            } 
            $letter = $positive ? 'p' : 'n'; 

            $prefix = $suffix = $cprefix = $csuffix = $signal = ''; 

            $signal = $positive ? $locale['positive_sign'] : $locale['negative_sign']; 

            if ($locale["{$letter}_sign_posn"] == 1 && $flags['usesignal'] == '+')
                $prefix = $signal; 
            elseif ($locale["{$letter}_sign_posn"] == 2 && $flags['usesignal'] == '+') 
                $suffix = $signal; 
            elseif ($locale["{$letter}_sign_posn"] == 3 && $flags['usesignal'] == '+')
                $cprefix = $signal; 
            elseif ($locale["{$letter}_sign_posn"] == 4 && $flags['usesignal'] == '+')
                $csuffix = $signal; 
            elseif ($flags['usesignal'] == '(' || $locale["{$letter}_sign_posn"] == 0) {
                $prefix = '('; 
                $suffix = ')'; 

            } 
            if (!$flags['nosimbol']) { 
                $currency = $cprefix . 
                            ($conversion == 'i' ? $locale['int_curr_symbol'] : $locale['currency_symbol']) . 
                            $csuffix; 
            } else { 
                $currency = ''; 
            } 
            $space  = $locale["{$letter}_sep_by_space"] ? ' ' : ''; 

            $value = format::number($value, $right, $locale['mon_decimal_point'], 
                     $flags['nogroup'] ? '' : $locale['mon_thousands_sep']);

            $value = @explode($locale['mon_decimal_point'], $value); 

            $n = strlen($prefix) + strlen($currency) + strlen($value[0]); 
            if ($left > 0 && $left > $n) { 
                $value[0] = str_repeat($flags['fillchar'], $left - $n) . $value[0]; 
            } 
            $value = implode($locale['mon_decimal_point'], $value); 
            if ($locale["{$letter}_cs_precedes"]) { 
                $value = $prefix . $currency . $space . $value . $suffix; 
            } else { 
                $value = $prefix . $value . $space . $currency . $suffix; 
            } 
            if ($width > 0) { 
                $value = str_pad($value, $width, $flags['fillchar'], $flags['isleft'] ? 
                         STR_PAD_RIGHT : STR_PAD_LEFT); 
            } 

            $format = str_replace($fmatch[0], $value, $format); 
        } 
        return $format; 
    } 

    function number  ($number  , $decimals = 2 , $dec_point = '.' , $sep = ',', $group=3   ){
        // Arbitrary-precision number formatting:
        // Takes plain-format, arbitrary-length decimal string (eg: '123456789123456789.123456').
        // Takes the same parameters as PHP's native number_format plus a flexible 'grouping' parameter. 
        // WARNINGS: Truncates -- does not round; not inherently locale-aware

        $num = (string) $number;   
        if (strpos($num, '.')) $num = substr($num, 0, (strpos($num, '.') + 1 + $decimals)); // truncate
        $num = explode('.',$num);
        while (strlen($num[0]) % $group) $num[0]= ' '.$num[0];
        $num[0] = str_split($num[0],$group);
        $num[0] = join($sep[0],$num[0]);
        $num[0] = trim($num[0]);
        $num = join($dec_point[0],$num);

        return $num;
    }
}
测试:

 setlocale(LC_MONETARY, 'en_ZW'); // pick your favorite hyperinflated currency
 $string = '123456789123456789.123456';

 echo "original string: " . 
  $string . "<br>";
  // 123456789123456789.123456
 echo "float cast - " . 
  ((float) $string) . "<br>";
  // 1.23456789123E+17
 echo "number_format original: " . 
  number_format($string, 4) . "<br>";
  // 123,456,789,123,456,768.0000
 echo "number_format new: " . 
  format::number($string, 4) . "<br>";
  // 123,456,789,123,456,789.1234
 echo "money_format original: " . 
  money_format('%n', $string) . "<br>";
  // Z$ 123,456,789,123,456,784.00 
 echo "money_format new: " . 
  format::money('%n',$string) . "<br>";
  // Z$ 123,456,789,123,456,789.12
setlocale(LC_MONETARY,'en_ZW');//选择你最喜欢的超膨胀货币
$string='123456789123456789.123456';
回显“原始字符串:”。
$string。“
”; // 123456789123456789.123456 回声“浮动铸造-”。 ((浮动)$string)。“
”; //1.23456789123E+17 回显“编号\格式原件:”。 数字格式($string,4)。“
”; // 123,456,789,123,456,768.0000 回显“编号\格式新:”。 格式::数字($string,4)。“
”; // 123,456,789,123,456,789.1234 回声“货币格式原件:”。 货币格式('%n',$string)。“
”; //Z$12345678923456784.00 echo“money_format new:”。 格式::货币('%n',$string)。“
”; //Z$1234567898123456789.12
根据PHP网站上的评论者提交的函数拼凑而成。编辑以使用任意精度参数

class format {
    function money($format, $number) 
    { 
        // Takes plain-format, arbitrary-length decimal string (eg: '123456789123456789.123456')
        // Returns localized monetary string, truncated at the hundredth value after the decimal point.
        // (eg: $ 123,456,789,123,456,789.12)
        $regex  = '/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?'. 
                  '(?:#([0-9]+))?(?:\.([0-9]+))?([in%])/'; 
        if (setlocale(LC_MONETARY, 0) == 'C') { 
            setlocale(LC_MONETARY, ''); 
        } 
        $locale = localeconv(); 
        preg_match_all($regex, $format, $matches, PREG_SET_ORDER); 
        foreach ($matches as $fmatch) { 
            $value = (string) $number;
            $flags = array( 
                'fillchar'  => preg_match('/\=(.)/', $fmatch[1], $match) ? 
                               $match[1] : ' ', 
                'nogroup'   => preg_match('/\^/', $fmatch[1]) > 0, 
                'usesignal' => preg_match('/\+|\(/', $fmatch[1], $match) ? 
                               $match[0] : '+', 
                'nosimbol'  => preg_match('/\!/', $fmatch[1]) > 0, 
                'isleft'    => preg_match('/\-/', $fmatch[1]) > 0 
            ); 
            $width      = trim($fmatch[2]) ? (int)$fmatch[2] : 0; 
            $left       = trim($fmatch[3]) ? (int)$fmatch[3] : 0; 
            $right      = trim($fmatch[4]) ? (int)$fmatch[4] : $locale['int_frac_digits']; 
            $conversion = $fmatch[5]; 

            $positive = true; 
            if ($value[0] == '-') { 
                $positive = false; 
                $value  = bcmul($value, '-1');
            } 
            $letter = $positive ? 'p' : 'n'; 

            $prefix = $suffix = $cprefix = $csuffix = $signal = ''; 

            $signal = $positive ? $locale['positive_sign'] : $locale['negative_sign']; 

            if ($locale["{$letter}_sign_posn"] == 1 && $flags['usesignal'] == '+')
                $prefix = $signal; 
            elseif ($locale["{$letter}_sign_posn"] == 2 && $flags['usesignal'] == '+') 
                $suffix = $signal; 
            elseif ($locale["{$letter}_sign_posn"] == 3 && $flags['usesignal'] == '+')
                $cprefix = $signal; 
            elseif ($locale["{$letter}_sign_posn"] == 4 && $flags['usesignal'] == '+')
                $csuffix = $signal; 
            elseif ($flags['usesignal'] == '(' || $locale["{$letter}_sign_posn"] == 0) {
                $prefix = '('; 
                $suffix = ')'; 

            } 
            if (!$flags['nosimbol']) { 
                $currency = $cprefix . 
                            ($conversion == 'i' ? $locale['int_curr_symbol'] : $locale['currency_symbol']) . 
                            $csuffix; 
            } else { 
                $currency = ''; 
            } 
            $space  = $locale["{$letter}_sep_by_space"] ? ' ' : ''; 

            $value = format::number($value, $right, $locale['mon_decimal_point'], 
                     $flags['nogroup'] ? '' : $locale['mon_thousands_sep']);

            $value = @explode($locale['mon_decimal_point'], $value); 

            $n = strlen($prefix) + strlen($currency) + strlen($value[0]); 
            if ($left > 0 && $left > $n) { 
                $value[0] = str_repeat($flags['fillchar'], $left - $n) . $value[0]; 
            } 
            $value = implode($locale['mon_decimal_point'], $value); 
            if ($locale["{$letter}_cs_precedes"]) { 
                $value = $prefix . $currency . $space . $value . $suffix; 
            } else { 
                $value = $prefix . $value . $space . $currency . $suffix; 
            } 
            if ($width > 0) { 
                $value = str_pad($value, $width, $flags['fillchar'], $flags['isleft'] ? 
                         STR_PAD_RIGHT : STR_PAD_LEFT); 
            } 

            $format = str_replace($fmatch[0], $value, $format); 
        } 
        return $format; 
    } 

    function number  ($number  , $decimals = 2 , $dec_point = '.' , $sep = ',', $group=3   ){
        // Arbitrary-precision number formatting:
        // Takes plain-format, arbitrary-length decimal string (eg: '123456789123456789.123456').
        // Takes the same parameters as PHP's native number_format plus a flexible 'grouping' parameter. 
        // WARNINGS: Truncates -- does not round; not inherently locale-aware

        $num = (string) $number;   
        if (strpos($num, '.')) $num = substr($num, 0, (strpos($num, '.') + 1 + $decimals)); // truncate
        $num = explode('.',$num);
        while (strlen($num[0]) % $group) $num[0]= ' '.$num[0];
        $num[0] = str_split($num[0],$group);
        $num[0] = join($sep[0],$num[0]);
        $num[0] = trim($num[0]);
        $num = join($dec_point[0],$num);

        return $num;
    }
}
测试:

 setlocale(LC_MONETARY, 'en_ZW'); // pick your favorite hyperinflated currency
 $string = '123456789123456789.123456';

 echo "original string: " . 
  $string . "<br>";
  // 123456789123456789.123456
 echo "float cast - " . 
  ((float) $string) . "<br>";
  // 1.23456789123E+17
 echo "number_format original: " . 
  number_format($string, 4) . "<br>";
  // 123,456,789,123,456,768.0000
 echo "number_format new: " . 
  format::number($string, 4) . "<br>";
  // 123,456,789,123,456,789.1234
 echo "money_format original: " . 
  money_format('%n', $string) . "<br>";
  // Z$ 123,456,789,123,456,784.00 
 echo "money_format new: " . 
  format::money('%n',$string) . "<br>";
  // Z$ 123,456,789,123,456,789.12
setlocale(LC_MONETARY,'en_ZW');//选择你最喜欢的超膨胀货币
$string='123456789123456789.123456';
回显“原始字符串:”。
$string。“
”; // 123456789123456789.123456 回声“浮动铸造-”。 ((浮动)$string)。“
”; //1.23456789123E+17 回显“编号\格式原件:”。 数字格式($string,4)。“
”; // 123,456,789,123,456,768.0000 回显“编号\格式新:”。 格式::数字($string,4)。“
”; // 123,456,789,123,456,789.1234 回声“货币格式原件:”。 货币格式('%n',$string)。“
”; //Z$12345678923456784.00 echo“money_format new:”。 格式::货币('%n',$string)。“
”; //Z$1234567898123456789.12
round()或number\u format()不够好吗?只需将字符串强制转换为浮点。@Cesar-显式强制转换对舍入错误没有任何作用。尝试将一个较大的字符串数(~20位左右)转换为浮点,您将得到类似于3.178173138888E+20的结果。将它输入到一个格式化函数中,该函数采用类似于money\u格式或number\u格式的浮点值,那么多数字之后,您将得到随机的、不正确的垃圾。这仅仅是使用浮点数的结果——我相信你已经有了大约15位数的精度。我知道这是一个多么具有保留性的函数,但它似乎应该包含一个字符串参数。round()或number_format()还不够好吗?只需将字符串强制转换为浮点。@Cesar-显式强制转换对舍入错误没有任何作用。尝试将一个较大的字符串数(~20位左右)转换为浮点,您将得到类似于3.178173138888E+20的结果。将它输入到一个格式化函数中,该函数采用类似于money\u格式或number\u格式的浮点值,那么多数字之后,您将得到随机的、不正确的垃圾。这仅仅是使用浮点数的结果——我相信你已经有了大约15位数的精度。我知道这有多大的保留性,但仍然--它看起来像这样的函数应该包含一个字符串参数。我恐怕没有PECL扩展来测试它,但它看起来仍然是一个浮点数作为参数?也许我误解了?这些例子有很多数字,但大多数都是无关紧要的,只是四舍五入。我恐怕没有PECL扩展来测试它,但它看起来仍然是一个浮点数作为参数?也许我误解了?这些例子有很多数字,但大多数都无关紧要,只是四舍五入。