Coerce floats/strings/Decimals to Decimal with predictable scale and rounding.
Project description
always-decimal
A tiny Python package to safely convert floats, strings, and numbers into Decimal objects.
It solves common headaches when comparing PostgreSQL numeric values with Python floats by ensuring consistent Decimal values.
✨ Features
- 🔒 Exact conversion from
float→Decimal(no hidden rounding). - ⚖️ Configurable coercion: set scale, rounding mode, normalize trailing zeros.
- 🧩 Works with
float,int,str, orDecimal. - 🛡️ Raises clear
DecimalCoercionErroron invalid inputs. - 🐍 Compatible with Python 3.10+.
- 📦 Easy install via uv or pip.
📦 Installation
Using uv (recommended)
uv add always-decimal
Using pip
pip install always-decimal
For local development:
uv pip install -e ".[dev]"
# or
pip install -e ".[dev]"
🚀 Usage
from always_decimal import ensure_decimal, to_decimal_exact
# 1. Exact conversion: no rounding, no scale changes
d1 = to_decimal_exact(0.1)
print(d1)
# Decimal('0.1000000000000000055511151231257827021181583404541015625')
# 2. Safe coercion with fixed scale
price = ensure_decimal(19.995, scale=2)
print(price)
# Decimal('20.00') (ROUND_HALF_EVEN default)
# 3. String input with quantization
val = ensure_decimal("1.2345", scale=3)
print(val)
# Decimal('1.235')
# 4. Normalizing (remove trailing zeros)
norm = ensure_decimal("1.2300", scale=4, normalize=True)
print(norm)
# Decimal('1.23')
⚙️ API
to_decimal_exact(value: float | Decimal) -> Decimal
Convert a float or Decimal to a Decimal exactly:
float→Decimal.from_float(value)Decimal→ returned as-is RaisesTypeErrorfor other types.
ensure_decimal(value, *, scale=None, rounding=ROUND_HALF_EVEN, clamp_exp=True, normalize=False) -> Decimal
Convert and coerce input to Decimal.
- scale: number of fractional digits to quantize (e.g., 2 → cents).
- rounding: rounding mode (default: bankers rounding).
- clamp_exp: clamp exponents to context limits.
- normalize: trim trailing zeros.
🧪 Running Tests
make test
or directly:
pytest
📄 License
🙌 Contributing
Issues and PRs are welcome!
Please format with ruff, type-check with mypy, and run pytest before submitting.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file always_decimal-1.0.0.tar.gz.
File metadata
- Download URL: always_decimal-1.0.0.tar.gz
- Upload date:
- Size: 39.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
637dd11f3d14c9009f40df400ef98961618ec6b7c9ae0da65f60e8395daeb918
|
|
| MD5 |
f3f30648f15e27a2ed58b73182f1b1de
|
|
| BLAKE2b-256 |
5083d8ab2b1e151f1d0a86b3afe2c7eb760087e9421c67cf4c4340c69e2e6a58
|
Provenance
The following attestation bundles were made for always_decimal-1.0.0.tar.gz:
Publisher:
ci-publish.yml on vib795/always-decimal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
always_decimal-1.0.0.tar.gz -
Subject digest:
637dd11f3d14c9009f40df400ef98961618ec6b7c9ae0da65f60e8395daeb918 - Sigstore transparency entry: 411467035
- Sigstore integration time:
-
Permalink:
vib795/always-decimal@6dca4d62a6ed92df41c1284e4b03fa4b7c3b6606 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/vib795
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci-publish.yml@6dca4d62a6ed92df41c1284e4b03fa4b7c3b6606 -
Trigger Event:
push
-
Statement type:
File details
Details for the file always_decimal-1.0.0-py3-none-any.whl.
File metadata
- Download URL: always_decimal-1.0.0-py3-none-any.whl
- Upload date:
- Size: 4.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6726d95e68f15f2c47d522433ae518f26a06fd4e97eece4657118a9a1d81b434
|
|
| MD5 |
4bf3cb18cfc84e24d682ff319b52afe1
|
|
| BLAKE2b-256 |
838c954ad5b851a1b460771f24d94739d8c1ce41b200e7417173f9b613626084
|
Provenance
The following attestation bundles were made for always_decimal-1.0.0-py3-none-any.whl:
Publisher:
ci-publish.yml on vib795/always-decimal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
always_decimal-1.0.0-py3-none-any.whl -
Subject digest:
6726d95e68f15f2c47d522433ae518f26a06fd4e97eece4657118a9a1d81b434 - Sigstore transparency entry: 411467050
- Sigstore integration time:
-
Permalink:
vib795/always-decimal@6dca4d62a6ed92df41c1284e4b03fa4b7c3b6606 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/vib795
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci-publish.yml@6dca4d62a6ed92df41c1284e4b03fa4b7c3b6606 -
Trigger Event:
push
-
Statement type: