Decimal Precision and Rounding#
Lactuca provides fine-grained control over the number of decimal places reported by every
public method. All settings live in the global Config singleton, accessible via
Config() (the class constructor always returns the same instance) or the pre-created
module-level alias config — both exported from the package. Settings are also surfaced
as read-only properties on table instances through the decimals attribute.
Default precision strategy#
The defaults reflect a deliberate hierarchy:
Group |
Default decimals |
Rationale |
|---|---|---|
Base probabilities ( |
15 |
Foundation of all calculations; maximum float64 precision preserved |
Commutation functions ( |
10 |
Large-magnitude accumulated sums; 10 decimals is sufficient |
Final outputs ( |
15 |
Users can round as needed without losing information |
The 15-decimal default ensures that extreme ages (\(l_{111} \approx 4.5 \times 10^{-5}\)) and continuous integration paths do not silently round intermediate values to zero.
Viewing current settings#
from lactuca import LifeTable
lt = LifeTable("PASEM2020_Rel_1o", "m")
# Access via properties on the table
print(lt.decimals.qx) # 15
print(lt.decimals.annuities) # 15
print(lt.decimals.Dx) # 10
Alternatively, query the singleton directly without a table instance:
from lactuca import config
print(config.decimals.qx) # 15
print(config.decimals.annuities) # 15
print(config.decimals.Dx) # 10
Changing settings#
Decimal properties on table objects (lt.decimals.*) are read-only. Attempting to
assign a value raises AttributeError:
from lactuca import LifeTable
lt = LifeTable("PASEM2020_Rel_1o", "m")
lt.decimals.annuities = 8
# AttributeError: decimals.annuities is read-only on a table instance.
# To change globally: config.decimals.annuities = 8
To change precision, write to the Config singleton. Both import styles are
equivalent — Config() and config are the same object:
from lactuca import Config, config
# Using Config() — the constructor always returns the same singleton
Config().decimals.annuities = 8
# Using the pre-created alias — identical effect
config.decimals.qx = 12
# Flat-key form (useful in scripts and batch changes)
config.set("decimals_annuities", 8)
config.set_many({"decimals_annuities": 8, "decimals_qx": 12})
# Verify
print(config.decimals.annuities) # 8
All changes take effect immediately and globally for all subsequent calculations in the process.
TOML configuration file#
Settings can also be loaded from a TOML file — useful for notebooks and shared project
defaults. Decimal keys go under [decimals] without the decimals_ prefix:
# lactuca_config.toml
[decimals]
annuities = 6
insurances = 6
qx = 10
[calculation]
calculation_mode = "continuous_precision"
from lactuca import config
config.load("lactuca_config.toml")
Note
The config_path argument to Config(config_path=...) is only respected on the
first instantiation of the singleton. Once the singleton exists, the argument is
silently ignored. Use config.load(...) to reload settings reliably at any point in
the session.
See Configuration for the complete settings reference and TOML format.
Full list of decimal settings#
Config key |
|
Affects |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note
lt.decimals.X is read-only on all table instances — see Changing settings above.
ix and ox are not available on LifeTable; the full availability matrix is in
Available properties by table type.
Available properties by table type#
Each table class exposes only the decimal properties that are relevant to its calculation
set. Accessing a blocked property raises AttributeError with a descriptive message.
Property |
|
|
|
|---|---|---|---|
|
✅ |
✅ |
✅ |
|
✅ |
❌ |
❌ |
|
✅ |
❌ |
❌ |
|
❌ |
✅ |
❌ |
|
❌ |
❌ |
✅ |
|
✅ |
❌ |
❌ |
|
✅ |
❌ |
❌ |
|
✅ |
❌ |
❌ |
Example — attempting to read a blocked property:
from lactuca import LifeTable
lt = LifeTable("PASEM2020_Rel_1o", "m")
print(lt.decimals.ix) # AttributeError: decimals.ix is not available for LifeTable.
Rounding implementation#
All rounding uses np.round(value, decimals). Lactuca applies rounding at exactly two
points in each calculation chain:
Table build time — the
lxcolumn is rounded todecimals.lxdecimal places when the table is first constructed. In both continuous calculation modes ("continuous_precision"and"continuous_simplified"), the full-precision survivor values are kept in a separate internal store and used instead, so lx rounding does not affect continuous annuity and insurance calculations.Public method output — each public method rounds its return value once, using the setting that matches the quantity returned.
No additional intermediate rounding occurs between these two points. For example,
tpx(x, t) computes \({}_{t}p_x = l_{x+t}/l_x\) from the rounded lx column and
rounds only the final result to decimals.tpx decimal places.
See also#
Configuration — complete settings reference and TOML file format
Numerical Precision — why internal calcs use float64 end-to-end
qx Derivation Flow — where rounding boundaries occur in the derivation chain