샘플
decimal 3.1.1: Configure Decimal arithmetic context, process isolation, precision limits, and signal traps in Elixir
검증된 샘플 — hex decimal 3.1.1: Configure Decimal arithmetic context, process isolation, precision limits, and signal traps in Elixir. elixir 1 · linux…
sha256:8dda07eeea1022e1f7a3cb9192a6c07cc54bf00dfe032e6dfeceb80a28c12c86
이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다.
통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다.
MIT-0
실행 증거
선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.
- 증거 기준
- 서명된 컨트랙트 통과
- 검증 영수증
- 2
- 빌드한 서명 키
- 2
선언된 환경
elixir linux x64 elixir elixir mix
검증 실행 환경
| 환경 | 컨트랙트 | 단계 | 실행일 |
|---|---|---|---|
| elixir 1 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:PASS · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · hex@1 |
2026-08-17 |
| elixir 1 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:PASS · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · hex@1 |
2026-08-18 |
케이스
HOW- 목표
- Configure Decimal arithmetic context, process isolation, precision limits, and signal traps in Elixir
- 패키지
- 심벌
-
- Decimal.Context.get
- Decimal.Context.set
- Decimal.Context.with
- Decimal.apply_context
- 환경
- elixir
- 생성일
- 2026-08-17T11:32:07Z
컨트랙트
- Decimal context is scoped to the calling process dictionary and is not inherited by spawned child tasks
- assert context precision limits total significant digits rather than fractional decimal places
- assert Decimal.new constructor preserves full coefficient digits until arithmetic or apply_context is invoked
- assert Decimal.Context.with isolates temporary configuration changes and restores previous context
- assert untrapped operations record inexact and rounded flags while trapped signals raise Decimal.Error
파일
- NOTES.md
- csx.json
- mix.exs
- mix.lock
- test/contract_test.exs
- test/test_helper.exs
소스
# Decimal Context Configuration, Process Scoping, and Precision Traps in Elixir
## What `search_known_solution` answered
`search_known_solution` returned existing sample `sha256:d7487beb8b4c961e46ea9378c44a47b0be35429cc897b1fa62a486db8059d276` ("Compare Decimal values in Elixir when == reports equal amounts as different", addressing struct comparison with `==` versus `Decimal.equal?/2`) and `sha256:a4920a6ff596a8407bc7add25eb98287d43846be1e22e63ee9c5c9fd588247fa` ("Parse decimal strings with 34 significant digits and leading zeros without exceeding default max_digits").
## Pinned Release
Pinned release: `decimal` `3.1.1` on Hex (`pkg:hex/decimal@3.1.1`).
## What a model would have written instead
1. Expected `Decimal.Context.set/1` to configure Decimal globally across the application or automatically propagate to spawned child processes like `Task.async`.
2. Configured `Decimal.Context.set(%{Decimal.Context.get() | precision: 2})` expecting results rounded to 2 fractional decimal places (scale / cents).
3. Assumed that parsing a string with `Decimal.new/1` immediately applies the context's `:precision` and truncates digits.
4. Assumed that division by recurring fractions or inexact operations always raises an exception by default.
## How the wrong version fails
- **Process isolation failure (silent)**: `Decimal.Context` is stored solely in the calling process's process dictionary (`Process.get(Decimal.Context)`). Any spawned child task (`Task.async`, `Task.start`, GenServer, or Phoenix channel) fails **silently** to inherit the context, reverting immediately to the default context (`precision: 34`, `rounding: :half_up`, `traps: [:invalid_operation, :division_by_zero]`).
- **Precision vs Decimal Places trap (silent corruption)**: In Decimal, `precision` controls the total number of **significant digits** (digits in the coefficient), not fractional decimal places. Setting `precision: 2` causes `Decimal.add(Decimal.new("100"), Decimal.new("23.45"))` to return `1.2E+2` (`120`), silently discarding `3.45`. Fractional decimal rounding requires `Decimal.round/2` under standard high precision.
- **Constructor precision retention**: `Decimal.new("123.456")` preserves all parsed digits without applying context precision; context precision is only enforced when an arithmetic operation (`Decimal.add`, `Decimal.mult`, `Decimal.div`) or `Decimal.apply_context/1` is executed.
- **Signal flags vs Traps**: Inexact operations like `Decimal.div(1, 3)` do not raise by default; they silently set `:inexact` and `:rounded` in `flags`. Adding `:inexact` or `:rounded` to `:traps` causes these operations to fail **loudly** at runtime with `%Decimal.Error{signal: :inexact}` or `%Decimal.Error{signal: :rounded}`.
{"buildCommand":["mix","deps.compile","--no-deps-check"],"case":{"believed":"Setting a Decimal context configuration applies across the application or automatically propagates to spawned child processes and tasks.","caseId":"case:sha256:d3e1f8440efdf99ae32cd286acf711a6c90f0181808329001a611c3bea065bc3","contract":["Decimal context is scoped to the calling process dictionary and is not inherited by spawned child tasks","assert context precision limits total significant digits rather than fractional decimal places","assert Decimal.new constructor preserves full coefficient digits until arithmetic or apply_context is invoked","assert Decimal.Context.with isolates temporary configuration changes and restores previous context","assert untrapped operations record inexact and rounded flags while trapped signals raise Decimal.Error"],"goal":"Configure Decimal arithmetic context, process isolation, precision limits, and signal traps in Elixir","kind":"HOW","packages":["pkg:hex/decimal@3.1.1"],"schemaVersion":1,"symbols":["Decimal.Context.get","Decimal.Context.set","Decimal.Context.with","Decimal.apply_context"]},"contractCommand":["mix","test","--no-deps-check"],"environment":{"arch":"x64","ecosystem":"hex","executionContext":"elixir","language":"elixir","os":"linux","packageManager":"mix","runtime":"elixir","schemaVersion":1},"license":"MIT-0","packages":["pkg:hex/decimal@3.1.1"],"schemaVersion":1,"symbols":["Decimal.Context.get","Decimal.Context.set","Decimal.Context.with","Decimal.apply_context"],"verifierAdapter":"hex@1"}
defmodule CsxDecimalConfig.MixProject do
use Mix.Project
def project do
[
app: :csx_decimal_config,
version: "0.1.0",
elixir: "~> 1.17",
deps: deps()
]
end
def application, do: [extra_applications: []]
defp deps do
[{:decimal, "3.1.1"}]
end
end
%{
"decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"},
}
defmodule CsxDecimalConfigTest do
use ExUnit.Case
# In Elixir's Decimal library, arithmetic behaviour is governed by %Decimal.Context{}.
# This contract verifies key configuration semantics, process-scoping boundaries,
# precision traps (significant digits vs decimal places), and exception trap handling.
test "Decimal context is scoped to the calling process dictionary and is not inherited by spawned child tasks" do
parent_ctx = %{Decimal.Context.get() | precision: 4, rounding: :floor}
Decimal.Context.set(parent_ctx)
assert Decimal.Context.get().precision == 4
assert Decimal.Context.get().rounding == :floor
# Child Task does NOT inherit the parent process's Decimal.Context dictionary
task =
Task.async(fn ->
child_ctx = Decimal.Context.get()
# Modifying context inside child process has no effect on parent
Decimal.Context.set(%{child_ctx | precision: 8})
child_ctx
end)
child_ctx = Task.await(task)
# Spawned child task received default Decimal context
assert child_ctx.precision == 34
assert child_ctx.rounding == :half_up
assert child_ctx.traps == [:invalid_operation, :division_by_zero]
# Parent process context remains unmodified at precision 4
assert Decimal.Context.get().precision == 4
# Reset parent process context
Decimal.Context.set(%Decimal.Context{})
end
test "context precision limits total significant digits rather than fractional decimal places" do
# Setting precision: 2 does NOT round to 2 decimal places; it limits total significant digits to 2.
Decimal.Context.with(%{Decimal.Context.get() | precision: 2}, fn ->
# 100 + 23.45 = 123.45, which rounds to 2 significant figures: 120 (1.2E+2)
sum = Decimal.add(Decimal.new("100"), Decimal.new("23.45"))
assert Decimal.to_string(sum) == "1.2E+2"
assert sum.coef == 12
assert sum.exp == 1
# Even Decimal.round(123.456, 2) is subjected to context precision and produces 2 significant digits
truncated = Decimal.round(Decimal.new("123.456"), 2)
assert Decimal.to_string(truncated) == "1.2E+2"
end)
# Fractional decimal places (scale) require standard/high precision with Decimal.round/2
exact_val = Decimal.new("123.456")
rounded_val = Decimal.round(exact_val, 2)
assert Decimal.to_string(rounded_val) == "123.46"
end
test "Decimal.new constructor preserves full coefficient digits until arithmetic or apply_context is invoked" do
Decimal.Context.with(%{Decimal.Context.get() | precision: 2}, fn ->
# Constructor preserves all parsed digits without truncating to context precision
raw = Decimal.new("123.456")
assert Decimal.to_string(raw) == "123.456"
assert raw.coef == 123_456
assert raw.exp == -3
# Arithmetic operation triggers context rounding to 2 significant digits
after_arithmetic = Decimal.add(raw, Decimal.new("0"))
assert Decimal.to_string(after_arithmetic) == "1.2E+2"
assert after_arithmetic.coef == 12
# Explicit apply_context also applies context precision
after_apply = Decimal.apply_context(raw)
assert Decimal.to_string(after_apply) == "1.2E+2"
assert after_apply.coef == 12
end)
end
test "Decimal.Context.with isolates temporary configuration changes and restores previous context" do
initial_ctx = Decimal.Context.get()
assert initial_ctx.precision == 34
# Run scoped execution with custom precision
scoped_result =
Decimal.Context.with(%{Decimal.Context.get() | precision: 5}, fn ->
assert Decimal.Context.get().precision == 5
Decimal.div(Decimal.new("1"), Decimal.new("7"))
end)
# Scoped result reflects 5 significant digits (0.14286)
assert Decimal.to_string(scoped_result) == "0.14286"
# Context is cleanly restored to precision 34
assert Decimal.Context.get().precision == 34
# Context is also restored when the scoped function raises
assert_raise RuntimeError, fn ->
Decimal.Context.with(%{Decimal.Context.get() | precision: 8}, fn ->
assert Decimal.Context.get().precision == 8
raise "unexpected failure"
end)
end
assert Decimal.Context.get().precision == 34
end
test "untrapped operations record inexact and rounded flags while trapped signals raise Decimal.Error" do
# Default context: :inexact and :rounded are NOT trapped; signals are recorded in flags
Decimal.Context.with(%Decimal.Context{}, fn ->
result = Decimal.div(Decimal.new("1"), Decimal.new("3"))
assert Decimal.to_string(result) == "0.3333333333333333333333333333333333"
current_ctx = Decimal.Context.get()
assert :inexact in current_ctx.flags
assert :rounded in current_ctx.flags
end)
# When :inexact is trapped, inexact division raises %Decimal.Error{signal: :inexact}
trapped_inexact_ctx = %{Decimal.Context.get() | traps: [:inexact | Decimal.Context.get().traps]}
Decimal.Context.with(trapped_inexact_ctx, fn ->
assert_raise Decimal.Error, fn ->
Decimal.div(Decimal.new("1"), Decimal.new("3"))
end
end)
# When :rounded is trapped under low precision, rounding raises %Decimal.Error{signal: :rounded}
trapped_rounded_ctx = %{
Decimal.Context.get()
| precision: 2,
traps: [:rounded | Decimal.Context.get().traps]
}
Decimal.Context.with(trapped_rounded_ctx, fn ->
assert_raise Decimal.Error, fn ->
Decimal.add(Decimal.new("100"), Decimal.new("23.45"))
end
end)
end
end
ExUnit.start()