- Guides
Money, quantities, and ambiguous spoken numbers
Resolve spoken amounts with explicit currency, units, decimal text, rounding policies, and reproducible negative fixtures.
- Author
Udit Goenka- Editorial date
- Reading time
- 25 min

TL;DR: Resolve whether a spoken number means money, a quantity, or an identifier before converting it. Keep the original surface separate from approved decimal text, require an explicit currency or unit, and test scale, rounding increments, negative zero, and confirmation as different boundaries.
A caller says "one twenty." That phrase might describe a price, a quantity, a time, or part of an identifier. A perfectly transcribed phrase still leaves the business value unresolved. Converting it immediately to a familiar decimal only hides the missing decision.
Start by identifying the requested field and the evidence needed to approve it. The postal-code guide shows why digits can identify something without representing an arithmetic quantity. This guide handles the next boundary: when the field really is an amount, preserve its meaning through decimal conversion and repeat-back.
The executable reference accepts already resolved decimal text under an explicit policy. It does not interpret arbitrary spoken number phrases, set real currency rules, execute payments, or claim recognition accuracy. Its fixtures are synthetic arithmetic checks, separate from evidence that a caller intended a particular amount.
Table of contents
- Resolve meaning before conversion
- Keep the surface and authoritative value separate
- Require currency and units explicitly
- Construct decimals from supported text
- Separate scale from cash increments
- Run the bounded decimal reference
- Interpret the negative fixtures
- Design a repairable repeat-back
- Carry exact values across adapters
- Evaluate interpretation and arithmetic separately
- Choose the boundary your workflow needs
- Frequently asked questions
- Ship an explicit amount contract
Resolve meaning before conversion
An amount needs a field kind before it needs a numeric representation. Money, measured quantities, counts, and identifiers have different rules even when they share a visible sequence of digits. The requested workflow can supply that kind, but it must do so explicitly rather than borrowing a guess from a transcript.
For example, a question asking for the weight of a parcel can establish a quantity field. It does not necessarily establish the measurement unit. A question asking for an order number establishes an identifier field, where arithmetic conversion may remove meaningful characters. The order-ID and email capture guide treats that literal-character contract separately.
The phrase "one twenty" remains unresolved if the application cannot distinguish one hundred twenty from one and twenty hundredths in the current task. Ask a question that exposes the ambiguity in the caller's own context. Do not manufacture a probability threshold or accept a familiar reading simply because it produces a syntactically valid amount.
The self-correction guide explains why clarification should identify the missing distinction. Repeating the same open prompt can produce the same ambiguous phrase. A useful repair asks about the unit, the whole amount, or the fractional portion without pretending that an unconfirmed candidate is already authoritative.
Keep interpretation evidence with the candidate. An explicit caller correction, a selected unit, and a workflow's known field kind are different evidence from a model's preferred transcript. A numeric parser should receive a resolved candidate and a named policy; it should not silently decide the business meaning of every input string.

The first useful result is therefore a typed candidate or a clarification outcome. A decimal alone is incomplete. A system that cannot explain whether a value represents money or a measurement has not finished capture, regardless of how accurately its arithmetic library preserves the digits.
Keep the surface and authoritative value separate
Keep the original spoken or recognized surface separate from the approved decimal text. The surface supports repair and inspection; the decimal text supports a bounded parser. A display formatter can then produce a locale-specific presentation without overwriting either field.
The Arabic digits, dates, and currencies guide explains the same separation for localized surfaces. A caller's language and number words affect presentation and interpretation, but they do not authorize a currency conversion or a change in the requested unit. Preserve those decisions explicitly.
CLDR's number-formatting specification describes decimal symbols, grouping, currency patterns, and related locale data. It is a formatting authority, not evidence that a caller meant the application's preferred currency. A formatting locale can choose a visible separator while the workflow still lacks an approved amount.
The reference retains its exact input text in an immutable result alongside the canonical output. Its input is already a supported decimal string, rather than the original audio or an unrestricted transcript. A surrounding capture system would retain its own authorized surface evidence and the interpretation that produced this string.
Do not collapse those layers into one mutable dictionary shared with a formatter. If a display component inserts a grouping separator, it must not rewrite the arithmetic input. If a caller changes the amount, create a new candidate and invalidate approval of the earlier value. The workflow-enforcement guide describes that approval boundary.
An immutable result also makes a failure easier to locate. You can compare the resolved input, policy, canonical value, and display surface without guessing which component last mutated a shared field. Keep any retained caller material under the application's justified retention policy; reproducing this synthetic example requires no real financial or account data.
Require currency and units explicitly
A monetary amount requires a currency identity. A measurement requires a unit. A bare decimal cannot tell a downstream adapter whether to charge, weigh, count, schedule, or merely display something. Missing context should remain missing rather than defaulting to an application's most common transaction.
The word "dollars" can require clarification when the workflow has not established the currency. A caller's phone country, current language, or network route is insufficient authority to choose it. The phone-number capture guide explains why calling context itself needs careful interpretation.
Keep unit conversion outside the reference. A request expressed in one weight unit does not become a different unit merely because a database column expects it. An actual conversion contract needs an explicit source unit, destination unit, conversion rule, rounding policy, and caller-facing presentation. This article tests preservation under a selected unit, not general measurement conversion.
The policy record names kind, unit, lexical scale, increment in minor units, and whether negative nonzero values are allowed. The unit field is a bounded identifier in this teaching API. It is not a complete registry validator, and passing an arbitrary supported label does not make it an internationally recognized currency or measurement unit.
The sample uses USD, JPY, kilograms, and a generic unit in synthetic fixtures. Their selected scales illustrate that scale is policy data. The artificial five-minor-unit increment attached to the USD fixture is explicitly a synthetic business rule; it does not claim a real US cash-rounding requirement.

The partial-transcript guide is relevant here: provisional recognition must not authorize an operation. Supported units, ranges, and rounding rules belong in a verified contract. A conversational prompt can ask for the missing unit, but it should not invent a policy that the consuming adapter never agreed to implement.
Construct decimals from supported text
Python's decimal documentation distinguishes decimal construction, arithmetic contexts, and signals. Constructing from approved decimal text preserves that decimal value. Constructing from a binary float preserves the float's actual value, which may differ from the decimal spelling the caller intended.
A historical developer question about multiplying an amount by one hundred illustrates the representation problem. Its reported float result is a question from a particular program, not a benchmark for a voice system. The useful engineering lesson is to avoid introducing an unnecessary binary-float conversion before exact decimal handling.
The Python floating-point tutorial explains representation error. That source supports the type decision; it does not imply floats are unsuitable for every engineering task. This reference handles literal decimal amounts, so converting through float would add an avoidable approximation boundary.
Its grammar accepts a minus sign, an integer portion without unnecessary leading zeroes, and an optional decimal fraction. It accepts ASCII digits and a literal period as the decimal separator. It rejects whitespace, exponent notation, localized separators, a leading plus, and other forms outside the declared subset.
Those restrictions are not global judgments about valid human number notation. They define the handoff from an interpretation layer to this small parser. A localized parser can be added before that handoff only with a separate policy and fixtures. Python's regular-expression documentation supports using fullmatch and explicit character classes to enforce this exact subset.
The Unicode character-database documentation distinguishes decimal, digit, and numeric character properties. A broad Unicode numeric conversion can accept more than the downstream decimal grammar intends. This artifact performs no character mapping; unsupported localized input must first be resolved under a separately declared conversion boundary.
Decimal construction alone does not make subsequent arithmetic immune to context. The reference bounds input length and scale, uses an independent local context sufficient for those bounds, and traps inexact, rounded, invalid, or overflowing conversion operations. It also tests that intentionally restrictive ambient precision and exponent limits do not change the result. Shared ambient configuration must not silently alter the contract.

