2

Existe-t-il une alternative de précision arbitraire à money_format disponible qui pourrait prendre une chaîne au lieu d'un flottant en tant que paramètre? Ce n'est pas que je prévois faire des calculs sur des billions d'unités monétaires, mais après avoir eu la difficulté de gérer correctement l'arithmétique monétaire sans abuser des flottants, il serait bien d'avoir une fonction qui ne crache pas de nombres aléatoires après environ 15 chiffres, même si les utilisateurs décident de donner des données non-sens. Ou, hé, peut-être quelqu'un veut acheter deux bâtons de gomme dans Zimbabwe dollars? J'hésite à utiliser des expressions régulières parce que j'espérais utiliser la localisation de money_format.Formatage de précision arbitraire/money_format?

edit - trouvé une solution réalisable; voir ci-dessous

+0

round() ou number_format() ne sont pas assez bon? juste juste besoin de lancer la chaîne pour flotter. – Cesar

+0

@Cesar - Casting explicitement ne fait rien pour les erreurs d'arrondi. Essayez de lancer un grand nombre de cordes (environ 20 chiffres) pour flotter et vous obtiendrez quelque chose comme 3.17817313888E + 20. Nourrissez-le dans une fonction de mise en forme qui prend un flottant comme money_format ou number_format et vous obtiendrez des déchets aléatoires et incorrects après tant de chiffres. Cela vient avec l'utilisation de flotteurs - vous avez une quinzaine de chiffres de précision, je crois. Je sais à quel point c'est anal-rétentif c'est, mais encore - il semble qu'une telle fonction devrait prendre un paramètre de chaîne. – Greg

Répondre

0

bricolée des fonctions présentées commentateur sur le site de PHP here et here. Edité pour travailler avec des paramètres de précision arbitraires.

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; 
    } 
} 

Tests:

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

essayer la classe NumberFormatter

+0

Je crains de ne pas avoir cette extension PECL pour le tester, mais ça ressemble encore à un flotteur en tant que paramètre? Peut-être que je suis un malentendu? Les exemples ont une tonne de chiffres, mais la plupart sont insignifiants et juste arrondis. – Greg