Skip to main content

num_valid/backends/rug/
raw.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! # Arbitrary-Precision Floating-Point Raw Implementations
4//!
5//! This module provides raw trait implementations for the [`rug`](https://docs.rs/rug/) backend,
6//! enabling arbitrary-precision floating-point arithmetic with configurable precision.
7//!
8//! ## Purpose and Role
9//!
10//! The primary role of this module is to implement the core raw traits
11//! ([`RawScalarTrait`](crate::core::traits::raw::RawScalarTrait),
12//! [`RawRealTrait`](crate::core::traits::raw::RawRealTrait), etc.) for [`rug::Float`]
13//! and [`rug::Complex`]. These implementations provide the computational foundations
14//! that validated wrappers build upon.
15//!
16//! ## Precision Model
17//!
18//! Unlike the native `f64` backend with fixed 53-bit precision, the rug backend uses
19//! **const generic precision**:
20//!
21//! - Precision is specified in bits (e.g., 100, 200, 500)
22//! - All operations preserve precision of the operands
23//! - Precision mismatches are detected during validation
24//!
25//! ## MPFR Constant Optimization
26//!
27//! For mathematical constants, this module uses MPFR's precomputed constants
28//! when available, providing significant performance improvements:
29//!
30//! - [`rug::float::Constant::Pi`] - ~10x faster than computing `acos(-1)`
31//! - [`rug::float::Constant::Log2`] - ~10x faster than computing `ln(2)`
32//!
33//! ## Memory and Performance Characteristics
34//!
35//! - `rug::Float` and `rug::Complex` are heap-allocated, non-`Copy` types
36//! - All operations support reference-based variants to minimize cloning
37//! - Memory usage scales with precision (approximately `precision/8` bytes per value)
38
39use crate::{
40    core::{
41        errors::{
42            ErrorsRawRealToInteger, ErrorsValidationRawComplex, ErrorsValidationRawReal,
43            capture_backtrace,
44        },
45        traits::{
46            raw::{
47                RawComplexTrait, RawRealTrait, RawScalarHyperbolic, RawScalarPow, RawScalarTrait,
48                RawScalarTrigonometric, RawScalarValidate,
49            },
50            validation::FpChecks,
51        },
52    },
53    functions::{Conjugate, NegAssign, Rounding, Sign},
54    kernels::PrecisionBits,
55};
56use duplicate::duplicate_item;
57use rug::{
58    float::Constant as MpfrConstant,
59    ops::{CompleteRound, Pow},
60};
61use std::{
62    cmp::Ordering,
63    hash::{Hash, Hasher},
64    num::FpCategory,
65    ops::Neg,
66};
67
68#[duplicate_item(
69    T;
70    [rug::Float];
71    [rug::Complex];
72)]
73impl RawScalarTrigonometric for T {
74    #[duplicate_item(
75        unchecked_method method;
76        [unchecked_sin]  [sin];
77        [unchecked_asin] [asin];
78        [unchecked_cos]  [cos];
79        [unchecked_acos] [acos];
80        [unchecked_tan]  [tan];
81        [unchecked_atan] [atan];)]
82    #[inline(always)]
83    fn unchecked_method(self) -> Self {
84        T::method(self)
85    }
86}
87
88#[duplicate_item(
89    T;
90    [rug::Float];
91    [rug::Complex];
92)]
93impl RawScalarHyperbolic for T {
94    #[duplicate_item(
95        unchecked_method  method;
96        [unchecked_sinh]  [sinh];
97        [unchecked_asinh] [asinh];
98        [unchecked_cosh]  [cosh];
99        [unchecked_acosh] [acosh];
100        [unchecked_tanh]  [tanh];
101        [unchecked_atanh] [atanh];)]
102    #[inline(always)]
103    fn unchecked_method(self) -> Self {
104        T::method(self)
105    }
106}
107
108#[duplicate_item(
109    T;
110    [rug::Float];
111    [rug::Complex];
112)]
113impl RawScalarPow for T {
114    #[duplicate_item(
115        unchecked_method               exponent_type;
116        [unchecked_pow_exponent_i8]    [i8];
117        [unchecked_pow_exponent_i16]   [i16];
118        [unchecked_pow_exponent_i32]   [i32];
119        [unchecked_pow_exponent_i64]   [i64];
120        [unchecked_pow_exponent_i128]  [i128];
121        [unchecked_pow_exponent_isize] [isize];
122        [unchecked_pow_exponent_u8]    [u8];
123        [unchecked_pow_exponent_u16]   [u16];
124        [unchecked_pow_exponent_u32]   [u32];
125        [unchecked_pow_exponent_u64]   [u64];
126        [unchecked_pow_exponent_u128]  [u128];
127        [unchecked_pow_exponent_usize] [usize];
128    )]
129    #[inline(always)]
130    fn unchecked_method(self, exponent: &exponent_type) -> Self {
131        T::pow(self, exponent)
132    }
133}
134
135impl RawScalarValidate for rug::Float {
136    type ValidationError = ErrorsValidationRawReal<rug::Float>;
137
138    #[inline(always)]
139    fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
140        match self.raw_classify() {
141            FpCategory::Nan => Err(ErrorsValidationRawReal::<rug::Float>::IsNaN {
142                value: self.clone(),
143                backtrace: capture_backtrace(),
144            }),
145            FpCategory::Infinite => {
146                if self.is_sign_positive() {
147                    Err(ErrorsValidationRawReal::<rug::Float>::IsPosInfinity {
148                        backtrace: capture_backtrace(),
149                    })
150                } else {
151                    Err(ErrorsValidationRawReal::<rug::Float>::IsNegInfinity {
152                        backtrace: capture_backtrace(),
153                    })
154                }
155            }
156            FpCategory::Subnormal => {
157                unreachable!("rug::Float should not produce subnormal values");
158            }
159            FpCategory::Zero | FpCategory::Normal => {
160                // No action needed
161                Ok(())
162            }
163        }
164    }
165}
166
167impl RawScalarTrait for rug::Float {
168    fn raw_zero(precision: u32) -> Self {
169        rug::Float::with_val(precision, 0.)
170    }
171
172    fn is_zero(&self) -> bool {
173        rug::Float::is_zero(self)
174    }
175
176    fn raw_one(precision: u32) -> Self {
177        rug::Float::with_val(precision, 1.)
178    }
179
180    #[duplicate_item(
181        unchecked_method       method;
182        [unchecked_reciprocal] [recip];
183        [unchecked_exp]        [exp];
184        [unchecked_sqrt]       [sqrt];
185        [unchecked_ln]         [ln];
186        [unchecked_log2]       [log2];
187        [unchecked_log10]      [log10];
188    )]
189    #[inline(always)]
190    fn unchecked_method(self) -> Self {
191        rug::Float::method(self)
192    }
193
194    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
195    ///
196    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
197    #[inline(always)]
198    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
199        rug::Float::mul_add(self, b, c)
200    }
201
202    #[inline(always)]
203    fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H) {
204        debug_assert!(
205            self.is_finite(),
206            "Hashing a non-finite rug::Float value (i.e., NaN or Infinity) may lead to inconsistent results."
207        );
208        // Always include precision in the hash
209        self.prec().hash(state);
210
211        // Handle signed zeros by normalizing to positive zero
212        if self.is_zero() {
213            // Ensure that -0.0 and 0.0 hash to the same value
214            rug::Float::with_val(self.prec(), 0.0)
215                .to_string_radix(10, None)
216                .hash(state);
217        } else {
218            // For non-zero values, create a deterministic string representation
219            // Use enough decimal places to capture the full precision
220            self.to_string_radix(10, None).hash(state)
221        }
222    }
223}
224
225impl PrecisionBits for rug::Float {
226    #[inline(always)]
227    fn precision_bits(&self) -> u32 {
228        self.prec()
229    }
230}
231
232impl RawRealTrait for rug::Float {
233    type RawComplex = rug::Complex;
234
235    #[inline(always)]
236    fn unchecked_abs(self) -> rug::Float {
237        rug::Float::abs(self)
238    }
239
240    #[inline(always)]
241    fn unchecked_atan2(self, denominator: &Self) -> Self {
242        rug::Float::atan2(self, denominator)
243    }
244
245    #[inline(always)]
246    fn unchecked_pow_exponent_real(self, exponent: &Self) -> Self {
247        rug::Float::pow(self, exponent)
248    }
249
250    #[inline(always)]
251    fn unchecked_hypot(self, other: &Self) -> Self {
252        rug::Float::hypot(self, other)
253    }
254
255    #[inline(always)]
256    fn unchecked_ln_1p(self) -> Self {
257        rug::Float::ln_1p(self)
258    }
259
260    #[inline(always)]
261    fn unchecked_exp_m1(self) -> Self {
262        rug::Float::exp_m1(self)
263    }
264
265    /// Multiplies two pairs and adds them in one fused operation, rounding to the nearest with only one rounding error.
266    /// `a.unchecked_mul_add_mul_mut(&b, &c, &d)` produces a result like `&a * &b + &c * &d`, but stores the result in `a` using its precision.
267    #[inline(always)]
268    fn unchecked_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
269        self.mul_add_mul_mut(mul, add_mul1, add_mul2);
270    }
271
272    /// Multiplies two pairs and subtracts them in one fused operation, rounding to the nearest with only one rounding error.
273    /// `a.unchecked_mul_sub_mul_mut(&b, &c, &d)` produces a result like `&a * &b - &c * &d`, but stores the result in `a` using its precision.
274    #[inline(always)]
275    fn unchecked_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
276        self.mul_sub_mul_mut(mul, sub_mul1, sub_mul2);
277    }
278
279    #[inline(always)]
280    fn raw_total_cmp(&self, other: &Self) -> Ordering {
281        rug::Float::total_cmp(self, other)
282    }
283
284    /// Clamps the value within the specified bounds.
285    #[inline(always)]
286    fn raw_clamp(self, min: &Self, max: &Self) -> Self {
287        rug::Float::clamp(self, min, max)
288    }
289
290    #[inline(always)]
291    fn raw_classify(&self) -> FpCategory {
292        rug::Float::classify(self)
293    }
294
295    #[inline(always)]
296    fn raw_two(precision: u32) -> Self {
297        rug::Float::with_val(precision, 2.)
298    }
299
300    #[inline(always)]
301    fn raw_one_div_2(precision: u32) -> Self {
302        rug::Float::with_val(precision, 0.5)
303    }
304
305    #[inline(always)]
306    fn raw_pi(precision: u32) -> Self {
307        // Use MPFR's optimized precomputed π constant (10x faster than acos(-1))
308        rug::Float::with_val(precision, MpfrConstant::Pi)
309    }
310
311    #[inline(always)]
312    fn raw_two_pi(precision: u32) -> Self {
313        rug::Float::with_val(precision, MpfrConstant::Pi) * 2
314    }
315
316    #[inline(always)]
317    fn raw_pi_div_2(precision: u32) -> Self {
318        rug::Float::with_val(precision, MpfrConstant::Pi) / 2
319    }
320
321    #[inline(always)]
322    fn raw_max_finite(precision: u32) -> Self {
323        // Create a Float with the desired precision
324        // let f = rug::Float::new(PRECISION);
325
326        // Fill the significand with all 1s: 1 - 2^(-precision)
327        // This gives you the largest significand before it rolls over
328        let one = rug::Float::with_val(precision, 1);
329        let eps = rug::Float::with_val(precision, rug::Float::u_pow_u(2, precision)).recip();
330        let significand = one - &eps;
331
332        // Scale it to the maximum exponent (subtract 1 to avoid overflow to infinity)
333        let max_exp = rug::float::exp_max() - 1;
334        //        println!("max_exp = {max_exp}");
335        significand * rug::Float::with_val(precision, rug::Float::u_pow_u(2, max_exp as u32))
336    }
337
338    #[inline(always)]
339    fn raw_min_finite(precision: u32) -> Self {
340        Self::raw_max_finite(precision).neg()
341    }
342
343    #[inline(always)]
344    fn raw_epsilon(precision: u32) -> Self {
345        rug::Float::u_pow_u(2, precision - 1)
346            .complete(precision)
347            .recip()
348    }
349
350    #[inline(always)]
351    fn raw_ln_2(precision: u32) -> Self {
352        // Use MPFR's optimized precomputed ln(2) constant
353        rug::Float::with_val(precision, MpfrConstant::Log2)
354    }
355
356    #[inline(always)]
357    fn raw_ln_10(precision: u32) -> Self {
358        // ln(10) = ln(2) * log₂(10)
359        // More efficient than computing ln(10) directly
360        let ln2 = rug::Float::with_val(precision, MpfrConstant::Log2);
361        let log2_10 = rug::Float::with_val(precision, 10).log2();
362        ln2 * log2_10
363    }
364
365    #[inline(always)]
366    fn raw_log10_2(precision: u32) -> Self {
367        rug::Float::with_val(precision, 2.).log10()
368    }
369
370    #[inline(always)]
371    fn raw_log2_10(precision: u32) -> Self {
372        rug::Float::with_val(precision, 10.).log2()
373    }
374
375    #[inline(always)]
376    fn raw_log2_e(precision: u32) -> Self {
377        // log₂(e) = 1/ln(2)
378        // More efficient than computing e then taking log₂
379        rug::Float::with_val(precision, MpfrConstant::Log2).recip()
380    }
381
382    #[inline(always)]
383    fn raw_log10_e(precision: u32) -> Self {
384        // log₁₀(e) = 1/ln(10) = 1/(ln(2) * log₂(10))
385        let ln2 = rug::Float::with_val(precision, MpfrConstant::Log2);
386        let log2_10 = rug::Float::with_val(precision, 10).log2();
387        (ln2 * log2_10).recip()
388    }
389
390    #[inline(always)]
391    fn raw_e(precision: u32) -> Self {
392        rug::Float::with_val(precision, 1.).exp()
393    }
394
395    #[inline(always)]
396    fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<rug::Float, usize>> {
397        if !self.is_finite() {
398            return Err(ErrorsRawRealToInteger::NotFinite {
399                value: self,
400                backtrace: capture_backtrace(),
401            });
402        }
403
404        let truncation = self.clone().trunc();
405
406        let out_of_range = || ErrorsRawRealToInteger::OutOfRange {
407            value: self,
408            min: usize::MIN,
409            max: usize::MAX,
410            backtrace: capture_backtrace(),
411        };
412
413        let truncation_as_rug_integer = truncation.to_integer().ok_or_else(out_of_range.clone())?;
414
415        truncation_as_rug_integer
416            .to_usize()
417            .ok_or_else(out_of_range)
418    }
419}
420
421impl RawScalarValidate for rug::Complex {
422    type ValidationError = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;
423
424    fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
425        let real_validation = self.real().validate_raw_scalar_ref();
426        let imag_validation = self.imag().validate_raw_scalar_ref();
427        match (real_validation, imag_validation) {
428            (Ok(()), Ok(())) => Ok(()),
429            (Ok(()), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
430                source: Box::new(imag_err),
431            }),
432            (Err(real_err), Ok(())) => Err(ErrorsValidationRawComplex::InvalidRealPart {
433                source: Box::new(real_err),
434            }),
435            (Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
436                real_error: Box::new(real_err),
437                imag_error: Box::new(imag_err),
438            }),
439        }
440    }
441}
442
443impl RawScalarTrait for rug::Complex {
444    fn raw_zero(precision: u32) -> Self {
445        rug::Complex::with_val(precision, (0., 0.))
446    }
447
448    fn is_zero(&self) -> bool {
449        rug::Complex::is_zero(self)
450    }
451
452    fn raw_one(precision: u32) -> Self {
453        rug::Complex::with_val(precision, (1., 0.))
454    }
455
456    #[duplicate_item(
457        unchecked_method       method;
458        [unchecked_reciprocal] [recip];
459        [unchecked_exp]        [exp];
460        [unchecked_sqrt]       [sqrt];
461        [unchecked_ln]         [ln];
462        [unchecked_log10]      [log10];
463    )]
464    #[inline(always)]
465    fn unchecked_method(self) -> Self {
466        rug::Complex::method(self)
467    }
468
469    #[inline(always)]
470    fn unchecked_log2(self) -> Self {
471        let ln_2 = rug::Float::with_val(self.real().prec(), 2.).ln();
472        rug::Complex::ln(self) / ln_2
473    }
474
475    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
476    ///
477    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
478    #[inline(always)]
479    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
480        rug::Complex::mul_add(self, b, c)
481    }
482
483    fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H) {
484        self.raw_real_part().compute_hash_raw_scalar(state);
485        self.raw_imag_part().compute_hash_raw_scalar(state);
486    }
487}
488
489impl Conjugate for rug::Complex {
490    #[inline(always)]
491    fn conjugate(self) -> Self {
492        rug::Complex::conj(self)
493    }
494}
495
496impl RawComplexTrait for rug::Complex {
497    type RawReal = rug::Float;
498
499    fn new_unchecked_raw_complex(real: rug::Float, imag: rug::Float) -> Self {
500        debug_assert_eq!(
501            real.prec(),
502            imag.prec(),
503            "Different precision between real and imaginary part!"
504        );
505        rug::Complex::with_val(real.prec(), (real, imag))
506    }
507
508    /// Returns a mutable reference to the real part of the complex number.
509    fn mut_raw_real_part(&mut self) -> &mut rug::Float {
510        self.mut_real()
511    }
512
513    /// Returns a mutable reference to the imaginary part of the complex number.
514    fn mut_raw_imag_part(&mut self) -> &mut rug::Float {
515        self.mut_imag()
516    }
517
518    #[inline(always)]
519    fn unchecked_abs(self) -> rug::Float {
520        rug::Complex::abs(self).into_real_imag().0
521    }
522
523    #[inline(always)]
524    fn raw_real_part(&self) -> &rug::Float {
525        self.real()
526    }
527
528    #[inline(always)]
529    fn raw_imag_part(&self) -> &rug::Float {
530        self.imag()
531    }
532
533    #[inline(always)]
534    fn unchecked_arg(self) -> rug::Float {
535        rug::Complex::arg(self).into_real_imag().0
536    }
537
538    #[inline(always)]
539    fn unchecked_pow_exponent_real(self, exponent: &rug::Float) -> Self {
540        rug::Complex::pow(self, exponent)
541    }
542}
543
544impl FpChecks for rug::Float {
545    fn is_finite(&self) -> bool {
546        rug::Float::is_finite(self)
547    }
548
549    fn is_infinite(&self) -> bool {
550        rug::Float::is_infinite(self)
551    }
552
553    fn is_nan(&self) -> bool {
554        rug::Float::is_nan(self)
555    }
556    fn is_normal(&self) -> bool {
557        rug::Float::is_normal(self)
558    }
559}
560
561impl FpChecks for rug::Complex {
562    /// Returns `true` if the real and imaginary parts of `self` are neither infinite nor NaN.
563    #[inline(always)]
564    fn is_finite(&self) -> bool {
565        self.real().is_finite() && self.imag().is_finite()
566    }
567
568    /// Returns `true` if the real or the imaginary part of `self` are positive infinity or negative infinity.
569    #[inline(always)]
570    fn is_infinite(&self) -> bool {
571        !self.is_nan() && (self.real().is_infinite() || self.imag().is_infinite())
572    }
573
574    /// Returns `true` if the real or the imaginary part of `self` are NaN.
575    #[inline(always)]
576    fn is_nan(&self) -> bool {
577        self.real().is_nan() || self.imag().is_nan()
578    }
579
580    /// Returns `true` if the real and the imaginary part of `self` are *normal* (i.e. neither zero, infinite, subnormal, or NaN).
581    #[inline(always)]
582    fn is_normal(&self) -> bool {
583        self.real().is_normal() && self.imag().is_normal()
584    }
585}
586
587//------------------------------------------------------------------------------------------------
588#[duplicate_item(
589    T;
590    [rug::Float];
591    [rug::Complex];
592)]
593/// Compound negation and assignment.
594impl NegAssign for T {
595    /// Performs the negation of `self`.
596    fn neg_assign(&mut self) {
597        <T as rug::ops::NegAssign>::neg_assign(self);
598    }
599}
600//------------------------------------------------------------------------------------------------
601
602impl Sign for rug::Float {
603    /// Returns a number with the magnitude of `self` and the sign of `sign`.
604    #[inline(always)]
605    fn kernel_copysign(self, sign: &Self) -> Self {
606        self.copysign(sign)
607    }
608
609    /// Returns `true` if the value is negative, −0 or NaN with a negative sign.
610    #[inline(always)]
611    fn kernel_is_sign_negative(&self) -> bool {
612        self.is_sign_negative()
613    }
614
615    /// Returns `true` if the value is positive, +0 or NaN with a positive sign.
616    #[inline(always)]
617    fn kernel_is_sign_positive(&self) -> bool {
618        self.is_sign_positive()
619    }
620
621    /// Returns the signum of the number.
622    ///
623    /// This method relies on `rug::Float::signum()`.
624    /// The behavior for special values is as follows:
625    /// - If the number is positive and not zero, returns `1.0`.
626    /// - If the number is negative and not zero, returns `-1.0`.
627    /// - If the number is zero, `rug::Float::signum()` returns `1.0` (representing positive zero).
628    #[inline(always)]
629    fn kernel_signum(self) -> Self {
630        self.signum()
631    }
632}
633
634impl Rounding for rug::Float {
635    /// Returns the smallest integer greater than or equal to `self`.
636    #[inline(always)]
637    fn kernel_ceil(self) -> Self {
638        self.ceil()
639    }
640
641    /// Returns the largest integer smaller than or equal to `self`.
642    #[inline(always)]
643    fn kernel_floor(self) -> Self {
644        self.floor()
645    }
646
647    /// Returns the fractional part of `self`.
648    #[inline(always)]
649    fn kernel_fract(self) -> Self {
650        self.fract()
651    }
652
653    /// Rounds `self` to the nearest integer, rounding half-way cases away from zero.
654    #[inline(always)]
655    fn kernel_round(self) -> Self {
656        self.round()
657    }
658
659    /// Returns the nearest integer to a number. Rounds half-way cases to the number with an even least significant digit.
660    ///
661    /// This function always returns the precise result.
662    ///
663    /// # Examples
664    /// ```
665    /// use num_valid::{RealScalar, RealRugStrictFinite, functions::Rounding};
666    ///
667    /// const PRECISION: u32 = 100;
668    ///
669    /// let f = RealRugStrictFinite::<PRECISION>::try_from_f64(3.3).unwrap();
670    /// let g = RealRugStrictFinite::<PRECISION>::try_from_f64(-3.3).unwrap();
671    /// let h = RealRugStrictFinite::<PRECISION>::try_from_f64(3.5).unwrap();
672    /// let i = RealRugStrictFinite::<PRECISION>::try_from_f64(-4.5).unwrap();
673    ///
674    /// assert_eq!(f.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
675    /// assert_eq!(g.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(-3.).unwrap());
676    /// assert_eq!(h.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(4.).unwrap());
677    /// assert_eq!(i.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(-4.).unwrap());
678    /// ```
679    #[inline(always)]
680    fn kernel_round_ties_even(self) -> Self {
681        self.round_even()
682    }
683
684    /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero.
685    ///    
686    /// # Examples
687    /// ```
688    /// use num_valid::{RealScalar, RealRugStrictFinite, functions::Rounding};
689    ///
690    /// const PRECISION: u32 = 100;
691    ///
692    /// let f = RealRugStrictFinite::<PRECISION>::try_from_f64(3.7).unwrap();
693    /// let g = RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap();
694    /// let h = RealRugStrictFinite::<PRECISION>::try_from_f64(-3.7).unwrap();
695    ///
696    /// assert_eq!(f.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
697    /// assert_eq!(g.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
698    /// assert_eq!(h.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(-3.).unwrap());
699    /// ```
700    #[inline(always)]
701    fn kernel_trunc(self) -> Self {
702        self.trunc()
703    }
704}