Separate scale from cash increments
Scale describes decimal places under this contract. An increment describes which multiples are permitted after a named rounding operation. They are related through the selected minor-unit representation, but they are not the same rule.
Calling Decimal.quantize with a decimal operand whose exponent is minus two establishes a two-place exponent. It does not, by itself, round a value to multiples of five cents. The decimal documentation defines quantize in terms of the exponent of its second operand. Treating the coefficient of that operand as a cash increment would implement a different rule from the one advertised.
The reference makes that difference visible. After validating lexical scale, it obtains an exact integer number of minor units. It divides the magnitude by the explicit increment, compares twice the remainder with the increment, and selects the nearest multiple. Exact halfway cases move away from zero under this chosen synthetic policy.
This is one declared rounding rule, not a universal business recommendation. Accounting, pricing, taxes, unit measurements, and cash settlement may require different rules and different stages. A real consuming contract must select its own rule and verify applicable requirements before performing transactions. The example performs none.
The confirmation prosody guide explains why a changed value needs an explicit presentation boundary. If rounding changes the canonical amount, the caller must not approve only the unrounded phrase while a different amount is committed. Keep the original candidate and the final proposed value visible to the confirmation controller.
Negative zero needs a deliberate policy too. Decimal can preserve a signed zero even though it compares equal to ordinary zero. This reference canonicalizes it to unsigned zero and records that the source was negative zero. Negative nonzero values remain controlled by the separate allow-negative setting.
Strict lexical scale is another explicit choice. With scale two, the reference rejects a source containing three fractional characters even when the final character is zero. That is a representation rule, not a claim that the mathematical values differ. If an application wants to accept extra zeroes, it must change that contract and add fixtures rather than silently weakening it.
Run the bounded decimal reference
Run this full fence with Python's standard library. It includes the reference functions and positive, negative, ownership, ambient-context, and serialization fixtures. The JSON printed at the end contains actual computed synthetic values. It is not a payment response, a carrier measurement, or evidence of caller intent.
The scope is intentionally small: resolve supported decimal text into an immutable value under an explicit policy. There is no speech recognizer, general number-word parser, network adapter, confirmation state machine, tax calculation, or production currency registry hidden behind the function name.
from dataclasses import dataclass, FrozenInstanceError
from decimal import (Decimal, Context, localcontext, Inexact, Rounded,
InvalidOperation, Overflow, ROUND_UP)
import json
import re
@dataclass(frozen=True)
class Policy:
kind: str
unit: str
scale: int
increment_minor: int = 1
allow_negative: bool = False
@dataclass(frozen=True)
class Value:
original: str
kind: str
unit: str
canonical: str
rounding_applied: bool
negative_zero_canonicalized: bool
def capture_decimal(text, policy):
if not isinstance(policy, Policy):
raise ValueError('explicit policy required')
if policy.kind not in ('money', 'quantity'):
raise ValueError('identifier is not an arithmetic kind')
if (not isinstance(policy.unit, str) or
not re.fullmatch(r'[A-Za-z][A-Za-z0-9_-]{0,15}', policy.unit)):
raise ValueError('explicit bounded unit required')
if (type(policy.scale) is not int or not 0 <= policy.scale <= 6 or
type(policy.increment_minor) is not int or
not 1 <= policy.increment_minor <= 1000000 or
type(policy.allow_negative) is not bool):
raise ValueError('invalid policy')
if (not isinstance(text, str) or not 1 <= len(text) <= 20 or
not re.fullmatch(r'-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?', text)):
raise ValueError('supported decimal text required')
places = len(text.split('.')[1]) if '.' in text else 0
if places > policy.scale:
raise ValueError('lexical scale exceeds policy')
number = Decimal(text)
if not number.is_finite():
raise ValueError('finite value required')
if number < 0 and not policy.allow_negative:
raise ValueError('negative value unsupported')
negative_zero = number.is_zero() and number.is_signed()
# All operands are bounded above; this independent context makes conversion exact.
with localcontext(Context(prec=40, traps=[Inexact, Rounded,
InvalidOperation, Overflow])):
minor = int(number.scaleb(policy.scale))
magnitude = abs(minor)
quotient, remainder = divmod(magnitude, policy.increment_minor)
# Explicit nearest-increment policy, ties away from zero.
rounded_magnitude = (quotient + (2 * remainder >= policy.increment_minor)) * policy.increment_minor
rounded_minor = -rounded_magnitude if minor < 0 else rounded_magnitude
digits = str(abs(rounded_minor)).zfill(policy.scale + 1)
rendered = (digits[:-policy.scale] + '.' + digits[-policy.scale:]
if policy.scale else digits)
if rounded_minor < 0:
rendered = '-' + rendered
return Value(text, policy.kind, policy.unit, rendered,
rounded_minor != minor, negative_zero)
def rejects(text, policy):
try:
capture_decimal(text, policy)
except ValueError:
return
raise AssertionError(('unexpected acceptance', text, policy))
money = Policy('money', 'USD', 2)
cash = Policy('money', 'USD', 2, 5, True) # Synthetic business policy, not a currency rule.
quantity = Policy('quantity', 'kg', 3)
assert capture_decimal('1.20', money).canonical == '1.20'
assert capture_decimal('1.03', cash).canonical == '1.05'
assert capture_decimal('1.02', cash).canonical == '1.00'
assert capture_decimal('-1.03', cash).canonical == '-1.05'
assert capture_decimal('0.025', Policy('quantity', 'unit', 3, 50)).canonical == '0.050'
assert capture_decimal('-0.025', Policy('quantity', 'unit', 3, 50, True)).canonical == '-0.050'
assert capture_decimal('1.234', quantity).canonical == '1.234'
assert capture_decimal('7', Policy('money', 'JPY', 0)).canonical == '7'
zero = capture_decimal('-0.00', money)
assert zero.canonical == '0.00' and zero.negative_zero_canonicalized
assert capture_decimal('0', money).canonical == '0.00'
for bad in (1.2, True, None, '', 'NaN', 'Infinity', '-Infinity', '1e2',
'1,20', '1,200.00', ' 1.20', '+1.20', '01.20', '١.٢٠',
'1.200', '9' * 21, '-1.20'):
rejects(bad, money)
for bad_policy in (Policy('identifier', 'id', 0), Policy('money', '', 2),
Policy('money', 'USD', True), Policy('money', 'USD', 7),
Policy('money', 'USD', 2, 0), Policy('money', 'USD', 2, True),
Policy('money', 'USD', 2, 1, 'yes'), None):
rejects('1', bad_policy)
saved = capture_decimal('1.03', cash)
try:
saved.canonical = '999.00'
except FrozenInstanceError:
pass
else:
raise AssertionError('mutable value')
with localcontext() as ambient:
ambient.prec = 2
ambient.Emax = 2
ambient.Emin = -2
ambient.rounding = ROUND_UP
assert capture_decimal('12345.67', money).canonical == '12345.67'
assert json.loads(json.dumps({'amount': saved.canonical}))['amount'] == '1.05'
print(json.dumps({'fixture_class': 'Synthetic', 'source': saved.original,
'canonical': saved.canonical, 'unit': saved.unit,
'rounding': 'nearest increment; ties away from zero',
'increment_minor': cash.increment_minor,
'negative_zero': zero.canonical,
'caller_intent': 'not-tested', 'transaction': 'not-performed'}))
The first money policy retains two decimal places without changing minor units. The synthetic cash policy uses the same currency label and scale but a five-minor-unit increment. Those are separate inputs, so the fixture can expose a rounding change instead of pretending it came from the currency label alone.
The quantity case demonstrates that the same representation boundary can support a measured amount when its kind and unit are explicit. It does not make all money and quantity policies interchangeable. A real unit registry, allowed range, and conversion contract remain the responsibility of the surrounding application.
Interpret the negative fixtures
Read each failure as evidence about one declared boundary. A rejection is useful when it prevents an unsupported representation from becoming a plausible business value. It is less useful if the application then substitutes zero, the previous candidate, or a hidden default and continues as though capture succeeded.
The non-finite fixtures include NaN and Infinity spellings. The strict input grammar rejects them before construction, and a defensive finite-value check remains adjacent to the conversion. A future grammar expansion must preserve that invariant. Successfully constructing a Decimal is not enough to establish an acceptable amount.
The type fixtures reject booleans, floats, and missing values. Python's boolean relationship to integers is a reason to validate policy integers by exact type where this contract requires it. A boolean scale or increment must not masquerade as the smallest legitimate numeric setting.
The missing-unit, unsupported-kind, invalid-scale, and invalid-increment fixtures test policy boundaries. A parser cannot compensate for a policy that says an identifier is money or omits its unit. Fail explicitly before attempting arithmetic, and keep the failure attached to the request that supplied the invalid policy.
The speculative tool-calling guide addresses the downstream version of the same problem. Validation must apply to the actual structured arguments that an adapter receives. A clear spoken prompt does not prove that a later JSON object carries the right types or policy identifiers.
The immutable result fixture attempts to overwrite the canonical value and expects that ordinary dataclass assignment to fail. All result fields are immutable scalar values. The example does not claim protection against arbitrary hostile Python reflection; it establishes ordinary reference ownership within this teaching API.

