# `FastDecimal`
[🔗](https://github.com/b-erdem/fastdecimal/blob/v1.0.1/lib/fastdecimal.ex#L1)

Fast arbitrary-precision decimal arithmetic for Elixir.

A decimal is represented as `coef * 10^exp` where `coef` is a BEAM integer
(sign carried inline) and `exp` is an integer exponent. Operations work on
raw integers in the hot path; values that exceed 60-bit immediate ints
promote to BEAM bignums automatically.

## Design: exact arithmetic, explicit precision

Unlike `Decimal`, FastDecimal does **not** maintain an implicit per-process
precision context. `add`, `sub`, and `mult` are mathematically exact — the
result coefficient grows to whatever size is needed. Only `div/3` takes a
precision argument, because division is the only operation that can produce
a non-terminating decimal.

If you want bounded precision after an arithmetic chain, call `round/2` or
`normalize/1` explicitly. Trading implicit context for explicit calls is
faster (no process-dict lookup per op) and easier to reason about.

## Construction

    iex> FastDecimal.new("1.23")
    %FastDecimal{coef: 123, exp: -2}

    iex> FastDecimal.new(123, -2)
    %FastDecimal{coef: 123, exp: -2}

Use the `~d` sigil for compile-time literals (zero runtime parse cost):

    import FastDecimal
    ~d"1.23"   # => %FastDecimal{coef: 123, exp: -2}

## Arithmetic

    iex> FastDecimal.add(FastDecimal.new("1.23"), FastDecimal.new("4.567"))
    %FastDecimal{coef: 5797, exp: -3}

    iex> FastDecimal.div(FastDecimal.new("10"), FastDecimal.new("3"), precision: 5)
    %FastDecimal{coef: 33333, exp: -4}

## Comparison

    iex> FastDecimal.compare(FastDecimal.new("1.10"), FastDecimal.new("1.1"))
    :eq

    iex> FastDecimal.equal?(FastDecimal.new("1.10"), FastDecimal.new("1.1"))
    true

# `coef`

```elixir
@type coef() :: integer() | :nan | :inf | :neg_inf
```

# `rounding_mode`

```elixir
@type rounding_mode() ::
  :half_even | :half_up | :half_down | :down | :up | :floor | :ceiling
```

# `t`

```elixir
@type t() :: %FastDecimal{coef: coef(), exp: integer()}
```

# `to_string_format`

```elixir
@type to_string_format() :: :normal | :scientific | :raw | :xsd
```

Output format for `to_string/2`. `:normal` is the default.

# `abs`

```elixir
@spec abs(t()) :: t()
```

# `add`

```elixir
@spec add(t(), t()) :: t()
```

# `cast`

```elixir
@spec cast(t() | integer() | binary() | Decimal.t() | float() | nil) ::
  {:ok, t()} | :error
```

Soft parse: returns `{:ok, t()}` or `:error` without raising. Accepts the
same inputs as `new/1` plus existing `FastDecimal` and `Decimal` structs.

This is what Ecto's `Ecto.Type` machinery calls — exposing it directly
makes user code that needs "try to coerce, otherwise complain" pleasant.

# `compare`

```elixir
@spec compare(t(), t()) :: :lt | :eq | :gt | :nan
```

# `div`

```elixir
@spec div(t(), t(), keyword()) :: t()
```

Division with configurable precision and rounding.

Options:
  * `:precision` — number of significant digits to keep in the result (default `28`)
  * `:rounding` — `:half_even` (default, banker's), `:half_up`, `:half_down`,
    `:down`, `:up`, `:floor`, `:ceiling`

# `div_int`

```elixir
@spec div_int(t(), t()) :: t()
```

Integer (truncated) division. Like `Kernel.div/2` for integers — drops the
fractional part, truncating toward zero. Result always has `exp: 0`.

    iex> FastDecimal.div_int(FastDecimal.new("10.5"), FastDecimal.new("3"))
    %FastDecimal{coef: 3, exp: 0}

# `div_rem`

```elixir
@spec div_rem(t(), t()) :: {t(), t()}
```

Returns `{quotient, remainder}` such that `a == quotient * b + remainder`.
Quotient is computed by `div_int/2`.

    iex> FastDecimal.div_rem(FastDecimal.new("10"), FastDecimal.new("3"))
    {%FastDecimal{coef: 3, exp: 0}, %FastDecimal{coef: 1, exp: 0}}

# `equal?`

```elixir
@spec equal?(t(), t()) :: boolean()
```

Returns true if both decimals compare as equal. NaN never compares equal to
anything (matches IEEE 754 behavior for floating-point NaN).

# `finite?`

```elixir
@spec finite?(t()) :: boolean()
```

Returns true if the value is a finite number (not NaN, not infinity).

# `from_float`

```elixir
@spec from_float(float()) :: t()
```

Convert an Elixir float to a FastDecimal via `Float.to_string/1`. The result
is the decimal value that `Float.to_string/1` would print for the float —
not the exact rational represented by the IEEE 754 bits.

    iex> FastDecimal.from_float(1.5)
    %FastDecimal{coef: 15, exp: -1}

    iex> FastDecimal.from_float(0.1)
    %FastDecimal{coef: 1, exp: -1}

Mirrors `Decimal.from_float/1` for drop-in compatibility. For literal-float
inputs in code, prefer the `~d` sigil — it parses at compile time with no
runtime cost.

# `from_integer`

```elixir
@spec from_integer(integer()) :: t()
```

# `gt?`

```elixir
@spec gt?(t(), t()) :: boolean()
```

# `inf`

```elixir
@spec inf() :: t()
```

Returns the +∞ sentinel value.

# `inf?`

```elixir
@spec inf?(t()) :: boolean()
```

Returns true if the value is +∞ or -∞.

# `is_decimal`
*macro* 

Guard-safe predicate. True when the argument is a `%FastDecimal{}` struct.

    defmodule MyMod do
      require FastDecimal

      def total(d) when FastDecimal.is_decimal(d) do
        # ...
      end
    end

Mirrors `Decimal.Macros.is_decimal/1` so it can be drop-in-substituted.

# `lt?`

```elixir
@spec lt?(t(), t()) :: boolean()
```

# `max`

```elixir
@spec max(t(), t()) :: t()
```

# `min`

```elixir
@spec min(t(), t()) :: t()
```

# `mult`

```elixir
@spec mult(t(), t()) :: t()
```

# `multiply`

# `nan`

```elixir
@spec nan() :: t()
```

Returns the NaN sentinel value.

# `nan?`

```elixir
@spec nan?(t()) :: boolean()
```

Returns true if the value is NaN (not a number).

# `neg_inf`

```elixir
@spec neg_inf() :: t()
```

Returns the -∞ sentinel value.

# `negate`

```elixir
@spec negate(t()) :: t()
```

# `negative?`

```elixir
@spec negative?(t()) :: boolean()
```

# `new`

```elixir
@spec new(String.t() | integer()) :: t()
```

# `new`

```elixir
@spec new(integer(), integer()) :: t()
```

# `normalize`

```elixir
@spec normalize(t()) :: t()
```

Strip trailing zeros from the coefficient, raising the exponent.
`~d"1.10"` (coef=110, exp=-2) becomes `~d"1.1"` (coef=11, exp=-1).

# `parse`

```elixir
@spec parse(String.t()) :: {:ok, t()} | :error
```

# `positive?`

```elixir
@spec positive?(t()) :: boolean()
```

# `product`

```elixir
@spec product([t()]) :: t()
```

Product of a list of FastDecimals.

# `rem`

```elixir
@spec rem(t(), t()) :: t()
```

Remainder of decimal division (same sign as the dividend).

# `round`

```elixir
@spec round(t(), integer(), rounding_mode()) :: t()
```

Round to `places` decimal places using the given rounding mode.

Default: 0 places, `:half_even` (banker's rounding).

Supported modes: `:half_even`, `:half_up`, `:half_down`, `:down`, `:up`,
`:floor`, `:ceiling`.

    iex> FastDecimal.round(FastDecimal.new("1.235"), 2)
    %FastDecimal{coef: 124, exp: -2}

    iex> FastDecimal.round(FastDecimal.new("1.236"), 2, :down)
    %FastDecimal{coef: 123, exp: -2}

    iex> FastDecimal.round(FastDecimal.new("123.456"), -1)
    %FastDecimal{coef: 12, exp: 1}

# `sigil_d`
*macro* 

Compile-time literal sigil. `~d"1.23"` becomes a `%FastDecimal{}` at compile
time, paying zero parse cost at runtime.

Use by importing: `import FastDecimal`, then `~d"1.23"`.

# `sqrt`

```elixir
@spec sqrt(
  t(),
  keyword()
) :: t()
```

Square root with configurable precision (default 28 significant digits).

Newton-Raphson on bigints; converges in ~log(digits) iterations because
the initial guess uses the number's digit count.

    iex> FastDecimal.sqrt(FastDecimal.new("4"))
    %FastDecimal{coef: 2, exp: 0}

    iex> FastDecimal.sqrt(FastDecimal.new("2"), precision: 10)
    %FastDecimal{coef: 1414213562, exp: -9}

# `sub`

```elixir
@spec sub(t(), t()) :: t()
```

# `sum`

```elixir
@spec sum([t()]) :: t()
```

Sum a list of FastDecimals. Equivalent to `Enum.reduce(list, new(0), &add/2)`
but inlined and recursion-flat. The tight inner loop avoids `Enum`'s anonymous
function call overhead.

# `to_float`

```elixir
@spec to_float(t()) :: float()
```

# `to_integer`

```elixir
@spec to_integer(t()) :: integer()
```

# `to_string`

```elixir
@spec to_string(t(), to_string_format()) :: String.t()
```

Format a decimal as a string. `format` defaults to `:normal`.

  * `:normal` — `"1234.5678"`, `"0.001"`, `"123"` (decimal-point form when
    there are fractional digits, plain integer otherwise)
  * `:scientific` — `"1.2345678E+3"` (one digit before decimal, signed `E`
    exponent). Matches Decimal's `:scientific` format.
  * `:raw` — `"1234E-5"` (raw coefficient + `E` + raw exponent). Useful for
    debugging the internal representation.
  * `:xsd` — XML Schema canonical decimal form. Same as `:normal` for our
    representation since we don't use scientific in XSD.

Special values (`NaN`, `Infinity`, `-Infinity`) print the same in every format.

# `zero?`

```elixir
@spec zero?(t()) :: boolean()
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
