decimal-scaled 0.2.3

Const-generic base-10 fixed-point decimals (D9/D18/D38/D76/D153/D307) with integer-only transcendentals correctly rounded to within 0.5 ULP — exact at the type's last representable place. Deterministic across every platform; no_std-friendly.
Documentation
# The `d*!` macros

The crate exposes one proc-macro per public width — `d9!`, `d18!`,
`d38!`, plus `d76!` / `d153!` / `d307!` under the wide-tier feature
flags. Each parses a numeric literal (or inline expression),
picks or is told a scale, and expands to a
`D<N><SCALE>::from_bits(...)` call. There is no runtime parsing and
the scale lands in the type.

This page focuses on `d38!` because most examples in the crate use
the 128-bit tier; everything below applies identically to the other
entry points (substitute `MAX_SCALE` per-width). The full
specification — including radix-prefix integers and the curated
per-scale wrappers (`d38s12!`, `d18s6!`, …) — lives in
[`macros/README.md`](../macros/README.md).

Enable the macros with the `macros` feature (off by default):

```toml
[dependencies]
decimal-scaled = { version = "0.2.3", features = ["macros"] }
```

```rust
use decimal_scaled::d38;
```

## Automatic scale inference

With no qualifier, the scale is the number of fractional digits you
wrote — trailing zeros are significant:

```rust
# use decimal_scaled::d38;
# use decimal_scaled::{D38, D38s0, D38s2, D38s5};
let a = d38!(1.23);          // D38<2>
let b = d38!(123);           // D38<0>
let c = d38!(1.0);           // D38<1>  — the written `.0` counts
let d = d38!(1.230);         // D38<3>  — trailing zero preserved
let e = d38!(0.001);         // D38<3>
let f = d38!(-1.23);         // D38<2>
let g = d38!(1_234.567_89);  // D38<5>  — underscores allowed
# assert_eq!(a, D38s2::from_bits(123));
# assert_eq!(b, D38s0::from_bits(123));
```

## Scientific notation

`e` notation is supported; the resulting scale is whatever is needed to
represent the value exactly:

```rust
# use decimal_scaled::d38;
# use decimal_scaled::{D38, D38s0};
let a = d38!(1.5e3);    // 1500    -> D38<0>
let b = d38!(1.5e-3);   // 0.0015  -> D38<4>
let c = d38!(1e6);      // 1000000 -> D38<0>
let d = d38!(-2.5e-2);  // -0.025  -> D38<3>
# assert_eq!(a, D38s0::from_bits(1500));
```

## The `scale N` qualifier

Force a specific target scale. Scaling *up* pads with zeros (lossless):

```rust
# use decimal_scaled::d38;
# use decimal_scaled::D38;
let a = d38!(1.23, scale 4);     // D38<4>, raw 12_300
let b = d38!(42, scale 0);       // D38<0>, raw 42
let c = d38!(1.5e3, scale 5);    // D38<5>, raw 150_000_000
```

Scaling *down* with `scale N` alone is a compile error if it would lose
digits — add `rounded` to opt in.

## The `rounded` qualifier

Combine with `scale N` to round (half-to-even) when the target scale has
fewer digits than the literal:

```rust
# use decimal_scaled::d38;
# use decimal_scaled::D38s2;
let a = d38!(1.234999, scale 2, rounded);   // 1.23
let b = d38!(1.235001, scale 2, rounded);   // 1.24
let c = d38!(1.235,    scale 2, rounded);   // 1.24  (tie -> even)
let d = d38!(1.225,    scale 2, rounded);   // 1.22  (tie -> even)
# assert_eq!(a, D38s2::from_bits(123));
```

## Inline expressions

The first argument can be an integer expression; combine with
`scale N` to land it in a typed `D38`:

```rust
# use decimal_scaled::d38;
# use decimal_scaled::{D38, D38s0};
let a = d38!(10 * 12 + 3, scale 0);   // D38<0>, raw 123
let b = d38!(5, scale 4);             // D38<4>, raw 50_000
# assert_eq!(a, D38s0::from_bits(123));
```

---

# How the type family is generated — the `decl_*!` macros

This section is for *contributors*, not users of the crate.

Hand-writing six near-identical impl surfaces (`D9` … `D307`) would be
unmaintainable, so the per-width surface is generated by a family of
internal declarative macros in `src/macros/`. Each file owns one
surface — `arithmetic.rs`, `basics.rs`, `conversions.rs`, `display.rs`,
`overflow.rs`, `rescale.rs`, … — and exports one `decl_decimal_*!`
macro. `core_type.rs` is then largely a list of macro invocations, one
group per width:

```rust,ignore
crate::macros::basics::decl_decimal_basics!(D38, i128, 38);
crate::macros::arithmetic::decl_decimal_arithmetic!(D18, i64, i128);
crate::macros::basics::decl_decimal_basics!(wide D76, crate::wide::I256, 76);
```

Most macros have two front-end arms:

- a **native** arm for primitive-integer storage (`i32` / `i64` /
  `i128`), which uses `as`-casts and integer literals;
- a **`wide`** arm for the in-tree wide-integer storage (`D76` /
  `D153` / `D307`), which has no `as`-casts from literals — it builds
  constants via `from_str_radix` and widens via `crate::wide_int::wide_cast`.

Both arms forward to a shared `@impl` / `@common` arm wherever the logic
is genuinely identical, so each operation is written once.

The discipline: anything uniform across widths lives in a macro;
anything genuinely width-specific (e.g. `D38`'s hand-rolled
`mg_divide` multiply/divide path) stays hand-coded. See
[`macros/README.md`](../macros/README.md) for the full specification.