The synthetic negative-zero case confirms both the canonical output and the retained flag. The negative-nonzero case separately confirms rejection when the policy disallows it. Without those distinct assertions, a broad sign-removal routine could appear to handle negative zero while corrupting every negative amount.
Design a repairable repeat-back
A useful repeat-back states the amount with its currency or unit and any material rounding change. It should let the caller correct the part that is wrong. Repeating an unexplained decimal in a familiar voice is not the same as establishing the intended business value.
W3C's SSML specification defines mechanisms for controlling speech synthesis and interpretation hints. A provider's supported subset can differ; Google's SSML documentation describes its own supported behavior. Neither source turns a pronunciation instruction into proof that the caller understood or approved the amount.
Test the actual generated audio and the recognition of the caller's reply when evaluating a deployment. The pure Python fixture does not render speech. Keep that evidence boundary clear instead of attaching an invented pronunciation success rate to arithmetic tests that never touched an audio device.
The speaking-rate guide discusses how presentation can support comprehension. A pause between the whole amount and unit may help a particular script, but the semantic contract still needs explicit currency, scale, and candidate ownership. Presentation cannot fill missing authority.
A correction replaces the candidate that was repeated back. Changing the currency while preserving the digits is still a material correction. Changing a unit or rounding policy is material too, because the same literal now has a different interpretation or committed value. Old approval must not silently transfer to the new candidate.
The DTMF and voice capture guide covers ownership when switching modalities. Keypad input can clarify digit characters in a suitable workflow, but it does not authenticate a caller or identify a missing currency. Keep those responsibilities separate when offering an alternate input route.
This synchronous reference deliberately has no approval API. Its returned value is a candidate for a separate confirmation controller. If that controller awaits synthesis, recognition, or a tool, it must recheck the current candidate, age, and ownership after the await before using a reply. The arithmetic result alone cannot authorize a transaction.
Carry exact values across adapters
Serialize the canonical decimal spelling as a string with its explicit kind and unit, or use a separately agreed integer-minor-unit schema. Do not let a generic JSON encoder introduce an accidental float because the receiving API happens to accept numeric tokens.
RFC 8259 defines JSON number syntax and discusses interoperability limits between implementations. JSON has no built-in Decimal type. A string contract must therefore be documented with its grammar and semantics; the fact that a string survives transport does not tell the recipient which amount policy to apply.
The reference's final JSON assertion proves only that its selected canonical string survives Python's JSON encoding and decoding. A production SDK might coerce the field later. Test the actual adapter boundary with the complete payload and inspect the receiving type before claiming end-to-end decimal preservation.
The workflow-enforcement guide becomes relevant when that value reaches a side-effecting operation. A new caller correction is a new semantic request; an exact transport retry must preserve the original request identity and frozen payload. Reusing an old request identity with changed money fields confuses retry protection with a new instruction.
Keep the submitted payload and response evidence tied together. An adapter's timeout does not prove that no remote side effect occurred. This article makes no remote call, so it has no evidence about that outcome. A consuming workflow must follow its actual operation contract and obtain separate settlement or execution evidence.
The truthful tool-waiting guide explains why local waiting and remote execution are different facts. Do not repair an ambiguous operation by recomputing an amount under a newly changed rounding policy and sending it as though it were the same original request.
Evaluate interpretation and arithmetic separately
Use two evidence tracks. Interpretation tests ask whether the field kind, amount, currency, unit, and caller correction were resolved correctly. Arithmetic tests ask whether a known supported input obeys its declared scale and rounding policy. Passing one track does not establish the other.
The regression-testing guide discusses why transcript accuracy alone cannot establish task correctness. A recognizer may capture every word while the workflow chooses the wrong unit. Conversely, a repaired transcript may lead to the right approved amount despite an earlier recognition error.
Use synthetic arithmetic vectors for exact expectations, including values just below an increment boundary, just above it, at a tie, at zero, and at a permitted negative value. These examples can have deterministic assertions. Label them synthetic and avoid presenting their pass rate as a result from live callers or financial transactions.
Conversation fixtures should describe the question asked, explicit context, candidate interpretation, clarification, and approval evidence. Do not fabricate verbatim customer transcripts. A scripted fixture can demonstrate a contract without pretending it was observed in production or that it predicts a deployment's actual error rate.

