2009-10-29 15 views
17

muszę naśladować dokładnego przebiegu funkcji ceil(), floor() i round() na liczbach bcmath, I've already found a very similar question ale niestety the answer provided isn't good enough for me ponieważ brakuje wsparcia dla liczb ujemnych i precyzji argument dla round() funkcja brakuje .Jak sufit, podłogowe i okrągłe numery bcmath?

Zastanawiam się, czy ktoś może zaproponować dość krótkie i eleganckie rozwiązanie tego problemu.

Wszystkie dane wejściowe są mile widziane, dzięki!

Odpowiedz

23

Po nocy stracił stara się rozwiązać ten problem wierzę Znalazłem dość proste rozwiązanie, to jest tutaj:

function bcceil($number) 
{ 
    if (strpos($number, '.') !== false) { 
     if (preg_match("~\.[0]+$~", $number)) return bcround($number, 0); 
     if ($number[0] != '-') return bcadd($number, 1, 0); 
     return bcsub($number, 0, 0); 
    } 
    return $number; 
} 

function bcfloor($number) 
{ 
    if (strpos($number, '.') !== false) { 
     if (preg_match("~\.[0]+$~", $number)) return bcround($number, 0); 
     if ($number[0] != '-') return bcadd($number, 0, 0); 
     return bcsub($number, 1, 0); 
    } 
    return $number; 
} 

function bcround($number, $precision = 0) 
{ 
    if (strpos($number, '.') !== false) { 
     if ($number[0] != '-') return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision); 
     return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision); 
    } 
    return $number; 
} 

Chyba nie przegapić niczego, jeśli ktoś może zlokalizuj dowolny błąd, daj mi znać. Oto kilka testów:

assert(bcceil('4') == ceil('4')); // true 
assert(bcceil('4.3') == ceil('4.3')); // true 
assert(bcceil('9.999') == ceil('9.999')); // true 
assert(bcceil('-3.14') == ceil('-3.14')); // true 

assert(bcfloor('4') == floor('4')); // true 
assert(bcfloor('4.3') == floor('4.3')); // true 
assert(bcfloor('9.999') == floor('9.999')); // true 
assert(bcfloor('-3.14') == floor('-3.14')); // true 

assert(bcround('3', 0) == number_format('3', 0)); // true 
assert(bcround('3.4', 0) == number_format('3.4', 0)); // true 
assert(bcround('3.5', 0) == number_format('3.5', 0)); // true 
assert(bcround('3.6', 0) == number_format('3.6', 0)); // true 
assert(bcround('1.95583', 2) == number_format('1.95583', 2)); // true 
assert(bcround('5.045', 2) == number_format('5.045', 2)); // true 
assert(bcround('5.055', 2) == number_format('5.055', 2)); // true 
assert(bcround('9.999', 2) == number_format('9.999', 2)); // true 
+0

bcceil ("4") zwraca "3" zamiast 4, tak jak powinien. Ten sam problem z bcsub. Dobrym pomysłem jest użycie bcadd ($ number, 0, 0) do obcinania miejsc dziesiętnych, nie myślałem o tym sam. –

+0

Miałem na myśli powrót 5, a nie 3. –

+0

@reko_t: Naprawiono błąd na bcceil(), ale nie mogłem odtworzyć błędu, o którym wspomniałeś w funkcji bcfloor(). –

3

Oto te, które obsługują liczby ujemne i dokładny argument zaokrąglania.

function bcceil($val) { 
    if (($pos = strpos($val, '.')) !== false) { 
     if ($val[$pos+1] != 0 && $val[0] != '-') 
      return bcadd(substr($val, 0, $pos), 1, 0); 
     else 
      return substr($val, 0, $pos); 
    } 
    return $val; 
} 

function bcfloor($val) { 
    if (($pos = strpos($val, '.')) !== false) { 
     if ($val[$pos+1] != 0 && $val[0] == '-') 
      return bcsub(substr($val, 0, $pos), 1, 0); 
     else 
      return substr($val, 0, $pos); 
    } 
    return $val; 
} 

