The parse_float parameter in Python’s tomllib module lets you control how TOML floating-point literals are converted. By default, values such as 0.1, 3.14, and 1e-6 become Python float objects. That behavior is convenient, but it may be unsuitable for financial settings, exact decimal thresholds, audit-sensitive systems, or applications that need custom numeric types.
This guide explains how to use parse_float with tomllib.load and tomllib.loads, why Decimal is often the best choice, which restrictions apply, and how to test the result. For a broader introduction, see our Python tomllib guide.
What parse_float does
parse_float receives a callable. Every time the TOML parser finds a floating-point literal, it passes the original text to that callable. The callable returns the object stored in the final dictionary.
import tomllib
from decimal import Decimal
config = tomllib.loads('rate = 0.1', parse_float=Decimal)
print(config['rate'])
print(type(config['rate']))
The result is Decimal('0.1'), preserving the digits exactly as written.
Why float may be insufficient
Python float uses binary IEEE 754 representation. Many simple decimal fractions do not have an exact binary representation. Therefore, calculations may produce values such as 0.30000000000000004. This is expected behavior, not a parser bug.
For graphics, approximate measurements, and many scientific workloads, binary floating point is perfectly appropriate. For prices, tax rates, accounting rules, contractual limits, or exact comparisons, decimal arithmetic is often a better fit. Read our Python Decimal guide for more context.
Using tomllib.load
When reading a file, open it in binary mode and pass the converter:
from decimal import Decimal
import tomllib
with open('config.toml', 'rb') as file:
data = tomllib.load(file, parse_float=Decimal)
Only TOML floating-point literals use this callback. Integers remain int, booleans remain bool, strings remain strings, and date or time values use the appropriate datetime types.
Custom converters
The callable can perform validation or return a custom type:
from decimal import Decimal
def parse_nonnegative(text: str) -> Decimal:
value = Decimal(text)
if value < 0:
raise ValueError('negative values are not allowed')
return value
This approach can reject bad configuration values early. However, parsing should not replace full schema validation. You should still validate required keys, nested structures, ranges, and relationships after loading. A typed configuration layer using classes or dataclasses can help; see our Python dataclasses guide.
Important restrictions
The callback must not return a list or dictionary. Returning container types would interfere with TOML’s own structural mapping. Use scalar-like values or custom objects instead.
If the callback raises an exception, parsing stops. Catch both TOML syntax errors and converter errors at the application boundary:
try:
data = tomllib.loads(text, parse_float=parse_nonnegative)
except (tomllib.TOMLDecodeError, ValueError) as exc:
print(f'Invalid configuration: {exc}')
Decimal context and precision
Constructing Decimal from the original string preserves the literal exactly. Later arithmetic follows the active decimal context, including precision and rounding. Configure the context only where necessary, preferably with localcontext for isolated calculations.
Do not convert the TOML literal to float first and then to Decimal. That would preserve the binary approximation rather than the original decimal value. The key advantage of parse_float=Decimal is direct string-to-decimal conversion.
Price configuration example
# config.toml
[product]
price = 19.90
discount = 0.15
from decimal import Decimal
import tomllib
with open('config.toml', 'rb') as file:
config = tomllib.load(file, parse_float=Decimal)
price = config['product']['price']
discount = config['product']['discount']
final_price = price * (Decimal('1') - discount)
print(final_price.quantize(Decimal('0.01')))
The application stays in decimal arithmetic from input to final rounding.
Scientific notation
TOML supports exponent notation. The converter receives the original text, so 1e-6 becomes Decimal('1e-6'). This makes the technique useful for tolerances, probabilities, and engineering constants that require controlled precision.
Custom numeric wrappers
A project can return a custom wrapper that stores the original literal, unit metadata, source location, or audit information. This may be useful in specialized systems, but it increases complexity. Most applications should prefer Decimal because it is standard, well documented, and compatible with established arithmetic patterns.
Validation after parsing
TOML parsing confirms syntax, not business correctness. After loading, verify that all required sections exist, types match expectations, and values stay within acceptable ranges. Type hints can make that contract clearer; see our Python type hints guide.
Testing strategy
Test exact decimals, negative values, zero, exponent notation, large magnitudes, and malformed TOML. Check both the value and the resulting type.
def test_exact_decimal():
data = tomllib.loads('value = 0.1', parse_float=Decimal)
assert data['value'] == Decimal('0.1')
assert isinstance(data['value'], Decimal)
Also verify that integer literals do not use the callback. The TOML literal 1 remains an integer, while 1.0 uses parse_float.
Performance considerations
Decimal arithmetic is slower than binary floating point. Configuration files are usually small and loaded infrequently, so the overhead is normally negligible. If a system parses very large TOML documents repeatedly, benchmark the actual workload before optimizing.
TOML is primarily a configuration format. It should not be used as a high-volume analytics transport when throughput is the main requirement.
Security and maintainability
Keep the callback deterministic, small, and free from network access or hidden global mutations. Parsing configuration should not execute arbitrary code. Document the resulting numeric type so downstream code does not assume float.
The official Python tomllib documentation describes the API, while the TOML specification defines valid floating-point syntax.
When to use parse_float
Use it when exact decimal representation is required, when configuration values need early validation, or when a custom numeric type provides clear benefits. Keep the default float behavior for approximate UI settings, noncritical measurements, coordinates, or parameters where binary floating point is expected.
Conclusion
tomllib.parse_float is a small but powerful extension point. It lets you preserve decimal literals, apply controlled validation, and align configuration parsing with your application’s numeric requirements. Combined with post-parse validation, clear error handling, tests, and documentation, it produces a reliable configuration pipeline without requiring a third-party TOML parser.







