2

I've come across what I feel is inconsistent rounding with PHP's sprintf() function. The following code:

$n = 691.625;
$s = sprintf("%.2f", $n);
$r = round($n, 2);
print "$n: $s $r\n";

$n = 17.565;
$s = sprintf("%.2f", $n);
$r = round($n, 2);
print "$n: $s $r\n";

$n = 19.875;
$s = sprintf("%.2f", $n);
$r = round($n, 2);
print "$n: $s $r\n";

produces the following output:

691.625: 691.62 691.63
 17.565:  17.57  17.57
 19.875:  19.88  19.88

I expected the following:

691.625: 691.63 691.63
 17.565:  17.57  17.57
 19.875:  19.88  19.88

The difference is in the second number on the first line. So my question is, why does sprintf() round down for the first number and up for the second and third?

I've replicated this under PHP 5.4, 5.5 and 7.0. I've included a comparison with the result of round().

(I thought it might have been the internal floating point representation, but all three appear to be stored correctly.)

dave
  • 11,641
  • 5
  • 47
  • 65
  • What version of PHP? – Ates Goral Mar 30 '17 at 04:12
  • I see this under PHP 5.4 and 5.5. – dave Mar 30 '17 at 04:16
  • In PHP 7 its showing same result. – TIGER Mar 30 '17 at 04:17
  • I don't have access to PHP 7. Anyone? – dave Mar 30 '17 at 04:18
  • The `mode` argument (3rd one) of [round()](http://php.net/manual/en/function.round.php) controls the rounding behaviour. I don't know if `sprintf()` internally uses `round()`, but I'd see if you're getting the same behaviour with `round()`, and then try some different modes (the default mode is sadly not documented). – Ates Goral Mar 30 '17 at 04:19
  • You can experiment with PHP 7 here: https://repl.it/languages/php – Ates Goral Mar 30 '17 at 04:20
  • @pvg I don't think it's rounding half to even. If it was, wouldn't `17.565` be rounded to `17.56`? – dave Mar 30 '17 at 04:20
  • @AtesGoral If I use `round()` I get `691.63`. I'll update the question. – dave Mar 30 '17 at 04:23
  • @dave yep, I got my modes wrong, how did you test what the representation of the numbers was? – pvg Mar 30 '17 at 04:34
  • @pvg By printing $n and viewing it within PHPStorm using a breakpoint. I'm pretty sure I've seen things like 691.6249999999995748 when the FP representation is not exact, but I could be wrong. – dave Mar 30 '17 at 04:36
  • [this](http://stackoverflow.com/a/27108849/1564959) answer says that why `round` can offer better results than a function not specifically designed for rounding, we need to consider the limits of the double precision floating point representation. – TIGER Mar 30 '17 at 04:40
  • see http://stackoverflow.com/questions/8204963/printing-float-values-with-sprintf, http://stackoverflow.com/questions/1604696/php-float-calculation-2-decimal-point, http://stackoverflow.com/questions/28739818/php-how-to-add-leading-zeros-zero-padding-to-float-via-sprintf, http://stackoverflow.com/questions/27884209/php-printf-as-float-precision, http://stackoverflow.com/questions/5294129/wrong-float-formatting-in-php-sprintf-printf, http://stackoverflow.com/questions/14764447/how-does-sprintf4-2f-value-function-in-php. Sure one of those post will explain precision lost – Louis Loudog Trottier Mar 30 '17 at 04:41
  • @dave it seems that one actually does have an exact representation. the the last digit, which is at least mildly amusing. – pvg Mar 30 '17 at 04:47
  • It's important to note that sprintf will ROUND floats if it thinks it needs to. For example, take the division 31 / 53 = 0.584905[...] - if we want 3 decimal places, we can do sprintf("%.3f", (31 / 53)) but this does NOT give us 0.584. It gives us 0.585 – NID Mar 30 '17 at 04:47
  • This one could be useful: http://stackoverflow.com/questions/27107238/why-does-phps-sprintf-not-round-5s-reliably – kojow7 Mar 30 '17 at 04:53
  • It seems like sprintf %f rounding (in the rare cases you get an exact number) employs whatever rounding rule the implementors picked. Take a look at https://pastebin.com/4njJzKtd and run it, the rounding on the two exactly represented values suggests an even/odd rounding rule in use. All the other numbers are 'just' rounded if you take into account their full precision. The key thing is probably 'don't rely on sprintf for consistent rounding' – pvg Mar 30 '17 at 04:59
  • need php 4.3.0 - 5.2.0 https://3v4l.org/YkSGn – Deadooshka Mar 30 '17 at 06:15

0 Answers0