The outcome metrics guide explains why unresolved and abandoned attempts belong in the ledger. Keep counts for ambiguity, unsupported notation, rejected policy, correction, approval, and downstream outcomes according to the actual workflow. Do not hide difficult attempts by counting only values that reached the arithmetic function.
Identity matters alongside counts. A repeated transport event should not become a second caller correction, while a genuinely new correction must remain distinguishable. Retain the evidence needed to associate each outcome with its real attempt and candidate revision rather than deriving a reassuring rate from anonymous totals alone.
Choose the boundary your workflow needs
Decide which behavior is required before extending this reference. A price quotation, a parcel-weight capture, and a refund instruction have different consuming contracts. Reusing a decimal helper is reasonable; reusing every policy by accident is not.
| Criterion | Supported decision ✓ | Unsupported shortcut ✗ |
|---|---|---|
| Field meaning | Explicit money or quantity kind | Guess from digit shape |
| Currency or unit | Required named policy field | Infer from caller language |
| Decimal construction | Approved bounded text | Convert through binary float |
| Lexical scale | Explicit per-policy limit | Assume every currency has two places |
| Increment rounding | Named multiple and tie rule | Treat quantize's coefficient as an increment |
| Negative zero | Canonicalize and retain a flag | Strip every negative sign |
| Unsupported notation | Clarify before conversion | Delete separators until parsing succeeds |
| Confirmation | Current amount plus unit and policy | Reuse approval after a material change |
Start with the consuming adapter's documented shape. If it requires integer minor units, verify what its currency and scale fields actually mean. If it requires decimal strings, verify its accepted grammar and range. The reference's unit label and synthetic increments do not replace those external specifications.
Keep resource bounds visible. The input length and maximum scale limit parsing work and make the chosen precision auditable. A production application can select different bounds, but then it should demonstrate that its intermediate arithmetic remains exact under those new limits. Arbitrarily increasing accepted input length without reviewing precision would weaken the stated guarantee.
The observability guide can help organize evidence when a caller reports a wrong amount. Preserve the resolved candidate, selected policy, canonical output, approval ownership, submitted payload, and remote evidence according to the application's actual retention rules. These stages identify where the meaning changed.
Avoid a larger framework until a real boundary requires it. The small function is useful precisely because an engineer can inspect every conversion and execute the entire published fixture. Add number-word interpretation, locale parsing, unit conversion, or transaction orchestration as separately named contracts with their own negative cases rather than hiding them behind a function that appears to parse everything.
An implementation decision should specify when the candidate becomes stale. A caller can interrupt a repeat-back, revise only the fractional portion, or start correcting a unit before the previous prompt finishes. The interruption-safety guide describes why late work cannot reclaim ownership simply because it eventually completed. Apply that principle to the candidate that a confirmation reply is allowed to approve.
For example, a synthetic conversation can begin with a supported amount and then supply a different unit while synthesis is pending. The expected outcome is not a second formatting success; it is invalidation of approval for the old material candidate. Only the new current value may be presented for approval. An arithmetic helper cannot implement this temporal rule by returning a more precise decimal, so keep its responsibility separate from the controller's.
An acknowledgment with a correction needs the same care. The yes-but repair guide treats a reply that both acknowledges and changes the value as a repair. If a caller says that the whole amount is right but the unit is wrong, accepting the first acknowledgment fragment would authorize an amount the full reply did not approve. Test the completed reply and the selected input ownership rather than matching one reassuring token.
The reference's result contains no implicit authorization field. That omission is deliberate: a return from capture_decimal proves only that its supported representation and policy produced a value. A consuming application should make the next required state visible. It might need clarification, a repeat-back, a current-value approval, a range check, or a documented adapter capability. Those states should never be inferred from a nonempty canonical string.
Keep arithmetic policy changes versioned when they affect a persisted candidate. The same source string can yield a different canonical amount under another increment or tie rule. Retaining only the final decimal prevents an engineer from explaining that difference. Retaining the selected policy identity and its immutable material settings permits a replay of the conversion without claiming that old caller approval covers a newly selected rule.
The fixtures also establish useful limits for review. Their bounded grammar excludes exponent notation, so a very short input cannot request an arbitrarily large decimal exponent. Their input length bounds the integer magnitude before conversion. Their scale bounds the minor-unit expansion. The local precision is chosen to exceed these particular bounds, and the ambient-context fixture checks that ordinary surrounding configuration cannot silently shrink it. Change those limits together if the supported contract grows.
Reviewers should be able to identify the deliberate rounding operation without searching through display code. In this artifact it occurs only in integer quotient-and-remainder logic after exact decimal conversion. The output records whether it changed the minor-unit value. The printed synthetic report then exposes the original source, canonical result, unit, increment, and tie rule, while explicitly leaving caller intent and transactions untested.
This division gives a practical first-value path. An engineer can copy the full published fence, run its actual assertions, and compare the small contract with a real adapter's requirements. A conversational evaluator can then test ambiguity and confirmation without having to infer whether a decimal discrepancy came from a float conversion, a locale guess, a cash increment, or a stale candidate. Each failure has a defined boundary and a concrete next investigation.
Frequently asked questions
How do I distinguish an amount from an identifier?
Use the requested field and explicit workflow context. An identifier preserves literal characters; an amount participates in arithmetic under a unit or currency. A sequence of digits alone cannot decide between them. If the current question leaves that distinction unclear, clarify before choosing a numeric parser.
What can one twenty mean?
Its interpretation depends on the task, language, and caller's intended grouping. This article does not assign a universal reading. Ask a targeted question about the whole amount, fractional portion, or requested field, then preserve the resolved candidate and context rather than treating a familiar interpretation as confirmed.
Why is a transcript insufficient for a monetary value?
A transcript records recognized words, not necessarily the intended currency, scale, or business action. Even accurate words can remain ambiguous. The authoritative candidate needs the resolved amount and explicit currency, followed by current-value confirmation under the workflow's actual requirements.
Should dollars imply USD?
Only if explicit authorized context establishes that currency for the current workflow. The word alone may leave a material ambiguity. Caller language, telephone context, and application defaults should not silently choose a currency when the caller has not been given that contract.
Why construct Decimal from a string?
The approved string directly expresses the decimal value being captured. A float may already contain a binary approximation before Decimal receives it. Avoiding that intermediate conversion preserves the chosen decimal spelling's value and makes the representation boundary easier to test.
What goes wrong with float-to-Decimal conversion?
It can preserve an approximation introduced earlier rather than the decimal value the caller intended. Converting that float back to a short string is not a universal repair for unknown provenance. Keep the original approved decimal text and test the actual producer instead of trying to reconstruct it later.
Does Decimal eliminate all rounding?
No. Arithmetic operations obey a context, and a business policy may deliberately round. Exact construction and exact bounded conversion are separate from increment rounding. The reference sets its own local precision and traps conversion signals, then performs one explicitly named synthetic rounding rule.
Who chooses a rounding policy?
The consuming business and adapter contract must define the applicable rule. This article's nearest-increment, ties-away-from-zero rule is a synthetic teaching choice. It is not a universal accounting, tax, currency, or settlement recommendation, and the example executes no real transaction.
How do currency scales vary?
Do not hard-code a universal two-place assumption. Use the actual consuming specification and verified policy data for supported currencies and operations. The fixture includes different selected scales to test the mechanism; those cases do not constitute a complete currency registry or current settlement-rule catalog.
Should quantities and money share one parser?
They can share a bounded decimal representation helper while retaining different kinds, units, ranges, and operation policies. The reference makes kind explicit. Sharing a helper must not cause a weight to inherit a cash increment or a monetary amount to lose its currency identity.
How do I represent units?
Carry an explicit stable unit identifier alongside the canonical value and original surface. Define supported units and conversions separately. The reference validates a bounded label only; it does not certify arbitrary labels against a measurement standard or perform any conversion between units.
What should negative amounts do?
Follow the explicit operation policy. The reference rejects negative nonzero values unless allowed, and it tests that branch separately from signed zero. Do not remove a minus sign to make an input pass, and do not assume that every workflow supporting refunds should accept every negative quantity.
When is zero a valid value?
That is a field-specific decision. The reference permits numeric zero and canonicalizes its presentation to the selected scale. A production workflow may require a positive amount or a separate no-charge state; enforce that consuming rule explicitly rather than confusing zero with a missing value.
Should NaN and Infinity be rejected?
They are outside this finite amount contract. The grammar rejects their spellings, and conversion retains a defensive finite-value check. A successfully constructed numeric object does not automatically represent an acceptable business amount. Keep that invariant if the supported grammar changes.
Can separators be removed automatically?
Not under this reference. A comma or period can have different roles in different number conventions. Deleting punctuation before resolving the convention can change the value. Use a separately declared locale interpretation and preserve the original surface before producing approved decimal text.
How do I preserve an exact repeat-back?
Build it from the current canonical candidate with explicit currency or unit, while retaining the original surface for repair. Test the actual speech renderer separately. A numeric formatting assertion cannot establish that synthesized audio was intelligible or that the caller approved the intended amount.
What happens after a caller changes currency?
The interpretation changes even if the digits remain identical. Create a new candidate and invalidate prior approval. Do not treat the new currency as display decoration or reuse an old transaction request identity with a changed payload. The surrounding confirmation controller owns that state transition.
Does the example parse arbitrary spoken numbers?
No. It accepts an already resolved ASCII decimal string under a bounded grammar. Number-word interpretation, locale parsing, and speech recognition are separate contracts. Their outputs must establish the intended field kind and unit before entering this arithmetic reference.
What is a safe synthetic fixture?
Use invented arithmetic inputs with deterministic expectations, explicit units, and a clearly labeled policy. Do not attach real account data or claim the vector came from a customer call. The published output labels itself Synthetic and states that caller intent and transactions were not tested.
What confirmation is required before committing a value?
The workflow must approve the current material candidate, including amount, currency or unit, and any rounding change. This reference returns no approval token and performs no side effect. A separate controller must enforce its age, revision, ownership, and downstream operation requirements.
Ship an explicit amount contract
Treat capture as a sequence of evidence-bearing decisions: resolve the field kind, obtain its context, preserve supported decimal text, apply an explicit policy, and confirm the current proposed value. Each step should expose uncertainty rather than hide it behind a plausible decimal.
For 60db Agent integrations, the useful first result is a reproducible boundary an engineer can verify. Run the complete synthetic fixture, compare the consuming adapter's real contract, and add separate interpretation and audio evidence before claiming an end-to-end outcome. Exact arithmetic protects a resolved value; the conversation still has to resolve the right value.
