When you print numbers in a Python program, the raw output often isn't what you want to show to users. Python number format tools give you precise control over how numbers appear — from rounding floats to two decimal places to adding commas as thousands separators. Whether you're building a finance dashboard, a CLI report, or a data summary tool, knowing how to format numbers in Python makes your output readable and professional.

In this tutorial you'll learn every practical technique for python number formatting using f-strings, the built-in format() function, and format specifiers. Each method is explained with working examples and real output so you can follow along in any Python 3 environment.

Understanding the Python Format Specifier

The foundation of python number format is the format specifier — a mini-language built into Python that tells it exactly how to render a number. You'll encounter it inside f-strings and format() calls, placed after a colon inside curly braces.

The general structure looks like this:

{value:[fill][align][sign][#][0][width][grouping][.precision][type]}

That looks intimidating at first, but in practice you only ever use a few of these pieces at a time. Here's what each key part means:

  • width — the minimum total number of characters in the output, padded with spaces by default
  • .precision — how many digits to show after the decimal point for floats, or total significant digits for other types
  • type — the kind of number: f for fixed-point float, d for decimal integer, e for scientific notation, g for general format, % for percentage
  • grouping — a comma , or underscore _ to visually separate every three digits

Understanding this python format specifier is the unlock for every technique in this guide. Once you've seen the pattern once, every format string becomes readable.

Python F-String Number Format

F-strings (formatted string literals) arrived in Python 3.6 and quickly became the most popular way to handle python f-string number format. You write them by adding an f prefix before the opening quote, then put your variable name followed by a colon and your format specifier inside the curly braces.

Here's the most common use case — displaying a price with exactly two decimal places:

price = 19.5
print(f"Price: {price:.2f}")
Price: 19.50

The :.2f is the format specifier. The .2 means two digits after the decimal, and f means fixed-point float notation. Python takes 19.5 and renders it as 19.50, adding the trailing zero so the output is always consistent.

You can also control the total width of the field, which is essential for aligning columns in a table. The number before the dot sets the minimum character width:

items = [("Apple", 1.5), ("Banana", 0.75), ("Mango", 3.125)]

for name, cost in items:
    print(f"{name:<10} ${cost:8.2f}")
Apple      $    1.50
Banana     $    0.75
Mango      $    3.12

The :<10 left-aligns the item name inside a 10-character field. The :8.2f right-aligns the cost inside an 8-character field with 2 decimal places. This is exactly how you'd format a receipt or invoice in a terminal application — clean, aligned, and consistent regardless of string length.

Python Format Decimal Places

Controlling decimal places is one of the most frequent python number format tasks you'll encounter — especially for currency, measurements, sensor data, or any user-facing statistics. The .precision portion of the format specifier handles this directly, and you can use it with any numeric value.

Here's a comparison of 1 through 4 decimal places on the same number:

value = 3.14159265358979

print(f"1 decimal:  {value:.1f}")
print(f"2 decimals: {value:.2f}")
print(f"3 decimals: {value:.3f}")
print(f"4 decimals: {value:.4f}")
1 decimal:  3.1
2 decimals: 3.14
3 decimals: 3.142
4 decimals: 3.1416

Python rounds correctly at every level — 3.14159 rounded to 4 places gives 3.1416, not 3.1415. Standard rounding rules apply.

There's also the g type specifier, which uses a "general" format that automatically trims trailing zeros. This makes python format float output feel natural when displaying varying values:

a = 3.10000
b = 3.14159

print(f"{a:.4g}")
print(f"{b:.4g}")
3.1
3.142

The g type is useful for scientific measurements or any display where you don't want unnecessary trailing zeros cluttering the output. Python decides whether to use fixed-point or exponential notation based on which is shorter for the given precision.

Python Thousands Separator

When you're working with large numbers — population figures, financial totals, file sizes — adding a thousands separator makes them dramatically easier to scan. Python number format supports this natively with a comma inside the format specifier, and you don't need any extra imports or functions.

Adding comma grouping is as simple as including , between the width and precision in your specifier:

population = 8045311447
salary = 125000.75

print(f"World population: {population:,}")
print(f"Annual salary: ${salary:,.2f}")
World population: 8,045,311,447
Annual salary: $125,000.75

Python also supports the underscore _ as an alternative grouping character. This is particularly useful when displaying large binary or hexadecimal values where commas would look out of place:

big_number = 1000000000
hex_value = 16777215

print(f"With underscores: {big_number:_}")
print(f"Hex with underscores: {hex_value:_x}")
With underscores: 1_000_000_000
Hex with underscores: ff_ffff

The _x type formats the integer as lowercase hexadecimal and adds underscore grouping every four hex digits. This is handy when working with color values, memory addresses, or any low-level numeric display.

Python Format Integer

Python format integer covers more than just printing a number — you can control zero-padding, sign display, and the numeric base: decimal, binary, octal, and hexadecimal. The type letters for integers are d (decimal), b (binary), o (octal), and x / X (hexadecimal lowercase and uppercase).

Zero-padding is a common need when generating sequential file names, IDs, or any output that needs consistent character width:

for i in range(1, 6):
    print(f"File: report_{i:04d}.csv")
File: report_0001.csv
File: report_0002.csv
File: report_0003.csv
File: report_0004.csv
File: report_0005.csv

The 04d means: format as a decimal integer, padded with zeros on the left to a minimum width of 4. This is cleaner and more readable than calling str(i).zfill(4).

For displaying the same number in multiple bases — which often comes up in systems programming, networking, and low-level debugging:

n = 255

print(f"Decimal:     {n:d}")
print(f"Binary:      {n:b}")
print(f"Octal:       {n:o}")
print(f"Hex lower:   {n:x}")
print(f"Hex upper:   {n:X}")
print(f"Hex prefix:  {n:#x}")
Decimal:     255
Binary:      11111111
Octal:       377
Hex lower:   ff
Hex upper:   FF
Hex prefix:  0xff

The # flag adds the 0b, 0o, or 0x prefix automatically, saving you from concatenating those strings manually. This is the cleanest way to render base-prefixed numbers in Python.

Python Scientific Notation Format

When you're dealing with very large or very small numbers — distances in astronomy, probabilities in statistics, physical constants, or machine learning loss values — python scientific notation format keeps things legible. The e type formats a number using standard scientific notation.

light_speed = 299792458       # meters per second
electron_mass = 9.10938e-31   # kilograms

print(f"Speed of light:  {light_speed:.3e}")
print(f"Electron mass:   {electron_mass:.4e}")
Speed of light:  2.998e+08
Electron mass:   9.1094e-31

The .3e format means three digits after the decimal point in scientific notation. Using uppercase E produces an uppercase exponent marker — a common requirement in scientific publishing:

avogadro = 6.02214076e23
print(f"Avogadro's number: {avogadro:.5E}")
Avogadro's number: 6.02214E+23

Python also handles percentage formatting through the % type, which multiplies the value by 100 and appends the percent sign automatically. This is one of the more elegant features of python number format:

accuracy = 0.9742
discount = 0.15

print(f"Model accuracy:  {accuracy:.1%}")
print(f"Discount rate:   {discount:.0%}")
Model accuracy:  97.4%
Discount rate:   15%

No manual multiplication, no manual string concatenation — Python handles both in a single format specifier.

Formatting Numbers with format() in Python

Before f-strings became standard, the built-in format() function and the .format() string method were the primary way to handle python format number tasks. These still appear frequently in existing codebases, libraries, and any situation where the format string must be stored in a variable.

The standalone format() function takes a value and a format specifier string and returns the formatted result:

pi = 3.14159

print(format(pi, ".2f"))
print(format(1234567, ","))
print(format(0.0045, ".2e"))
3.14
1,234,567
4.50e-03

The format specifier syntax is identical to f-strings — only the way you write the call is different. The .format() method on strings lets you embed and format multiple values at once using named placeholders:

template = "Name: {name:<15} Score: {score:06.2f} Rank: {rank:>4}"

print(template.format(name="Alice",   score=98.5,    rank=1))
print(template.format(name="Bob",     score=87.333,  rank=12))
print(template.format(name="Charlie", score=72.1,    rank=100))
Name: Alice           Score: 098.50 Rank:    1
Name: Bob             Score: 087.33 Rank:   12
Name: Charlie         Score: 072.10 Rank:  100

This approach is still valuable when format strings come from configuration files or database templates, since f-strings can only be written as literals in source code. You can find the complete reference for every available option in the Python format specification mini-language documentation.

Full Working Example

This example combines every python number format technique from this guide into a single financial report generator — decimal places, thousands separators, scientific notation, integer zero-padding, and percentage formatting all working together.

from datetime import date


def generate_report(records):
    today = date.today().strftime("%B %d, %Y")
    width = 62

    print(f"{'QUARTERLY FINANCIAL REPORT':^{width}}")
    print(f"{'Report Date: ' + today:^{width}}")
    print("-" * width)

    col_header = f"{'Division':<26} {'Revenue':>14} {'Growth':>9} {'Share':>8}"
    print(f"\n{col_header}")
    print("-" * width)

    total = sum(r["revenue"] for r in records)

    for r in records:
        share = r["revenue"] / total
        sign = "+" if r["growth"] >= 0 else ""
        print(
            f"{r['name']:<26}"
            f" ${r['revenue']:>12,.2f}"
            f"  {sign}{r['growth']:>5.1f}%"
            f"  {share:>5.1%}"
        )

    print("-" * width)
    print(f"{'TOTAL':<26} ${total:>12,.2f}")

    largest = max(r["revenue"] for r in records)
    smallest = min(r["revenue"] for r in records)

    print(f"\nLargest division revenue:  {largest:.4e}")
    print(f"Smallest division revenue: {smallest:.4e}")
    print(f"Revenue ratio (L/S):       {largest / smallest:.2f}x")
    print(f"\nDivisions on record: {len(records):04d}")
    print(f"Average revenue:     ${total / len(records):,.2f}")


quarterly_data = [
    {"name": "North America",    "revenue": 5_241_830.75, "growth":  14.2},
    {"name": "Europe",           "revenue": 2_893_412.50, "growth":  -2.8},
    {"name": "Asia Pacific",     "revenue": 4_107_655.00, "growth":  21.3},
    {"name": "Latin America",    "revenue": 1_032_980.25, "growth":   6.7},
    {"name": "Middle East",      "revenue":   748_334.60, "growth":   3.1},
    {"name": "Africa",           "revenue":   415_221.80, "growth": -0.5},
]

generate_report(quarterly_data)

Output

           QUARTERLY FINANCIAL REPORT            
             Report Date: May 15, 2026           
--------------------------------------------------------------

Division                       Revenue    Growth    Share
--------------------------------------------------------------
North America              $5,241,830.75   +14.2%   36.8%
Europe                     $2,893,412.50    -2.8%   20.3%
Asia Pacific               $4,107,655.00   +21.3%   28.8%
Latin America              $1,032,980.25    +6.7%    7.2%
Middle East                  $748,334.60    +3.1%    5.3%
Africa                       $415,221.80    -0.5%    2.9%
--------------------------------------------------------------
TOTAL                     $14,439,434.90

Largest division revenue:  5.2418e+06
Smallest division revenue: 4.1522e+05
Revenue ratio (L/S):       12.62x

Divisions on record: 0006
Average revenue:     $2,406,572.48