use super::FixedPoint;
impl<const D: u32> FixedPoint<u64, D> {
pub const fn from_integer(value: u64) -> Option<Self> {
let scale = Self::scale_u64();
match value.checked_mul(scale) {
Some(raw) => Some(Self { raw }),
None => None,
}
}
pub const fn from_decimal(integer: u64, frac: u64) -> Option<Self> {
let scale = Self::scale_u64();
if frac >= scale {
return None;
}
let frac_digits = count_digits_u64(frac);
if frac_digits > D {
return None;
}
let pad = D - frac_digits;
let mut padded_frac = frac;
let mut i = 0;
while i < pad {
padded_frac = match padded_frac.checked_mul(10) {
Some(v) => v,
None => return None,
};
i += 1;
}
match integer.checked_mul(scale) {
Some(int_raw) => match int_raw.checked_add(padded_frac) {
Some(raw) => Some(Self { raw }),
None => None,
},
None => None,
}
}
pub const fn to_integer(self) -> u64 {
self.raw / Self::scale_u64()
}
pub const fn frac_raw(self) -> u64 {
self.raw % Self::scale_u64()
}
}
impl<const D: u32> FixedPoint<u128, D> {
pub const fn from_integer(value: u128) -> Option<Self> {
let scale = Self::scale_u128();
match value.checked_mul(scale) {
Some(raw) => Some(Self { raw }),
None => None,
}
}
pub const fn from_decimal(integer: u128, frac: u128) -> Option<Self> {
let scale = Self::scale_u128();
if frac >= scale {
return None;
}
let frac_digits = count_digits_u128(frac);
if frac_digits > D {
return None;
}
let pad = D - frac_digits;
let mut padded_frac = frac;
let mut i = 0;
while i < pad {
padded_frac = match padded_frac.checked_mul(10) {
Some(v) => v,
None => return None,
};
i += 1;
}
match integer.checked_mul(scale) {
Some(int_raw) => match int_raw.checked_add(padded_frac) {
Some(raw) => Some(Self { raw }),
None => None,
},
None => None,
}
}
pub const fn to_integer(self) -> u128 {
self.raw / Self::scale_u128()
}
pub const fn frac_raw(self) -> u128 {
self.raw % Self::scale_u128()
}
}
const fn count_digits_u64(mut n: u64) -> u32 {
if n == 0 {
return 0;
}
let mut count = 0;
while n > 0 {
count += 1;
n /= 10;
}
count
}
const fn count_digits_u128(mut n: u128) -> u32 {
if n == 0 {
return 0;
}
let mut count = 0;
while n > 0 {
count += 1;
n /= 10;
}
count
}
#[cfg(test)]
mod tests {
use super::*;
type Fp6 = FixedPoint<u64, 6>;
type Fp18 = FixedPoint<u128, 18>;
#[test]
fn from_integer_u64() {
let v = Fp6::from_integer(42).unwrap();
assert_eq!(*v.raw(), 42_000_000);
assert_eq!(v.to_integer(), 42);
}
#[test]
fn from_integer_overflow_u64() {
assert!(Fp6::from_integer(u64::MAX).is_none());
}
#[test]
fn from_decimal_u64() {
let v = Fp6::from_decimal(3, 14).unwrap();
assert_eq!(*v.raw(), 3_140_000);
}
#[test]
fn from_decimal_zero_frac_u64() {
let v = Fp6::from_decimal(5, 0).unwrap();
assert_eq!(*v.raw(), 5_000_000);
}
#[test]
fn from_decimal_frac_too_large_u64() {
assert!(Fp6::from_decimal(1, 1_000_000).is_none());
}
#[test]
fn from_integer_u128() {
let v = Fp18::from_integer(100).unwrap();
assert_eq!(*v.raw(), 100_000_000_000_000_000_000u128);
assert_eq!(v.to_integer(), 100);
}
#[test]
fn from_decimal_u128() {
let v = Fp18::from_decimal(3, 14).unwrap();
assert_eq!(v.to_integer(), 3);
assert_eq!(v.frac_raw(), 140_000_000_000_000_000);
}
#[test]
fn to_integer_truncates() {
let v = Fp6::from_raw(7_999_999); assert_eq!(v.to_integer(), 7);
}
#[test]
fn frac_raw_correct() {
let v = Fp6::from_raw(3_140_000);
assert_eq!(v.frac_raw(), 140_000);
}
#[test]
fn count_digits() {
assert_eq!(count_digits_u64(0), 0);
assert_eq!(count_digits_u64(1), 1);
assert_eq!(count_digits_u64(9), 1);
assert_eq!(count_digits_u64(10), 2);
assert_eq!(count_digits_u64(999), 3);
assert_eq!(count_digits_u64(1000), 4);
}
}