function bcround($val, $precision = 0) { 
    if (($pos = strpos($val, '.')) !== false) { 
     if ($precision > 0) { 
      $int = substr($val, 0, $pos); 
      $pos2 = ++$pos+$precision; 
      if ($pos2 < strlen($val)) { 
       $val2 = sprintf('%s.%s', substr($val, $pos, $pos2-$pos), substr($val, $pos2)); 
       $val2 = $val2[0] >= 5 ? bcceil($val2) : bcfloor($val2); 
       if (strlen($val2) > $precision) 
        return bcadd($int, $val[0] == '-' ? -1 : 1, 0); 
       else 
        return sprintf('%s.%s', $int, rtrim($val2, '0')); 
      } 
      return $val; 
     } else { 
      if ($val[$pos+1] >= 5) 
       return ($val[0] == '-' ? bcfloor($val) : bcceil($val)); 
      else 
       return ($val[0] == '-' ? bcceil($val) : bcfloor($val)); 
     } 
    } 
    return $val; 
} 
+0

nie testowałem go jeszcze, ale wierzę bcround (99.999, 2) błędnie wraca 99,100, nie? –

+0

Nie: $ php -r 'include "bc.php"; var_dump (bcround (99,999, 2)); " string (3) "100" –

+0

"Część" if (strlen ($ val2)> $ $) "ma temu zapobiec. :) –

0
function getBcRound($number, $precision = 0) 
{ 
    $precision = ($precision < 0) 
       ? 0 
       : (int) $precision; 
    if (strcmp(bcadd($number, '0', $precision), bcadd($number, '0', $precision+1)) == 0) { 
     return bcadd($number, '0', $precision); 
    } 
    if (getBcPresion($number) - $precision > 1) { 
     $number = getBcRound($number, $precision + 1); 
    } 
    $t = '0.' . str_repeat('0', $precision) . '5'; 
    return $number < 0 
      ? bcsub($number, $t, $precision) 
      : bcadd($number, $t, $precision); 
} 

function getBcPresion($number) { 
    $dotPosition = strpos($number, '.'); 
    if ($dotPosition === false) { 
     return 0; 
    } 
    return strlen($number) - strpos($number, '.') - 1; 
} 

var_dump(getBcRound('3', 0) == number_format('3', 0)); 
var_dump(getBcRound('3.4', 0) == number_format('3.4', 0)); 
var_dump(getBcRound('3.56', 0) == number_format('3.6', 0)); 
var_dump(getBcRound('1.95583', 2) == number_format('1.95583', 2)); 
var_dump(getBcRound('5.045', 2) == number_format('5.045', 2)); 
var_dump(getBcRound('5.055', 2) == number_format('5.055', 2)); 
var_dump(getBcRound('9.999', 2) == number_format('9.999', 2)); 
var_dump(getBcRound('5.0445', 5) == number_format('5.044500', 5)); 
var_dump(getBcRound('5.0445', 4) == number_format('5.04450', 4)); 
var_dump(getBcRound('5.0445', 3) == number_format('5.0445', 3)); 
var_dump(getBcRound('5.0445', 2) == number_format('5.045', 2)); 
var_dump(getBcRound('5.0445', 1) == number_format('5.05', 1)); 
var_dump(getBcRound('5.0445', 0) == number_format('5.0', 0));// 
var_dump(getBcRound('5.04455', 2) == number_format('5.045', 2)); 
var_dump(getBcRound('99.999', 2) == number_format('100.000', 2)); 
var_dump(getBcRound('99.999') == number_format('99.999', 0)); 
var_dump(getBcRound('99.999', 'a') == number_format('99.999', 0)); 
var_dump(getBcRound('99.999', -1.5) == number_format('99.999', 0)); 
var_dump(getBcRound('-0.00001', 2) == number_format('-0.000', 2)); 
var_dump(getBcRound('-0.0000', 2) == number_format('0', 2)); 
var_dump(getBcRound('-4.44455', 2) == number_format('-4.445', 2)); 
var_dump(getBcRound('-4.44555', 0) == number_format('-4.5', 0)); 
var_dump(getBcRound('-4.444444444444444444444444444444444444444444445', 0) == number_format('-4.5', 0));