diff --git a/README.md b/README.md index 327c33d..ccc1d27 100644 --- a/README.md +++ b/README.md @@ -74,13 +74,12 @@ To facilitate comparisons, the methods `is_positive_zero` and `is_negative_zero` # Methods implemented -All 12 types implement the methods available on [`f32`] and [`f64`] except: +All 12 types implement the methods available on [`f32`] and [`f64`] **except**: - deprecated and nightly-only methods +- total_cmp(&self, other: &f64) -> Ordering - sin_cos(self) -> (f64, f64) - mul_add(self, a: f64, b: f64) -> f64 -- powi(self, n: i32) -> f64 -- powf(self, n: f64) -> f64 - clamp(self, min: f64, max: f64) -> f64 - LowerExp - UpperExp diff --git a/typed_floats/src/lib.rs b/typed_floats/src/lib.rs index 7ba2c44..743d8f7 100644 --- a/typed_floats/src/lib.rs +++ b/typed_floats/src/lib.rs @@ -426,6 +426,15 @@ pub trait Atan2 { fn atan2(self, rhs: T) -> Self::Output; } +/// This trait is used to specify the return type of the [`Powf::powf()`] function. +pub trait Powf { + /// The resulting type after applying [`Powf::powf()`]. + type Output; + + /// See [`f64::powf()`] for more details. + fn powf(self, rhs: T) -> Self::Output; +} + typed_floats_macros::generate_docs!( pub trait TypedFloat {} ); diff --git a/typed_floats/tests/powf.rs b/typed_floats/tests/powf.rs new file mode 100644 index 0000000..8e1c4f4 --- /dev/null +++ b/typed_floats/tests/powf.rs @@ -0,0 +1,3 @@ +use typed_floats::*; + +typed_floats_macros::generate_tests_self_rhs!(powf); diff --git a/typed_floats/tests/powi.rs b/typed_floats/tests/powi.rs new file mode 100644 index 0000000..82faef1 --- /dev/null +++ b/typed_floats/tests/powi.rs @@ -0,0 +1,3 @@ +use typed_floats::*; + +typed_floats_macros::generate_tests_self!(powi); diff --git a/typed_floats_macros/src/gen_tests.rs b/typed_floats_macros/src/gen_tests.rs index 6716dc4..cc7130f 100644 --- a/typed_floats_macros/src/gen_tests.rs +++ b/typed_floats_macros/src/gen_tests.rs @@ -150,7 +150,7 @@ pub(crate) fn generate_tests_self( // Execute the operation, will throw if the result type is too strict let res = #test; - println!("{:?} = {:?}",#op_name, res); + println!("{:?}({:?}) = {:?}",#op_name, a, res); // Get the result as a float let as_float = #get; @@ -263,7 +263,7 @@ pub(crate) fn generate_tests_self_rhs( println!("{:?} = ...",#op_name); // This will panic if the result isn't compatible with the return type let res = #test; - println!("{:?} = {:?}",#op_name, res); + println!("{:?}({:?},{:?}) = {:?}",#op_name, a, b, res); let f: #float_type = #get; diff --git a/typed_floats_macros/src/impl_self.rs b/typed_floats_macros/src/impl_self.rs index 9cc64a1..562cc35 100644 --- a/typed_floats_macros/src/impl_self.rs +++ b/typed_floats_macros/src/impl_self.rs @@ -1070,5 +1070,48 @@ pub(crate) fn get_impl_self() -> Vec { }) })) .build(), + OpBuilder::new("powi") + .params(quote! {self, n: i32}) + .op_fn(Box::new(|_| { + let n = syn::Ident::new("n", proc_macro2::Span::call_site()); + + quote! { self.get().powi(#n) } + })) + .description(quote! { + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Examples + /// + /// ``` + /// # use typed_floats::*; + /// let x: NonNaN = (-2.0).try_into().unwrap(); + /// + /// assert_eq!(x.powi(3), -8.0); + /// assert_eq!(x.powi(2), 4.0); + /// assert_eq!(x.powi(1), -2.0); + /// assert_eq!(x.powi(0), 1.0); + /// assert_eq!(x.powi(-1), -0.5); + /// assert_eq!(x.powi(-2), 0.25); + /// assert_eq!(x.powi(-3), -0.125); + /// ``` + /// See [`f64::powi()`] for more details. + }) + .result(Box::new(|float| { + ReturnTypeSpecification::FloatSpecifications(FloatSpecifications { + accept_negative: float.s.accept_negative, + accept_positive: true, + accept_zero: true, + accept_inf: true, + }) + })) + .skip_check_return_type_strictness() + .op_test(Box::new(|var| { + quote! { #var.powi(2) } + })) + .build(), ] } diff --git a/typed_floats_macros/src/impl_self_rhs.rs b/typed_floats_macros/src/impl_self_rhs.rs index eb1359d..dfb9a6f 100644 --- a/typed_floats_macros/src/impl_self_rhs.rs +++ b/typed_floats_macros/src/impl_self_rhs.rs @@ -353,5 +353,21 @@ pub(crate) fn get_impl_self_rhs() -> Vec { }) })) .build(), + OpRhsBuilder::new("Powf", "powf") + .op_test_primitive(Box::new(|var1, var2| quote! { #var1.powf(#var2) })) + .comment("If the base is negative and the exponent is not an integer, the result is `NaN`.") + .result(Box::new(|float, _| { + if float.s.accept_negative { + ReturnTypeSpecification::NativeFloat + } else { + ReturnTypeSpecification::FloatSpecifications(FloatSpecifications { + accept_negative: false, + accept_positive: true, + accept_zero: true, + accept_inf: true, + }) + } + })) + .build(), ] } diff --git a/typed_floats_macros/src/lib.rs b/typed_floats_macros/src/lib.rs index 269a3ee..b5190a2 100644 --- a/typed_floats_macros/src/lib.rs +++ b/typed_floats_macros/src/lib.rs @@ -540,31 +540,6 @@ fn do_generate_floats(floats: &[FloatDefinition]) -> proc_macro2::TokenStream { pub fn is_positive_zero(&self) -> bool { self.0 == 0.0 && self.0.is_sign_positive() } - - /// Return the ordering between `self` and `other`. - /// - /// Unlike the standard partial comparison between floating point numbers, - /// this comparison always produces an ordering in accordance to - /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) - /// floating point standard. The values are ordered in the following sequence: - /// - /// - negative infinity - /// - negative numbers - /// - negative subnormal numbers - /// - negative zero - /// - positive zero - /// - positive subnormal numbers - /// - positive numbers - /// - positive infinity - /// - /// The ordering established by this function does not always agree with the - /// [`PartialOrd`] and [`PartialEq`] implementations of `f64`: - /// they consider negative and positive zero equal, while `total_cmp` doesn't. - #[inline] - #[must_use] - pub fn total_cmp(&self, other: &#float_type) -> core::cmp::Ordering { - self.0.total_cmp(other) - } } impl PartialEq for #full_type { diff --git a/typed_floats_macros/src/types.rs b/typed_floats_macros/src/types.rs index 5bbccb2..18f64a3 100644 --- a/typed_floats_macros/src/types.rs +++ b/typed_floats_macros/src/types.rs @@ -198,6 +198,7 @@ pub(crate) struct Op { pub(crate) fn_name: &'static str, pub(crate) trait_name: Option<&'static str>, pub(crate) comment: Option<&'static str>, + pub(crate) params: proc_macro2::TokenStream, pub(crate) description: proc_macro2::TokenStream, pub(crate) skip_check_return_type_strictness: bool, op: OpCallback, @@ -218,6 +219,7 @@ impl OpBuilder { key: fn_name, display: fn_name, fn_name, + params: quote! { self }, trait_name: None, description: proc_macro2::TokenStream::new(), comment: None, @@ -254,6 +256,11 @@ impl OpBuilder { self } + pub fn params(mut self, params: proc_macro2::TokenStream) -> Self { + self.op.params = params; + self + } + pub fn description(mut self, description: proc_macro2::TokenStream) -> Self { self.op.description = description; self @@ -327,6 +334,8 @@ impl Op { let description = &self.description; + let params = &self.params; + if let Some(trait_name) = &self.trait_name { let trait_name: proc_macro2::TokenStream = trait_name.parse().unwrap(); @@ -337,7 +346,7 @@ impl Op { #description #[inline] #[must_use] - fn #fn_ident(self) -> Self::Output { + fn #fn_ident(#params) -> Self::Output { #return_value } } @@ -348,7 +357,7 @@ impl Op { #description #[inline] #[must_use] - pub fn #fn_ident(self) -> #output_name { + pub fn #fn_ident(#params) -> #output_name { #return_value } }