Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Arithmetic and Overflow

Values of one scale add, subtract, compare and take a remainder as their steps do, so + on two D64<2>s is the i64’s add and nothing more. A value times an integer, or divided by one, is a count of it:

fn values_of_one_scale_add_as_their_steps_do() {
    use ddust::{D64, dec};

    let (bid, ask): (D64<2>, D64<2>) = (dec!(99.98), dec!(100.02));
    assert_eq!(ask - bid, dec!(0.04));
    assert_eq!((bid + ask) / 2, dec!(100), "a mean, by a count");
    assert_eq!(bid * 3, dec!(299.94));
    assert_eq!(dec!(10.00: D64<2>) % dec!(3), dec!(1), "the remainder after a whole quotient");
    assert!(bid < ask);
    let fills: [D64<2>; 3] = [dec!(1.25), dec!(2.50), dec!(0.75)];
    assert_eq!(fills.iter().sum::<D64<2>>(), dec!(4.5));
}
OperationIts scaleExact
a + b, a - b, a % b, -athe operands’yes
a * n and n * a, n an Ia’syes
a * bthe sum of the factors’yes
a / n, n an Ia’struncated toward zero
a / bthe dividend’struncated toward zero
==, < and the other comparesyes

Each operator has its assigning form, += and the rest, and a decimal sums from an iterator, starting from zero at its scale.

Division Truncates

A quotient is at the dividend’s scale, truncated toward zero as the integer’s / is:

fn a_quotient_keeps_the_dividends_scale_and_truncates() {
    use ddust::{D64, dec};

    let (budget, price): (D64<2>, D64<2>) = (dec!(100), dec!(3));
    assert_eq!(budget / price, dec!(33.33), "toward zero, as the integer's `/`");
    assert_eq!(-budget / price, dec!(-33.33));
    assert_eq!(dec!(1: D64<2>) / 3, dec!(0.33), "by a count, likewise");
}

The operators never take a rounding mode, since they behave as the integer’s. To round a quotient, or to take it at another scale, call div_round or div_round_to with a mode, as Rounding shows. A quotient by zero panics in every build, as the integer’s does.

Overflow

Past the range, an operator does what the integer’s does: it panics with overflow checks on, as in a debug build, and wraps otherwise, as in a release build. The profile’s overflow-checks decides, never debug-assertions:

fn past_the_range_an_operator_panics_with_overflow_checks_on() {
    use ddust::{D64, dec};

    let max: D64<2> = D64::MAX;
    let _ = max + dec!(0.01);
}

A release build that should panic instead sets overflow-checks = true in its profile. Where the range can be passed in either build, the integer’s four families say what happens instead:

fn each_operator_has_the_integers_four_families() {
    use ddust::{D64, dec};

    let (max, cent): (D64<2>, D64<2>) = (D64::MAX, dec!(0.01));
    assert_eq!(max.checked_add(cent), None);
    assert_eq!(max.saturating_add(cent), D64::MAX);
    assert_eq!(max.wrapping_add(cent), D64::MIN);
    assert_eq!(max.overflowing_add(cent), (D64::MIN, true));
    assert_eq!(cent.checked_div(D64::<2>::ZERO), None, "never by zero");
}
FamilyPast the range
checked_*None
saturating_*the end of the range it passes
wrapping_*wrapped around the range
overflowing_*wrapped around the range, and true

Each family has add, sub, mul (the exact product), div, mul_int and div_int (by a count), neg and abs; rem has checked_rem, wrapping_rem and overflowing_rem, as the integer has. A checked_* method returns None for a zero divisor too, where its operator panics.

Unsigned Decimals

An unsigned decimal, UD64 and its siblings, stops at zero as its integer does:

fn an_unsigned_decimal_stops_at_zero() {
    use ddust::{UD64, dec};

    let (balance, withdrawal): (UD64<2>, UD64<2>) = (dec!(10), dec!(12.50));
    assert_eq!(balance.checked_sub(withdrawal), None);
    assert_eq!(balance.saturating_sub(withdrawal), UD64::ZERO);
}

It has no negation, which the compiler refuses:

//! An unsigned decimal has no negation.

use ddust::{UD64, dec};

fn main() {
    let amount: UD64<2> = dec!(1.5);
    let _negated = -amount;
}
error[E0600]: cannot apply unary operator `-` to type `Decimal<u64, Fixed<2>>`
 --> tests/compile_fail/an_unsigned_decimal_never_negates.rs:7:20
  |
7 |     let _negated = -amount;
  |                    ^^^^^^^ cannot apply unary operator `-`

unsigned_abs takes a signed value’s magnitude as an unsigned decimal, exact for every value, the minimum included.