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