Key takeaway
An exact decimal value can still change meaning when its scale is lost or its rounding stage changes. Preserve the original value and a tested receiving rule.
Exact storage does not choose the receiving convention
A tabular field uses an exact decimal representation, so the receiving team assumes every displayed amount must agree. Exact decimal storage avoids one source of binary approximation; it does not decide scale, tie-breaking or when a later rounding operation occurs. Two systems can start with the same exact value and produce different two-place results under different explicit rules.
Apache Parquet defines its DECIMAL logical annotation using an unscaled integer multiplied by 10 to the negative scale. Precision is the permitted digit capacity, while scale identifies fractional decimal digits. Carry that logical metadata with the physical field. Copying its integer payload into a generic numeric column without the annotation can change the interpreted value before any arithmetic begins.
Decode the coefficient before rounding the value
In this hypothetical fixture, the physical coefficient 12345 has scale 3 and precision 5. Its logical value is 12345×10⁻³=12.345. Interpreting the same coefficient at scale 2 gives 123.45, ten times as large. These are invented scalar values in a receiving-format test, not invoices, offers or observed financial records.
Precision 5 does not mean five places after the decimal point. Nor does decoding the value to 12.345 prescribe whether a downstream two-place field should hold 12.34 or 12.35. That later transformation requires its own declared rounding rule. Preserve the exact original alongside the derivative so a reviewer can locate whether the disagreement began in decoding or quantization.
| Synthetic input | Declared operation | Result | Review distinction |
|---|---|---|---|
| Coefficient 12345; scale 3 | Decode coefficient ×10⁻³ | 12.345 | Original logical value |
| Same coefficient; scale 2 | Decode coefficient ×10⁻² | 123.45 | Different interpretation |
| Exact 12.345 | Two-place HALF_EVEN | 12.34 | Tie goes to even final digit |
| Exact 12.345 | Two-place HALF_UP | 12.35 | Tie goes away from zero |
| Exact −12.345 | Two-place HALF_UP | −12.35 | Away from zero, not toward positive infinity |
Name the tie rule, including negative values
Python’s Decimal documentation distinguishes ROUND_HALF_EVEN from ROUND_HALF_UP. The former selects the nearest even result at a tie; the latter resolves ties away from zero. The word up is therefore insufficient as a business rule. For exact −12.345 at two places, HALF_UP yields −12.35, whereas HALF_EVEN yields −12.34. A rule toward positive infinity would be another operation.
The author computed the fixture using Decimal values constructed from the stated decimal strings and explicit quantize arguments. No binary-float conversion is part of that check, and no Parquet writer or receiving database was executed. Test the actual complete path separately, including source extraction, logical metadata, reader type and final field conversion. An exact result produced after an earlier undocumented conversion cannot recover lost source precision.
A second rounding stage can change the answer
Now take the hypothetical exact value 12.3449 and require a two-place HALF_UP result. Rounding directly to two places gives 12.34. If an intermediate export first rounds to three places, it produces 12.345; applying the same HALF_UP rule again at two places then gives 12.35. Both stages follow the named rule, but the two-stage pipeline differs from the agreed direct transformation.
That example isolates double rounding, without floating-point cancellation or a changed row order. The completed acceptance decision is to retain the original exact value and perform the agreed final quantization at the stated boundary. If an unavoidable source step already rounds, record that provenance and reconsider what final claim the retained data can support.
Choose rejection behavior as well as success behavior. A receiving field can overflow its permitted precision or receive more fractional places than the contract allows. Python documents using quantize with an Inexact trap as one validation method; it is not automatically the policy of every database. Preserve any rejection or rounding signal rather than silently treating a coerced value as unchanged source evidence.
Use the fixture as a receiving contract
The final fixture binds coefficient, scale, precision, exact logical value, target scale, rounding mode, rounding stage and expected rejection cases. Include positive and negative ties, a value just below the tie and a value that exceeds the target precision. Report actual observed receiving results before claiming that a format or reader round trip preserves the field.
This is a numeric interpretation decision. The example supplies no pricing rate, payable amount, tax treatment or accounting policy. A domain owner must choose the rule that fits the real field and its governing requirements. The inventory can record the original logical type and the receiving transformation so a later reviewer can distinguish a deliberate derivative from a scale error or unrecorded rounding step.