primitive_fixed_point_decimal 1.0.1

Primitive fixed-point decimal types.
Documentation
# Comparison to Other Decimal Crates

This document compares `primitive_fixed_point_decimal` crate to
[`rust_decimal`](https://docs.rs/rust_decimal) and
[`bigdecimal`](https://docs.rs/bigdecimal) crates.

Because I have not used these 2 other crates in real project, the following
comparison is superficial and subjective.

Note: `primitive_fixed_point_decimal` supports 2 types, `ConstScaleFpdec`
and `OobPrecFpdec`. They differ only in the way they specify scale.
So only the former is used in the following description.


## Representation

A decimal consists of 3 parts: a scale, a sign, and the mantissa (significant digits).

Now let's look at how the three crates represent these parts respectively.


### `primitive_fixed_point_decimal::ConstScaleFpdec`

The *scale* is specified in the type's constant generics. The *sign*
and *mantissa* are represented by the underlying signed integer.

For example, `ConstScaleFpdec<i64, 4>` means using `i64` as the underlying
representation for sign and mantissa, and `4` is the scale.

The memory layout:

```
+-?--------------- ... ---+
| signed mantissa         |
+----------------- ... ---+
```

The size (`?` above) depends on the type of underlying signed integer,
which supports all Rust primitive signed integers: `i8`, `i16`, `i32`,
`i64` and `i128`. For example, `ConstScaleFpdec<i64, 4>` takes 64 bits
(8 bytes).

The scale is bound to the decimal *type* but not *instance*, so it is not
shown in the memory layout above.

The [definition](https://docs.rs/primitive_fixed_point_decimal/0.7.0/src/primitive_fixed_point_decimal/const_scale_fpdec.rs.html#23):

```rust
pub struct ConstScaleFpdec<I, const S: i32>(I);
```

where `I` is the type of underlying signed integer.


### `rust_decimal::Decimal`

1 bit for *sign*, 8 bits for *scale*, 23 bits unused, and 96 bits for *mantissa*.
So it takes 128 bits (16 bytes) totally.

The memory layout:

```
+-32---------------+-96------------------- ... --+
|sign,scale,unused | mantissa                    |
+------------------+---------------------- ... --+
```

The [definition](https://docs.rs/rust_decimal/1.37.2/src/rust_decimal/decimal.rs.html#115-126):

```rust
pub struct Decimal {
    // Bits 0-15: unused
    // Bits 16-23: Contains "e", a value between 0-28 that indicates the scale
    // Bits 24-30: unused
    // Bit 31: the sign of the Decimal value, 0 meaning positive and 1 meaning negative.
    flags: u32,
    // The lo, mid, hi, and flags fields contain the representation of the
    // Decimal value as a 96-bit integer.
    hi: u32,
    lo: u32,
    mid: u32,
}
```


### `bigdecimal::BigDecimal`

64 bits for *scale*, 8 bits for *sign*, (sizeof(usize) - 8) bits for padding,
and `sizeof(usize)*3` bits for the Vec of mantissa data.

For example, on 64-bits system, it takes 320 bits (40 bytes) for the meta data
and uncertain-size for mantissa data.

Obviously the `BigDecimal` is not `Copy`.

The memory layout:

```
+-64-------+-8--+-?-----+-usize*3----------------------------+
| scale    |sign|padding| mantissa: Vec<u32/u64>             |
+----------+----+-------+---+--------------------------------+
                            |
                            V-?------------- ... ---+
                            | mantissa              |
                            +--------------- ... ---+
```

The [definition](https://docs.rs/bigdecimal/0.4.8/src/bigdecimal/lib.rs.html#206-210):

```rust
pub struct BigDecimal {
    int_val: BigInt,
    // A positive scale means a negative power of 10
    scale: i64,
}
pub struct BigInt {
    sign: Sign,
    data: BigUint,
}
pub struct BigUint {
    data: Vec<BigDigit>,
}
```

### Summary

The biggest difference of `primitive_fixed_point_decimal`, compared with the
other two crates, is that it binds the scale to decimal *type*, while the
other two crates save the scale in decimal *instance*.

Besides, `primitive_fixed_point_decimal` has the most compact memory layout,
while `bigdecimal` has the strongest expression ability (unlimited mantissa).


## Operations

In `primitive_fixed_point_decimal`, the `+`, `-` and comparison operations
work only between decimals of the same scale, which is designed intentionally.
The `*` and `/` operations can work between decimals of different scales
and can also set the result's scale. See the crate's document for details.

Other 2 crates both support `+`, `-` and comparison operations between
decimals of different scales. They first rescale the decimal with smaller
scale into the larger scale, and then execute the operations. And
the result's scale of `*` operation is the sum of 2 oprands. Because
`rust_decimal` has limited mantissa which makes it easy to overflow,
it will also try to reduce the result's scale if overflow.


## How to Choose

As I understand it, these types can be categorized into the following:

- `primitive_fixed_point_decimal`: fixed-point in base 10

- `rust_decimal` and `bigdecimal`: floating-point in base 10

- Rust floats(`f32` and `f64`): floating-point in base 2

All `primitive_fixed_point_decimal`, `rust_decimal` and `bigdecimal` are in
base 10, which is the reason they are called `*_decimal`.
Therefore, they can represent decimal fractions accurately, and avoid
calculation error such as `0.1 + 0.2 != 0.3`.

The difference is that `primitive_fixed_point_decimal` is *fixed-point*,
while the 2 others are *floating-point*. Thus, the application scenarios
are very clear.

If each decimal type has a fixed scale in you application, which means
all the decimal instances under each type have the same scale, it's
suitable for `primitive_fixed_point_decimal` crate. Otherwise, you
should use `rust_decimal` or `bigdecimal` crate.

Note: [The documentation of `rust_decimal`](https://docs.rs/rust_decimal/1.37.1/rust_decimal/struct.Decimal.html)
says it's "a fixed-precision decimal number".
And [the documentation of `bigdecimal`](https://docs.rs/bigdecimal/0.4.8/bigdecimal/index.html)
also implies it's fixed-point by: "avoids common floating point errors".
However, I still think they are
[floating-point](https://en.wikipedia.org/wiki/Decimal_floating_point),
because the scale is carried in the number and changes during calculation.