CodeSampleX

サンプル

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

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。 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

コントラクト

  1. Decimal context is scoped to the calling process dictionary and is not inherited by spawned child tasks
  2. assert context precision limits total significant digits rather than fractional decimal places
  3. assert Decimal.new constructor preserves full coefficient digits until arithmetic or apply_context is invoked
  4. assert Decimal.Context.with isolates temporary configuration changes and restores previous context
  5. 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

ソースアーティファクトをダウンロード (tar.gz)

ソース

NOTES.md
# 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}`.
csx.json
{"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"}
mix.exs
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
mix.lock
%{
  "decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"},
}
test/contract_test.exs
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
test/test_helper.exs
ExUnit.start()

オリジンシーダー

csx-seed