hooks_rs/api/float.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
use core::{
cmp::Ordering,
ops::{Add, Div, Mul, Neg, Sub},
};
use crate::c;
use super::*;
/// Abstraction of [XFL floating point numbers](https://github.com/XRPLF/XRPL-Standards/discussions/39).
/// The struct is overloaded with basic numeric operations, such as addition, subtraction, multiplication, division, and negation. Comparison operators are also implemented.
#[derive(Clone, Copy)]
pub struct XFL(pub i64);
/// Output an XFL as a serialized object
#[inline(always)]
pub fn float_sto(
amount: &mut [u8],
currency_code: &[u8],
issuer_accid: &[u8],
float: XFL,
field_code: FieldId,
) -> Result<u64> {
let res = unsafe {
c::float_sto(
amount.as_mut_ptr() as _,
amount.len() as _,
currency_code.as_ptr() as _,
currency_code.len() as _,
issuer_accid.as_ptr() as _,
issuer_accid.len() as _,
float.0,
field_code as _,
)
};
res.into()
}
impl XFL {
/// Create a new XFL number from an exponent and mantissa
///
/// # Example
/// ```
/// let plus_1000 = XFL::new(-12, 1000000000000000).unwrap_line_number();
/// ```
#[inline(always)]
pub fn new(exponent: i32, mantissa: i64) -> Result<Self> {
Self::from_verified_i64(unsafe { c::float_set(exponent, mantissa) })
}
/// Read a serialized XFL amount into an XFL
#[inline(always)]
pub fn from_sto(serialized_xfl: &[u8; XFL_LEN]) -> Result<Self> {
Self::from_verified_i64(unsafe {
c::float_sto_set(serialized_xfl.as_ptr() as _, XFL_LEN as _)
})
}
/// Return the number 1 represented in an XFL enclosing number
///
/// # Example
/// ```
/// let half = XFL::one().mulratio(false, 1, 2)
/// ```
#[inline(always)]
pub fn one() -> Self {
// Instead of using c::float_one, we use the computed enclosing
// value directly.
XFL(6089866696204910592)
}
/// Convert an XFL floating point into an integer (floor).
/// The behavior is as follows:
/// 1. Left shift (multiply by 10) the XFL by the number of specified decimal places
/// 2. Convert the resulting XFL to an integer, discarding any remainder
/// 3. Return the integer
///
/// # Example
/// ```
/// let approx_pi = XFL::new(-15, 3140000000000000).unwrap_line_number();
///
/// if 3 != approx_pi.to_int64(0, false).unwrap_line_number() {
/// rollback(b"incorect rounding", line!().into());
/// }
/// ```
#[inline(always)]
pub fn to_int64(&self, decimal_places: u32, is_absolute: bool) -> Result<i64> {
let result = unsafe { c::float_int(self.0, decimal_places, is_absolute as _) };
match result {
res if res >= 0 => Ok(res),
_ => Err(Error::from_code(result as _)),
}
}
/// Get the exponent of an XFL enclosing number
///
/// # Example
/// ```
/// let plus_998 = XFL::new(-13, 9980000000000000).unwrap_line_number();
///
/// if plus_998.exponent() != -13 {
/// rollback(b"exponent incorrect", line!().into());
/// }
/// ```
#[inline(always)]
pub fn exponent(&self) -> i64 {
(((self.0 >> 54 & 0xFF) as i32) - 97).into()
}
/// Get the exponent of an XFL enclosing number
///
/// # Example
/// ```
/// let plus_998 = XFL::new(-13, 9980000000000000).unwrap_line_number();
///
/// if plus_998.mantissa() != 9980000000000000 {
/// rollback(b"mantissa incorrect", line!().into());
/// }
/// ```
#[inline(always)]
pub fn mantissa(&self) -> i64 {
unsafe { c::float_mantissa(self.0) }
}
/// Multiply an XFL floating point by a non-XFL numerator and denominator
///
/// # Example
/// ```
/// if XFL::one().mulratio(false, 1, 2).unwrap_line_number()
/// != XFL::one().mulratio(false, 5, 10).unwrap_line_number()
/// {
/// rollback(b"", line!().into());
/// }
/// ```
#[inline(always)]
pub fn mulratio(&self, round_up: bool, numerator: u32, denominator: u32) -> Result<XFL> {
Self::from_verified_i64(unsafe {
c::float_mulratio(self.0, round_up as _, numerator, denominator)
})
}
// Create a new XFL number from a verified i64, that is,
// a number that is known to be a valid XFL number.
//
// Because it is too dangerous to be exposed to the user,
// this function is only visible to `pub(crate)` level.
//
// For that reason, `From<i64> for Result<XFL>` is not implemented.
//
// Only use this function to create an XFL number from
// C function calls.
#[inline(always)]
pub(crate) fn from_verified_i64(source: i64) -> Result<Self> {
match source {
source if source >= 0 => Ok(XFL(source)),
_ => Err(Error::from_code(source as _)),
}
}
}
impl Add for XFL {
type Output = Result<XFL>;
#[inline(always)]
fn add(self, other: XFL) -> Self::Output {
Self::from_verified_i64(unsafe { c::float_sum(self.0, other.0) })
}
}
impl Sub for XFL {
type Output = Result<XFL>;
#[inline(always)]
fn sub(self, other: XFL) -> Self::Output {
unsafe {
let rhs = match Self::from_verified_i64(c::float_negate(other.0)) {
Ok(rhs) => rhs,
Err(e) => return Err(e),
};
Self::from_verified_i64(c::float_sum(self.0, rhs.0))
}
}
}
impl Mul for XFL {
type Output = Result<XFL>;
#[inline(always)]
fn mul(self, other: XFL) -> Self::Output {
Self::from_verified_i64(unsafe { c::float_multiply(self.0, other.0) })
}
}
impl Div for XFL {
type Output = Result<XFL>;
#[inline(always)]
fn div(self, other: XFL) -> Self::Output {
Self::from_verified_i64(unsafe { c::float_divide(self.0, other.0) })
}
}
impl Neg for XFL {
type Output = Result<XFL>;
#[inline(always)]
fn neg(self) -> Self::Output {
Self::from_verified_i64(unsafe { c::float_negate(self.0) })
}
}
impl PartialEq for XFL {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
let res = unsafe { c::float_compare(self.0, other.0, c::COMPARE_EQUAL) };
match res {
1 => true,
// This is based on the invariant that the arguments to eq function
// are all valid XFL numbers.
_ => false,
}
}
}
impl PartialOrd for XFL {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
unsafe {
match c::float_compare(self.0, other.0, c::COMPARE_EQUAL) {
1 => Some(Ordering::Equal),
0 => {
// This is because float_compare cannot return an ordering at one go.
match c::float_compare(self.0, other.0, c::COMPARE_LESS) {
1 => Some(Ordering::Less),
0 => Some(Ordering::Greater),
_ => None,
}
}
_ => None,
}
}
}
}