Python prints very small floats in scientific notation, like 1e-05 instead of 0.00001. The decimal form comes back with the right format specifier, in your output and in the files you write.
Where the exponent form comes from
Python stores a float as a binary fraction, and when it has to show you that value it produces the fewest digits that still round-trip. For most magnitudes the result is ordinary decimal text, and outside that band the shortest correct form is the exponent one.
The band is narrower than most people expect. Run a few magnitudes through print and the boundaries show up immediately.
- Values from 1e-4 up to just below 1e16 print in plain decimal.
- At 1e16 and above, Python prints the exponent form.
- Below 1e-4, Python prints the exponent form as well.
repr and str return the same string for a float in Python 3, so wrapping the value in a list or a dictionary does not change the text you see. Both call the same shortest-representation routine.
Format one float with a fixed number of decimals
The f specifier asks for fixed point notation with the number of decimal places you name, and it hands back a string you can print, log, or write to a file.
rate = 1.2e-05
print(f"{rate:.10f}")
print(format(rate, ".10f"))
print("%.10f" % rate)
Each line above produces 0.0000120000. The format method and the percent operator are older spellings of the same instruction, and any of them works when you are editing code that already uses one style.
The digit count is a promise about width rather than accuracy, because ten decimals means ten characters after the point even when the value only justifies six of them.

The large value needs no special handling because the f specifier never switches to exponent form. What it will do is pad the small value until the width matches your request, which matters when the number lands in a fixed-width report.
Pick the specifier that matches the output you want
Four specifiers cover almost every case, and they differ in how many digits survive and whether an exponent is allowed. Choosing the wrong one is the usual reason a formatted value still arrives in notation you did not want.
| Specifier | 1.2e-05 becomes | Notes |
|---|---|---|
| f | 0.000012 | Fixed point, six decimals by default |
| e | 1.200000e-05 | Exponent form, always |
| g | 1.2e-05 | Chooses for you, six significant digits |
| % | 0.0012% | Multiplies by 100, adds the sign |
The g specifier is the one that surprises people, because it looks like a general-purpose choice and it keeps the exponent when the value is small. A format string that reads as neutral is not the same as one that prints plain decimals.
value = 1.2e-05
print(f"{value:g}")
print(f"{value:.3e}")
print(f"{value:.2%}")
The digit count before the specifier letter also sets rounding. A value with more decimals than you asked for is rounded to the requested width rather than truncated, which is why a total can appear to change by a cent between two reports.
Suppress the exponent form in NumPy output
NumPy decides notation per array rather than per value, and it switches to scientific notation when the array’s smallest absolute value sits below 1e-4 or when the ratio between the largest and smallest entry is above 1000. The set_printoptions function exposes that decision as a flag.
import numpy as np
tiny = np.array([1.2e-05, 3.4e-07])
mixed = np.array([1.2e-05, 8.1e9])
print("default tiny :", tiny)
np.set_printoptions(suppress=True, precision=8)
print("suppress tiny :", tiny)
print("suppress mixed:", mixed)

