Module
LinkDecimal preserves exact finite decimal values with canonical scale. Parse complete decimal text, compare exact quantities, convert integers explicitly, and choose a result grid with round or div. RoundingMode supplies six distinct rounding policies.
Why
LinkDecimal arithmetic makes precision, scale and rounding explicit for exact decimal quantities. Numeric values stay independent of display padding, and different rounding intentions retain distinct modes.
Availability
LinkImport std.decimal in language mode 5.21. The current compiler selects std/decimal.tpz for this language mode. Availability in other released editions and inclusion in each installed distribution are Pending.
Exported types
Linkexport type Decimal = Decimal
export type Rounding
Type members keep the compiler-owned type name shown in their declarations. Importing a module does not create a new type identity.
Public API
LinkPending contracts
LinkPreserve exact decimal values and canonical scale
LinkPreserve exact decimal values and canonical scale
A Decimal represents a signed integer coefficient multiplied by ten raised to the negative scale. The coefficient has arbitrary precision. The scale is an integer from 0 through 4294967295 inclusive.
Decimal has no fixed significant digit limit or ambient precision context. Increasing the coefficient beyond the int range does not cause numeric overflow. Decimal values are finite and have no infinity or NaN.
Every Decimal is canonical. Remove trailing coefficient zeros while its scale is positive. Zero has coefficient zero and scale zero. A negative zero spelling produces the same value as zero.
Decimal.scale returns the canonical scale as int. It counts fractional decimal places after normalization. It does not retain input padding or a requested display precision.
Decimal.toString returns exact ASCII decimal text without an exponent or locale rules. A negative nonzero value starts with a minus sign. The integer part has at least one digit and no redundant leading zeros.
A positive scale produces a decimal point followed by exactly that many fractional digits. There are no trailing fractional zeros. Scale zero omits the decimal point, and zero is written as 0.
Exact addition and subtraction align the operand scales before combining their coefficients. Exact multiplication multiplies their coefficients and adds their scales without rounding.
Normalize the exact product before enforcing the scale limit. Raise TPZ4004 when its canonical scale exceeds 4294967295. An intermediate scale sum must not reject a product whose canonical scale fits.
Parsing the output of Decimal.toString recovers the same Decimal. A request for more fractional places through round or div does not append stored zeros.
Decimal values are immutable. Receiver and positional argument expressions evaluate once in source order. An earlier fault prevents later evaluation. The decimal operations themselves do not perform external effects.
Why
Exact coefficients preserve decimal quantities without a hidden precision setting. Canonical scale makes equality and text exchange independent of input padding. Display padding has a separate formatting intent and is not stored as numeric value state. Scale bounds apply to the value that the operation returns. A temporary representation has no distinct numeric intent and must not make an otherwise representable product fail.
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 3 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 71 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 194 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 184 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 212 - compiler/crates/topaz_value/src/value/operators.rs at
693a4cb69c39· 227 -
topaz.stdlib.contract.decimal-valuesatworking-tree - topaz.decision.decimal-values.1 at
topaz.decisi
Normalize signs and redundant decimal zeros
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}for text in (["0", "-0.000", "+00012.3400", ".0050", "100.000"]) {let value: Decimal = read(text)io.writeLine("{value.toString()} {value.scale()}")let reparsed: Option<Decimal> = Decimal.parse(value.toString())let recovered: Decimal = test.assertSome(reparsed)test.assertEq(recovered, value)}
0 0 0 0 12.34 2 0.005 3 100 0
Expected result
Expected result
Execution completes. Exit code 0.
0 00 012.34 20.005 3100 0
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 3 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 71 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 194 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 184 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 212 - compiler/crates/topaz_value/src/value/operators.rs at
693a4cb69c39· 227 -
topaz.stdlib.contract.decimal-valuesatworking-tree - topaz.decision.decimal-values.1 at
topaz.decisi
Keep every digit beyond machine integer precision
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}let left: Decimal = read("1000000000000000000000000000000.25")let right: Decimal = read("0.75")let sum: Decimal = left + rightlet difference: Decimal = left - rightlet product: Decimal = left * rightio.writeLine(sum.toString())io.writeLine(difference.toString())io.writeLine(product.toString())io.writeLine(left.toString())
1000000000000000000000000000001 999999999999999999999999999999.5 750000000000000000000000000000.1875 1000000000000000000000000000000.25
Expected result
Expected result
Execution completes. Exit code 0.
1000000000000000000000000000001999999999999999999999999999999.5750000000000000000000000000000.18751000000000000000000000000000000.25
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 3 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 71 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 194 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 184 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 212 - compiler/crates/topaz_value/src/value/operators.rs at
693a4cb69c39· 227 -
topaz.stdlib.contract.decimal-valuesatworking-tree - topaz.decision.decimal-values.1 at
topaz.decisi
Normalize a product before checking its final scale
Linkimport std.decimal { Decimal }import std.testimport std.iolet parsedPlace: Option<Decimal> = Decimal.parse("0.1")let mut place: Decimal = test.assertSome(parsedPlace)let mut smallest: Decimal = (1).toDecimal()let mut step: int = 0while (step < 32) {smallest = smallest * placeif (step < 31) {place = place * place}step = step + 1}let doubled: Decimal = smallest * (2).toDecimal()let parsedHalf: Option<Decimal> = Decimal.parse("0.5")let half: Decimal = test.assertSome(parsedHalf)let recovered: Decimal = doubled * halflet equal = recovered == smallestio.writeLine("{recovered.scale()} {equal}")
4294967295 true
Expected result
Expected result
Execution completes. Exit code 0.
4294967295 true
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 3 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 71 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 194 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 184 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 212 - compiler/crates/topaz_value/src/value/operators.rs at
693a4cb69c39· 227 -
topaz.stdlib.contract.decimal-valuesatworking-tree - topaz.decision.decimal-values.1 at
topaz.decisi
Parse complete finite decimal text
LinkParse complete finite decimal text
Decimal.parse reads the complete input string. Text starts with an optional ASCII plus or minus sign. The remaining text contains ASCII digits and at most one decimal point.
Without a decimal point, at least one digit is required. With a decimal point, at least one digit must follow it and the integer digits may be absent. The strings .5 and -.5 are accepted. The strings 1. and . are rejected.
Leading integer zeros and trailing fractional zeros are accepted. Their exact numeric value is normalized by the decimal value rule. An accepted sign on zero does not survive normalization.
The input may contain at most 4294967295 fractional digits before normalization. A larger fractional digit count returns None(). The integer digit count has no fixed numeric precision limit.
Whitespace, non-ASCII digits, separators, radix prefixes, exponents, NaN and infinity spellings are rejected. Parsing does not trim, remove separators, stop at a valid prefix, or convert through a binary float.
Valid text returns Some containing the exact Decimal. Every malformed spelling returns None() without a numeric conversion fault or replacement value.
This grammar describes text data passed to Decimal.parse. Numeric literal spelling follows the language literal rules.
Why
A complete decimal data grammar makes accepted input predictable and preserves every digit. Optional signs and a missing integer zero are accepted input conventions. Canonical output still gives each value one serialized spelling.
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 24 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 175 -
topaz.stdlib.contract.decimal-parseatworking-tree - topaz.decision.decimal-parse.1 at
topaz.decisi
Accept explicit signs and fractional decimal text
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}for text in (["7", "+7", "-7", "0007", ".5", "-.5", "+.5", "01.0200"]) {let value: Decimal = read(text)io.writeLine(value.toString())}
7 7 -7 7 0.5 -0.5 0.5 1.02
Expected result
Expected result
Execution completes. Exit code 0.
77-770.5-0.50.51.02
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 24 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 175 -
topaz.stdlib.contract.decimal-parseatworking-tree - topaz.decision.decimal-parse.1 at
topaz.decisi
Reject incomplete and nondecimal spellings
Linkimport std.decimal { Decimal }import std.iofor text in (["", "+", "-", ".", "+.", "-.", "1.", "1.2.3", "1e2", "1E+2", "0x10", "1_000", "1,2", "NaN", "Infinity", " 1", "1 ", "1.2", "١.٢", "1\n2", "\t1"]) {match (Decimal.parse(text)) {case None() => io.writeLine("rejected")case Some(_) => io.writeLine("accepted")}}
rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected
Expected result
Expected result
Execution completes. Exit code 0.
rejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejected
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 24 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 175 -
topaz.stdlib.contract.decimal-parseatworking-tree - topaz.decision.decimal-parse.1 at
topaz.decisi
Choose a decimal grid and a rounding direction
LinkChoose a decimal grid and a rounding direction
For a requested scale s, rounding selects a multiple of ten raised to negative s. A value already on that grid is unchanged in every rounding mode.
The requested scale must be an int from 0 through 4294967295 inclusive. A negative value or a value above that range raises TPZ5001 during execution. Scale is never clamped or interpreted as digits to the left of the decimal point.
When the requested scale is at least the canonical scale, round returns the same value. Otherwise it chooses a grid value according to the supplied mode and normalizes the result.
RoundingMode.Down() chooses the greatest grid value no greater than the input. It rounds toward negative infinity, including at a midpoint.
RoundingMode.Up() chooses the least grid value no smaller than the input. It rounds toward positive infinity, including at a midpoint.
RoundingMode.TowardZero() chooses the grid value with smaller absolute magnitude when the input is between two grid values. RoundingMode.AwayFromZero() chooses the one with larger absolute magnitude. These directions also apply at midpoints.
RoundingMode.HalfUp() chooses the nearer grid value. At an exact midpoint it chooses the value with greater absolute magnitude for either sign.
RoundingMode.HalfEven() chooses the nearer grid value. At an exact midpoint it chooses the value whose integer coefficient on the requested grid is even. Determine parity before canonical trailing zeros are removed.
Omitting the mode selects RoundingMode.HalfEven(). The default is independent of host settings and applies equally to positive and negative values.
A carry may reduce the canonical scale. A rounded zero has scale zero and no negative sign. Rounding does not pad the result to the requested scale.
Each RoundingMode spelling constructs a distinct zero payload enum variant and uses parentheses. The modes are immutable values that callers may store and pass to both round and div.
Why
A decimal grid states which precision the caller needs. Nearest rounding, an upper or lower bound, and movement toward or away from zero express different numeric intentions. Their results can agree for some inputs without making their meanings interchangeable. HalfEven gives exact ties a stable rule without consistently favoring one magnitude direction.
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 118 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 147 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 108 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 204 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-roundatworking-tree - topaz.decision.decimal-round.1 at
topaz.decisi
Resolve positive and negative ties in every rounding mode
Linkimport std.decimal { Decimal, RoundingMode }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}function rounded(text: string, mode: RoundingMode) -> string {let value: Decimal = read(text)return value.round(0, mode).toString()}let modes: Array<RoundingMode> = [RoundingMode.Down(), RoundingMode.Up(), RoundingMode.TowardZero(), RoundingMode.AwayFromZero(), RoundingMode.HalfUp(), RoundingMode.HalfEven()]for mode in (modes) {let a = rounded("2.5", mode)let b = rounded("3.5", mode)let c = rounded("-2.5", mode)let d = rounded("-3.5", mode)io.writeLine("{a} {b} {c} {d}")}
2 3 -3 -4 3 4 -2 -3 2 3 -2 -3 3 4 -3 -4 3 4 -3 -4 2 4 -2 -4
Expected result
Expected result
Execution completes. Exit code 0.
2 3 -3 -43 4 -2 -32 3 -2 -33 4 -3 -43 4 -3 -42 4 -2 -4
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 118 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 147 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 108 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 204 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-roundatworking-tree - topaz.decision.decimal-round.1 at
topaz.decisi
Distinguish directed rounding from nearest rounding
Linkimport std.decimal { Decimal, RoundingMode }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}function rounded(text: string, mode: RoundingMode) -> string {let value: Decimal = read(text)return value.round(0, mode).toString()}let modes: Array<RoundingMode> = [RoundingMode.Down(), RoundingMode.Up(), RoundingMode.TowardZero(), RoundingMode.AwayFromZero(), RoundingMode.HalfUp(), RoundingMode.HalfEven()]for mode in (modes) {let a = rounded("2.1", mode)let b = rounded("2.9", mode)let c = rounded("-2.1", mode)let d = rounded("-2.9", mode)io.writeLine("{a} {b} {c} {d}")}
2 2 -3 -3 3 3 -2 -2 2 2 -2 -2 3 3 -3 -3 2 3 -2 -3 2 3 -2 -3
Expected result
Expected result
Execution completes. Exit code 0.
2 2 -3 -33 3 -2 -22 2 -2 -23 3 -3 -32 3 -2 -32 3 -2 -3
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 118 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 147 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 108 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 204 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-roundatworking-tree - topaz.decision.decimal-round.1 at
topaz.decisi
Apply the default mode and normalize a carry or zero
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}let value: Decimal = read("1.245")io.writeLine(value.round(2).toString())io.writeLine(read("1.255").round(2).toString())io.writeLine(value.round(5).toString())let carry: Decimal = read("9.995").round(2)let zero: Decimal = read("-0.004").round(2)io.writeLine("{carry.toString()} {carry.scale()}")io.writeLine("{zero.toString()} {zero.scale()}")io.writeLine(value.toString())
1.24 1.26 1.245 10 0 0 0 1.245
Expected result
Expected result
Execution completes. Exit code 0.
1.241.261.24510 00 01.245
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 118 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 147 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 108 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 204 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-roundatworking-tree - topaz.decision.decimal-round.1 at
topaz.decisi
Accept the largest scale without adding stored zeros
Linkimport std.decimal { Decimal }import std.testimport std.iolet one: Decimal = (1).toDecimal()let zero: Decimal = (0).toDecimal()let same: Decimal = one.round(4294967295)let sameZero: Decimal = zero.round(4294967295)io.writeLine("{same.toString()} {same.scale()}")io.writeLine("{sameZero.toString()} {sameZero.scale()}")
1 0 0 0
Expected result
Expected result
Execution completes. Exit code 0.
1 00 0
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 118 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 147 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 108 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 204 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-roundatworking-tree - topaz.decision.decimal-round.1 at
topaz.decisi
Reject a rounding scale outside the valid range
Linkimport std.decimal { Decimal }import std.iolet value: Decimal = (1).toDecimal()value.round(-1)io.writeLine("after")
No stdout.
Expected result
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 118 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 147 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 108 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 204 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-roundatworking-tree - topaz.decision.decimal-round.1 at
topaz.decisi
Reject a rounding scale outside the valid range
Linkimport std.decimal { Decimal }import std.iolet value: Decimal = (1).toDecimal()value.round(4294967296)io.writeLine("after")
No stdout.
Expected result
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 118 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 147 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 108 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 204 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-roundatworking-tree - topaz.decision.decimal-round.1 at
topaz.decisi
Diagnostics
LinkDivide with an explicit result scale
LinkDivide with an explicit result scale
Decimal.div evaluates the exact quotient of its receiver and the divisor. For a nonzero divisor it rounds that quotient once to the requested decimal grid and returns Ok containing the normalized Decimal.
The requested scale and mode have the same meaning as in Decimal.round. Omitting the mode selects RoundingMode.HalfEven(). Input scales and coefficient sizes do not introduce another precision limit or an intermediate rounding step.
A nonterminating decimal quotient is rounded to the requested grid. A terminating quotient that is already on the grid remains exact. The canonical result scale can be smaller than the requested scale.
Division signs follow ordinary numeric division. All six rounding modes apply to the signed exact quotient, including negative ties and results that round to zero.
A zero divisor returns Err with the string Decimal.. This includes zero divided by zero and a divisor parsed from a negative zero spelling. This failure is a Result value and does not raise TPZ4002.
A zero dividend and a nonzero divisor return Ok containing zero for every valid requested scale. An internal sum of operand and requested scales must not turn a nonzero divisor into a division by zero error.
An invalid requested scale raises TPZ5001 during execution before the zero divisor is classified. The operation never replaces an invalid scale with zero or reports that scale error as a successful Result.
The receiver, divisor, scale and explicitly supplied mode expressions evaluate once in that order before their values are validated. The operation preserves both operands.
Why
A division result needs an explicit decimal grid because a finite pair of decimals can have a nonterminating quotient. Rounding the exact quotient once avoids hidden precision loss. A recoverable zero divisor stays distinct from invalid configuration and from internal scale arithmetic.
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 167 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 217 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-divisionatworking-tree - topaz.decision.decimal-division.1 at
topaz.decisi
Distinguish exact and repeating quotients at the requested grid
Linkimport std.decimal { Decimal }import std.testimport std.iofunction show(result: Result<Decimal, string>) -> () {let value: Decimal = test.assertOk(result)io.writeLine("{value.toString()} {value.scale()}")}let one: Decimal = (1).toDecimal()let third: Result<Decimal, string> = one.div((3).toDecimal(), 6)let eighth: Result<Decimal, string> = one.div((8).toDecimal(), 2)let negativeTie: Result<Decimal, string> = (-1).toDecimal().div((8).toDecimal(), 2)let exact: Result<Decimal, string> = (100).toDecimal().div((4).toDecimal(), 4)let integerGrid: Result<Decimal, string> = (2).toDecimal().div((3).toDecimal(), 0)show(third)show(eighth)show(negativeTie)show(exact)show(integerGrid)
0.333333 6 0.12 2 -0.12 2 25 0 1 0
Expected result
Expected result
Execution completes. Exit code 0.
0.333333 60.12 2-0.12 225 01 0
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 167 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 217 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-divisionatworking-tree - topaz.decision.decimal-division.1 at
topaz.decisi
Apply every rounding mode to signed quotient ties
Linkimport std.decimal { Decimal, RoundingMode }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}function quotient(text: string, mode: RoundingMode) -> string {let value: Decimal = read(text)let result: Result<Decimal, string> = value.div((2).toDecimal(), 0, mode)let rounded: Decimal = test.assertOk(result)return rounded.toString()}let modes: Array<RoundingMode> = [RoundingMode.Down(), RoundingMode.Up(), RoundingMode.TowardZero(), RoundingMode.AwayFromZero(), RoundingMode.HalfUp(), RoundingMode.HalfEven()]for mode in (modes) {let a = quotient("5", mode)let b = quotient("7", mode)let c = quotient("-5", mode)let d = quotient("-7", mode)io.writeLine("{a} {b} {c} {d}")}
2 3 -3 -4 3 4 -2 -3 2 3 -2 -3 3 4 -3 -4 3 4 -3 -4 2 4 -2 -4
Expected result
Expected result
Execution completes. Exit code 0.
2 3 -3 -43 4 -2 -32 3 -2 -33 4 -3 -43 4 -3 -42 4 -2 -4
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 167 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 217 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-divisionatworking-tree - topaz.decision.decimal-division.1 at
topaz.decisi
Return a recoverable error for every zero divisor
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}let divisor: Decimal = read("-0.000")let numerators: Array<Decimal> = [(0).toDecimal(), (1).toDecimal()]for numerator in (numerators) {let result: Result<Decimal, string> = numerator.div(divisor, 2)match (result) {case Err(message) => io.writeLine(message)case Ok(_) => io.writeLine("accepted")}}io.writeLine(divisor.toString())
Decimal.div: division by zero Decimal.div: division by zero 0
Expected result
Expected result
Execution completes. Exit code 0.
Decimal.div: division by zeroDecimal.div: division by zero0
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 167 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 217 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-divisionatworking-tree - topaz.decision.decimal-division.1 at
topaz.decisi
Keep a zero quotient at the largest valid scale
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}let divisor: Decimal = read("0.1")let result: Result<Decimal, string> = (0).toDecimal().div(divisor, 4294967295)match (result) {case Ok(value) => io.writeLine(value.toString())case Err(_) => io.writeLine("unexpected error")}
0
Expected result
Expected result
Execution completes. Exit code 0.
0
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 167 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 217 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-divisionatworking-tree - topaz.decision.decimal-division.1 at
topaz.decisi
Evaluate the receiver and division arguments once in order
Linkimport std.decimal { Decimal, RoundingMode }import std.testimport std.iofunction receiver() -> Decimal {io.writeLine("receiver")return (6).toDecimal()}function divisor() -> Decimal {io.writeLine("divisor")return (4).toDecimal()}function scale() -> int {io.writeLine("scale")return 1}function mode() -> RoundingMode {io.writeLine("mode")return RoundingMode.HalfEven()}let result: Result<Decimal, string> = receiver().div(divisor(), scale(), mode())let value: Decimal = test.assertOk(result)io.writeLine(value.toString())
receiver divisor scale mode 1.5
Expected result
Expected result
Execution completes. Exit code 0.
receiverdivisorscalemode1.5
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 167 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 217 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-divisionatworking-tree - topaz.decision.decimal-division.1 at
topaz.decisi
Reject an invalid scale before classifying a zero divisor
Linkimport std.decimal { Decimal }import std.iolet value: Decimal = (1).toDecimal()value.div((0).toDecimal(), -1)io.writeLine("after")
No stdout.
Expected result
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 167 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 217 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-divisionatworking-tree - topaz.decision.decimal-division.1 at
topaz.decisi
Reject an invalid scale before classifying a zero divisor
Linkimport std.decimal { Decimal }import std.iolet value: Decimal = (1).toDecimal()value.div((0).toDecimal(), 4294967296)io.writeLine("after")
No stdout.
Expected result
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 167 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 217 - compiler/crates/topaz_self_frontend/topaz/checker/members_capabilities.tpz at
b48527d0a174· 1916 - compiler/crates/topaz_self_frontend/topaz/checker/call_complete.tpz at
b48527d0a174· 1319 -
topaz.stdlib.contract.decimal-divisionatworking-tree - topaz.decision.decimal-division.1 at
topaz.decisi
Convert numeric values explicitly and without truncation
LinkConvert numeric values explicitly and without truncation
int.toDecimal converts every int value exactly and produces scale zero. Both signed 64 bit endpoints are representable. The conversion does not pass through float.
Decimal.toInt returns Some containing the same integer when the Decimal is integral and fits the integer conversion bounds.
A nonintegral Decimal or an integer outside the int range returns None(). Conversion does not round, truncate, wrap, saturate, or raise an arithmetic overflow fault. The original Decimal remains unchanged.
A value parsed from 1.000 is integral after normalization and converts to Some containing 1. A value parsed from 1.001 is nonintegral and returns None().
Existing int, float and BigInt values do not implicitly become Decimal values in arithmetic or comparisons. The module provides no direct float or BigInt conversion member.
For an exact BigInt conversion, format it with radix 10 and pass that text to Decimal.parse. To request an integral BigInt from a Decimal, pass its canonical text to BigInt.parse with radix 10. Fractional decimal text is rejected by that integer parser.
Converting through explicitly chosen text follows the corresponding text parser. Decimal.parse never recovers decimal digits that were lost in an earlier binary float calculation.
Why
Exact integer conversion and checked narrowing communicate different intentions. Option exposes fractions and overflow without selecting a rounding policy for the caller. Explicit text composition handles arbitrary precision integer exchange through the existing parsers without adding another conversion API.
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 103 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 170 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 196 -
topaz.stdlib.contract.decimal-conversionatworking-tree - topaz.decision.decimal-conversion.1 at
topaz.decisi
Convert both int endpoints and reject fractions or overflow
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}for text in (["-9223372036854775808", "9223372036854775807", "-9223372036854775809", "9223372036854775808", "1.000", "1.001", "-1.001", "-0.000"]) {let value: Decimal = read(text)match (value.toInt()) {case Some(integer) => io.writeLine("{integer}")case None() => io.writeLine("not an int")}}let large: Decimal = read("1000000000000000000000000000000")let narrowed: Option<int> = large.toInt()test.assertNone(narrowed)io.writeLine(large.toString())
-9223372036854775808 9223372036854775807 not an int not an int 1 not an int not an int 0 1000000000000000000000000000000
Expected result
Expected result
Execution completes. Exit code 0.
-92233720368547758089223372036854775807not an intnot an int1not an intnot an int01000000000000000000000000000000
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 103 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 170 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 196 -
topaz.stdlib.contract.decimal-conversionatworking-tree - topaz.decision.decimal-conversion.1 at
topaz.decisi
Convert int values to exact scale zero decimals
Linkimport std.decimal { Decimal }import std.testimport std.iolet integers: Array<int> = [-9223372036854775807 - 1, -1, 0, 1, 9223372036854775807]for integer in (integers) {let value: Decimal = integer.toDecimal()let narrowed: Option<int> = value.toInt()let recovered: int = test.assertSome(narrowed)test.assertEq(recovered, integer)io.writeLine("{value.toString()} {value.scale()}")}
-9223372036854775808 0 -1 0 0 0 1 0 9223372036854775807 0
Expected result
Expected result
Execution completes. Exit code 0.
-9223372036854775808 0-1 00 01 09223372036854775807 0
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 103 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 170 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 196 -
topaz.stdlib.contract.decimal-conversionatworking-tree - topaz.decision.decimal-conversion.1 at
topaz.decisi
Exchange arbitrary precision integers through explicit decimal text
Linkimport std.decimal { Decimal }import std.bigint { BigInt }import std.testimport std.iolet parsedInteger: Option<BigInt> = BigInt.parse("123456789012345678901234567890", 10)let integer: BigInt = test.assertSome(parsedInteger)let parsedDecimal: Option<Decimal> = Decimal.parse(integer.toString(10))let value: Decimal = test.assertSome(parsedDecimal)io.writeLine(value.toString())let parsedAgain: Option<BigInt> = BigInt.parse(value.toString(), 10)let recovered: BigInt = test.assertSome(parsedAgain)let equal = recovered == integerio.writeLine("{equal}")let parsedFraction: Option<Decimal> = Decimal.parse("1.5")let fraction: Decimal = test.assertSome(parsedFraction)let integral: Option<BigInt> = BigInt.parse(fraction.toString(), 10)match (integral) {case None() => io.writeLine("fraction rejected")case Some(_) => io.writeLine("converted")}
123456789012345678901234567890 true fraction rejected
Expected result
Expected result
Execution completes. Exit code 0.
123456789012345678901234567890truefraction rejected
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 103 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 170 - compiler/crates/topaz_value/src/value/builtins/bigint_decimal.rs at
693a4cb69c39· 196 -
topaz.stdlib.contract.decimal-conversionatworking-tree - topaz.decision.decimal-conversion.1 at
topaz.decisi
Compare exact decimal values independently of input scale
LinkCompare exact decimal values independently of input scale
Decimal equality compares exact numeric values. Values parsed from 1.2, 1.20 and 1.200 are equal. Redundant scale in an input spelling is not part of value identity.
The equality operators == and != return complementary results. Zero and every accepted signed or padded zero spelling compare equal.
The ordering operators <, <=, > and >= use exact numeric order. They align decimal places without first rounding either operand or converting either operand to float.
Every pair of Decimal values is comparable. Equality agrees with ordering, and exactly one of less than, equal to or greater than holds.
Comparison leaves both operands and their canonical scales unchanged. Call round explicitly when comparison after rounding is the intended operation.
Why
Numeric comparison answers whether quantities are equal or ordered. Retaining textual padding as a second value identity would mix formatting intent into arithmetic. Exact comparison also preserves distinctions beyond binary floating point precision.
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 3 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 194 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 222 -
topaz.stdlib.contract.decimal-comparisonatworking-tree - topaz.decision.decimal-comparison.1 at
topaz.decisi
Treat values with different input padding as equal
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}let left: Decimal = read("1.20")let right: Decimal = read("1.2000")let equal = left == rightlet unequal = left != rightlet less = left < rightlet lessOrEqual = left <= rightlet greater = left > rightlet greaterOrEqual = left >= rightio.writeLine("{equal} {unequal} {less} {lessOrEqual} {greater} {greaterOrEqual}")io.writeLine("{left.scale()} {right.scale()}")
true false false true false true 1 1
Expected result
Expected result
Execution completes. Exit code 0.
true false false true false true1 1
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 3 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 194 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 222 -
topaz.stdlib.contract.decimal-comparisonatworking-tree - topaz.decision.decimal-comparison.1 at
topaz.decisi
Order neighboring large values and signed zero exactly
Linkimport std.decimal { Decimal }import std.testimport std.iofunction read(text: string) -> Decimal {let parsed: Option<Decimal> = Decimal.parse(text)let value: Decimal = test.assertSome(parsed)return value}let lower: Decimal = read("9007199254740992")let higher: Decimal = read("9007199254740993")let increasing = lower < higherlet decreasing = higher > lowerlet distinct = lower != higherlet negativeOrder = -higher < -lowerio.writeLine("{increasing} {decreasing} {distinct} {negativeOrder}")let negativeZero: Decimal = read("-0.000")let zero: Decimal = (0).toDecimal()let sameZero = negativeZero == zeroio.writeLine("{sameZero}")
true true true true true
Expected result
Expected result
Execution completes. Exit code 0.
true true true truetrue
Source evidence
- compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 3 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 194 - compiler/crates/topaz_value/src/value/decimal.rs at
693a4cb69c39· 222 -
topaz.stdlib.contract.decimal-comparisonatworking-tree - topaz.decision.decimal-comparison.1 at
topaz.decisi