Strings7 min readExamples run on PHP 8.5.10

number_format()

Learn how PHP 8.4 number_format() groups thousands, rounds decimals half up, accepts custom separators and handles negative decimals.

Signature

number_format(
    float $num,
    int $decimals = 0,
    ?string $decimal_separator = ".",
    ?string $thousands_separator = ","
): string

Purpose

number_format() turns a number into a human-readable string. It groups thousands, rounds to a chosen number of decimal digits, and lets you pick the characters used for the decimal point and the thousands separator. It is a presentation tool: you use it when showing a value to a person, not when storing or calculating it.

Signature

Parameters and defaults

  • $num is the number to format. Integers and numeric strings are converted to float in the usual non-strict way.
  • $decimals is how many digits appear after the decimal point. The default is 0, so the fraction is rounded away. When it is 0, the decimal separator is omitted from the result entirely.
  • $decimal_separator is the string placed before the decimals. The default is a dot.
  • $thousands_separator is the string placed between groups of three digits. The default is a comma. Pass an empty string for no grouping.

Since PHP 8.0 you may pass one, two, three or four arguments. Earlier versions did not accept exactly three.

Return value and failure cases

The function always returns a string. Rounding uses the half-up rule, so 2.5 becomes 3 and -2.5 becomes -3. A value that rounds to zero is shown as 0, never -0; this has been the behavior since PHP 7.2.

The function does not return false. Passing something that cannot be treated as a float, such as the string "abc" or an array, raises a TypeError.

As of PHP 8.3, a negative $decimals rounds the number to that many positions before the decimal point. Before 8.3 negative values were treated like 0.

Example 1: default and custom separators

This shows the same value in English notation, European notation and as a plain machine-friendly string without grouping.

Example
<?php
$number = 1234567.891;
echo number_format($number), PHP_EOL;
echo number_format($number, 2), PHP_EOL;
echo number_format($number, 2, ',', '.'), PHP_EOL;
echo number_format($number, 2, '.', ''), PHP_EOL;
Output
1,234,568
1,234,567.89
1.234.567,89
1234567.89

The first call drops the fraction and rounds up to 1,234,568. The last call uses an empty thousands separator, which is useful when a value goes into a CSV-like text or an input field.

Example 2: rounding, negatives and zero

Example
<?php
$values = [
    'half' => number_format(2.5),
    'negative' => number_format(-1234.567, 2),
    'tiny_negative' => number_format(-0.01),
    'rounded_up' => number_format(0.129, 2),
];
echo json_encode($values, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"half":"3","negative":"-1,234.57","tiny_negative":"0","rounded_up":"0.13"}

The minus sign is kept for real negative values, but -0.01 rounded to zero decimals prints as 0.

Example 3: negative decimals

With PHP 8.3 and later, a negative $decimals rounds to tens, hundreds or thousands.

Example
<?php
$number = 1234.5678;
$result = [
    number_format($number, -1),
    number_format($number, -2),
    number_format($number, -3),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["1,230","1,200","1,000"]

Notice that the result still has thousands grouping, and the trailing digits become zeros rather than disappearing.

Edge cases

  • Zero decimals: no decimal separator appears, even if you passed a custom one.
  • Empty separators: an empty string is allowed for both separators and simply removes that piece of formatting.
  • Large values and floats: floating-point numbers are approximations, so a value such as 1.005 may not round the way decimal arithmetic on paper suggests.
  • Output is not numeric: a result like 1,234.50 is text. Casting it back to a number does not parse the grouping correctly.

Common mistakes

  1. Formatting before calculating. Format only at the final display step. If you format a subtotal and then add it to something else, you are working with a string that includes separators.
  2. Using it for money storage. Floats cannot represent many decimal fractions exactly. Store currency as integer minor units, such as cents, and divide only when displaying.
  3. Assuming locale awareness. number_format() does not read the user's locale. You must supply the separators yourself for each audience.
  4. Forgetting the default drops decimals. number_format(9.99) returns 10. Always pass $decimals when the fraction matters.
  5. Parsing the output. Do not send formatted text to APIs or databases. Keep the raw number and format a copy for display.
  • sprintf() and printf() format numbers with padding and precision, but they do not group thousands.
  • round() changes the numeric value and returns a float rather than a display string.
  • intdiv() and integer arithmetic are better for exact currency math in minor units.

Recap

number_format() returns a string with grouped thousands and rounded decimals. Defaults are no decimals, a dot and a comma. Rounding is half up, -0 is never returned, and negative $decimals rounds before the decimal point as of PHP 8.3. Use it only for output, and keep exact values in a separate numeric form.

Tested examples

Default and custom separators
<?php
$number = 1234567.891;
echo number_format($number), PHP_EOL;
echo number_format($number, 2), PHP_EOL;
echo number_format($number, 2, ',', '.'), PHP_EOL;
echo number_format($number, 2, '.', ''), PHP_EOL;
Output
1,234,568
1,234,567.89
1.234.567,89
1234567.89
Rounding, negatives and zero
<?php
$values = [
    'half' => number_format(2.5),
    'negative' => number_format(-1234.567, 2),
    'tiny_negative' => number_format(-0.01),
    'rounded_up' => number_format(0.129, 2),
];
echo json_encode($values, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"half":"3","negative":"-1,234.57","tiny_negative":"0","rounded_up":"0.13"}
Negative decimals
<?php
$number = 1234.5678;
$result = [
    number_format($number, -1),
    number_format($number, -2),
    number_format($number, -3),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["1,230","1,200","1,000"]