Read the third line of that run carefully, because it is where most advice about this flag goes wrong. On NumPy 2.5.3, suppress=True flips small values to fixed point and leaves a 8.1e9 entry in exponent form, so an array that mixes magnitudes still prints 8.1e+09.
When you need every entry in plain decimals, install a formatter instead of relying on the flag. The formatter receives each float and returns the text NumPy will use.
np.set_printoptions(formatter={"float_kind": lambda x: f"{x:.10f}"})
print(np.array([1.2e-05, 8.1e9]))
The same run prints both entries as 0.0000120000 and 8100000000.0000000000. For a single value, format_float_positional returns the fixed point string directly and leaves the global print settings untouched.
Do the same across a pandas table
pandas keeps its own formatting layer, and a DataFrame built from small floats will print a column of exponents until you tell it otherwise. The to_string method shows the frame as you would see it in a notebook, so it is the fastest way to confirm a setting took effect.
import pandas as pd
frame = pd.DataFrame({"measurement": [1.2e-05, 8.1e9]})
print(frame.to_string())
pd.set_option("display.float_format", lambda x: f"{x:.12f}")
print(frame.to_string())
- display.float_format changes what a notebook or terminal shows, and leaves the underlying float64 values alone.
- to_string accepts its own float_format argument when you need the plain text for one call only.
- to_csv takes float_format separately, because the display option does not reach the file writer.
frame.to_csv("measurements.csv", float_format="%.10f", index=False)
A spreadsheet will still read the result as a number, because the file holds digits rather than a display instruction. Widening the decimal count changes the text of the cell and not the stored value.
Use Decimal when the digits have to be exact
A float holds about fifteen to seventeen significant decimal digits, and formatting it cannot recover digits that were never stored. The Decimal type keeps decimal digits exactly, which is what you want for currency and for anything that gets compared against a printed figure.
from decimal import Decimal
value = Decimal("1e-7")
print(str(value))
print(f"{value:f}")

Decimal carries an exponent of its own, so str returns 1E-7 and the same value becomes 0.0000001 only once you format it. The type preserves the digits you typed, which is why Decimal(“1e-7”) and Decimal(1)/Decimal(7) both print without the noise a float would add.
total = Decimal("0.1") + Decimal("0.2")
print(total)
print(f"{total:.20f}")
That pair prints 0.3 at both widths, while the float version of the same sum reaches 0.30000000000000004441 by the twentieth decimal. When a printed total has to match an invoice, the type of the variable is what makes it match.
The extra digits you expose are float noise
Widening the format string does not add information. It reveals the binary fraction that was always underneath, and knowing that keeps you from chasing a precision problem that does not exist.
print(0.1 + 0.2)
print(f"{0.1 + 0.2:.20f}")
print(float(10**20))
The first line prints 0.30000000000000004, the second shows 0.30000000000000004441, and converting a large integer to a float prints 1e+20 because the nearest binary value no longer lands on the integer you started with. None of those results are wrong. They are the honest text of the value you asked Python to hold.
Round to a width that matches your source data and stop there. Ten decimals on a measurement recorded to six decimals produces eight characters of noise that a reader will try to interpret.
Where the exponent form still shows up
A format specifier only applies where you wrote it. Several common outputs take a float and choose their own notation, and each has its own override.
import json
print([1.2e-05])
print(json.dumps({"rate": 1.2e-05}))
The list prints 1.2e-05 because a container calls repr on its items, and json.dumps writes 1.2e-05 because the encoder emits the shortest round-trip form for interoperability. Send the value through your own formatter first when a downstream system reads the text and not the number.
import csv, io
buffer = io.StringIO()
csv.writer(buffer).writerow([1.2e-05])
print(buffer.getvalue())
The csv module writes str(value) into the cell, so a small rate lands in the file as 1.2e-05. Pass float_format when you use pandas to write the same file, or format the value yourself when you write the row by hand.
How do I stop Python from printing numbers in scientific notation?
Format the value with the f specifier, as in f'{value:.10f}’. That asks Python for fixed point notation with ten decimal places and never switches to an exponent. Use format(value, ‘.10f’) or ‘%.10f’ % value when the surrounding code already uses those styles.
Why does NumPy still print scientific notation with suppress set to True?
In current NumPy versions suppress=True converts small values to fixed point but leaves entries at very large magnitudes in exponent form, so an array that mixes 1.2e-05 with 8.1e9 still prints 8.1e+09. Set a float_kind formatter with set_printoptions when every entry has to print as plain decimals.
Does suppressing scientific notation change the stored value?
No. A format specifier builds a string and leaves the float untouched. The digits you see beyond the stored precision come from the underlying binary fraction, so widening the format string reveals noise rather than recovering accuracy.
When should I use Decimal instead of formatting a float?
Use Decimal when the printed digits have to be exact, such as currency totals that must match another system. A float stores roughly fifteen to seventeen significant decimal digits, so formatting cannot add precision that the binary value never held.

