2

是否有任意精度替代money_format可用,可以採用字符串而不是浮點數作爲參數?任意精度數字格式化/ money_format?

這並不是我計劃對數萬億貨幣單位進行計算,而是在經過麻煩以正確處理貨幣算術而沒有濫用浮動的情況下,它會很高興有一個函數不會隨機發出隨機數大約15位數字,即使用戶決定給它無意義的數據。或者,嘿,也許有人想買兩個口香糖在Zimbabwe dollars

我不願意使用正則表達式,因爲我希望能夠使用money_format的本地化。

編輯 - 找到一個可行的解決方案;見下文

+0

輪()或number_format()都不夠好?只需要將該字符串轉換爲浮動。 – Cesar 2010-09-27 05:44:07

+0

@Cesar - 明確施放它不會對舍入錯誤做任何事情。嘗試投射一個大的字符串數字(大約20位數字)浮動,你會得到像3.17817313888E + 20。將其輸入格式化函數,該格式函數使用像money_format或number_format這樣的浮點數,並且在這麼多數字之後會得到隨機的,不正確的垃圾。這只是使用浮點數 - 我相信你的精度大約有十五位數。我意識到這是如何保持肛門保持的,但仍然 - 似乎這樣的功能應該採取字符串參數。 – Greg 2010-09-27 05:50:32

回答

0

從評論者提交的功能在PHP的網站herehere拼湊。編輯爲使用任意精度參數。

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 
0

嘗試NumberFormatter

+0

恐怕我沒有那個PECL擴展來測試它,但它看起來仍然是一個浮點數作爲參數?也許我誤解了?這些例子有很多數字,但大多數都不重要,只是四捨五入。 – Greg 2010-09-27 06:43:04