dee
Arbitrary precision decimal arithmetic for Gleam. A pure Gleam implementation of Elixir’s Decimal library that works identically on both Erlang and JavaScript targets.
import dee/decimal
pub fn main() {
// Floating point: 0.1 + 0.2 = 0.30000000000000004
// Decimal: 0.1 + 0.2 = 0.3
let assert Ok(a) = decimal.from_string("0.1")
let assert Ok(b) = decimal.from_string("0.2")
let sum = decimal.add(a, b)
decimal.to_string(sum)
// -> "0.3"
}
Installation
gleam add dee
Features
- Elixir Decimal compatible: API and behavior aligned with Elixir’s Decimal as the reference implementation
- Arbitrary precision: No overflow or precision loss for any size numbers
- Cross-platform: Works identically on Erlang and JavaScript targets
- Pure Gleam: Single codebase, no FFI, no Elixir dependency
Quick Start
import dee/decimal.{type Decimal}
pub fn calculate_tax(price: Decimal, rate: Decimal) -> Decimal {
decimal.multiply(price, rate)
|> decimal.round(2, decimal.HalfUp)
}
pub fn main() {
let assert Ok(price) = decimal.from_string("19.99")
let assert Ok(tax_rate) = decimal.from_string("0.0825")
let tax = calculate_tax(price, tax_rate)
decimal.to_string(tax)
// -> "1.65"
}
Elixir Decimal Compatibility
This library uses Elixir’s Decimal as its reference implementation. The API, arithmetic behavior, rounding modes, and special value handling are designed to match. Code ported between the two libraries should produce identical results.
Why Pure Gleam Instead of FFI?
Using FFI to Elixir’s Decimal would only work on the Erlang target. A pure Gleam implementation gives us a single codebase that works identically on both Erlang and JavaScript targets, with no Elixir dependency.
Arbitrary precision is achieved via the bigi library, which uses native BEAM integers on Erlang and native BigInt on JavaScript.
Interoperability
Both libraries use the same mathematical representation (sign * coefficient * 10^exponent) with the same semantics for NaN, Infinity, and signed zero. The default context matches Elixir’s: 28 digits of precision with HalfUp rounding.
While the runtime representations differ (Gleam opaque type vs Elixir struct), converting between them on the BEAM is straightforward via to_string/from_string or by mapping component fields through from_parts, sign, and exponent.
Development
gleam test --target erlang # Run tests on Erlang
gleam test --target javascript # Run tests on JavaScript
gleam docs build # Build documentation
Further Documentation
Full API documentation is available at https://hexdocs.pm/dee.