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));
}
| Operation | Its scale | Exact |
|---|---|---|
a + b, a - b, a % b, -a | the operands’ | yes |
a * n and n * a, n an I | a’s | yes |
a * b | the sum of the factors’ | yes |
a / n, n an I | a’s | truncated toward zero |
a / b | the dividend’s | truncated toward zero |
==, < and the other compares | yes |
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");
}
| Family | Past 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.