Coverage Report

Created: 2026-09-19 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/noisy_float-0.2.1/src/lib.rs
Line
Count
Source
1
// Copyright 2016-2021 Matthew D. Michelotti
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//   http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
//! This crate contains floating point types that panic if they are set
16
//! to an illegal value, such as NaN.
17
//!
18
//! The name "Noisy Float" comes from
19
//! the terms "quiet NaN" and "signaling NaN"; "signaling" was too long
20
//! to put in a struct/crate name, so "noisy" is used instead, being the opposite
21
//! of "quiet."
22
//!
23
//! The standard types defined in `noisy_float::types` follow the principle
24
//! demonstrated by Rust's handling of integer overflow:
25
//! a bad arithmetic operation is considered an error,
26
//! but it is too costly to check everywhere in optimized builds.
27
//! For each floating point number that is created, a `debug_assert!` invocation is used
28
//! to check if it is valid or not.
29
//! This way, there are guarantees when developing code that floating point
30
//! numbers have valid values,
31
//! but during a release run there is *no overhead* for using these floating
32
//! point types compared to using `f32` or `f64` directly.
33
//!
34
//! This crate makes use of the num, bounded, signed and floating point traits
35
//! in the popular `num_traits` crate.
36
//! This crate can be compiled with no_std.
37
//!
38
//! # Examples
39
//! An example using the `R64` type, which corresponds to *finite* `f64` values.
40
//!
41
//! ```
42
//! use noisy_float::prelude::*;
43
//!
44
//! fn geometric_mean(a: R64, b: R64) -> R64 {
45
//!     (a * b).sqrt() //used just like regular floating point numbers
46
//! }
47
//!
48
//! fn mean(a: R64, b: R64) -> R64 {
49
//!     (a + b) * 0.5 //the RHS of ops can be the underlying float type
50
//! }
51
//!
52
//! println!(
53
//!     "geometric_mean(10.0, 20.0) = {}",
54
//!     geometric_mean(r64(10.0), r64(20.0))
55
//! );
56
//! //prints 14.142...
57
//! assert!(mean(r64(10.0), r64(20.0)) == 15.0);
58
//! ```
59
//!
60
//! An example using the `N32` type, which corresponds to *non-NaN* `f32` values.
61
//! The float types in this crate are able to implement `Eq` and `Ord` properly,
62
//! since NaN is not allowed.
63
//!
64
//! ```
65
//! use noisy_float::prelude::*;
66
//!
67
//! let values = vec![n32(3.0), n32(-1.5), n32(71.3), N32::infinity()];
68
//! assert!(values.iter().cloned().min() == Some(n32(-1.5)));
69
//! assert!(values.iter().cloned().max() == Some(N32::infinity()));
70
//! ```
71
//!
72
//! An example converting from R64 to primitive types.
73
//!
74
//! ```
75
//! use noisy_float::prelude::*;
76
//! use num_traits::cast::ToPrimitive;
77
//!
78
//! let value_r64: R64 = r64(1.0);
79
//! let value_f64_a: f64 = value_r64.into();
80
//! let value_f64_b: f64 = value_r64.raw();
81
//! let value_u64: u64 = value_r64.to_u64().unwrap();
82
//!
83
//! assert!(value_f64_a == value_f64_b);
84
//! assert!(value_f64_a as u64 == value_u64);
85
//! ```
86
//!
87
//! # Features
88
//!
89
//! This crate has the following cargo features:
90
//!
91
//! - `serde`: Enable serialization for all `NoisyFloats` using serde 1.0 and
92
//!   will transparently serialize then as floats
93
//! - `approx`: Adds implementations to use `NoisyFloat` with the `approx`
94
//!   crate
95
96
#![no_std]
97
98
#[cfg(feature = "serde")]
99
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
100
101
pub mod checkers;
102
mod float_impl;
103
pub mod types;
104
105
/// Prelude for the `noisy_float` crate.
106
///
107
/// This includes all of the types defined in the `noisy_float::types` module,
108
/// as well as a re-export of the `Float` trait from the `num_traits` crate.
109
/// It is important to have this re-export here, because it allows the user
110
/// to access common floating point methods like `abs()`, `sqrt()`, etc.
111
pub mod prelude {
112
    pub use crate::types::*;
113
114
    #[doc(no_inline)]
115
    pub use num_traits::Float;
116
}
117
118
use core::{fmt, marker::PhantomData};
119
use num_traits::Float;
120
121
/// Trait for checking whether a floating point number is *valid*.
122
///
123
/// The implementation defines its own criteria for what constitutes a *valid* value.
124
pub trait FloatChecker<F> {
125
    /// Returns `true` if (and only if) the given floating point number is *valid*
126
    /// according to this checker's criteria.
127
    ///
128
    /// The only hard requirement is that NaN *must* be considered *invalid*
129
    /// for all implementations of `FloatChecker`.
130
    fn check(value: F) -> bool;
131
132
    /// A function that may panic if the floating point number is *invalid*.
133
    ///
134
    /// Should either call `assert!(check(value), ...)` or `debug_assert!(check(value), ...)`.
135
    fn assert(value: F);
136
}
137
138
/// A floating point number with a restricted set of legal values.
139
///
140
/// Typical users will not need to access this struct directly, but
141
/// can instead use the type aliases found in the module `noisy_float::types`.
142
/// However, this struct together with a `FloatChecker` implementation can be used
143
/// to define custom behavior.
144
///
145
/// The underlying float type is `F`, usually `f32` or `f64`.
146
/// Valid values for the float are determined by the float checker `C`.
147
/// If an invalid value would ever be returned from a method on this type,
148
/// the method will panic instead, using either `assert!` or `debug_assert!`
149
/// as defined by the float checker.
150
/// The exception to this rule is for methods that return an `Option` containing
151
/// a `NoisyFloat`, in which case the result would be `None` if the value is invalid.
152
#[repr(transparent)]
153
pub struct NoisyFloat<F: Float, C: FloatChecker<F>> {
154
    value: F,
155
    checker: PhantomData<C>,
156
}
157
158
impl<F: Float, C: FloatChecker<F>> NoisyFloat<F, C> {
159
    /// Constructs a `NoisyFloat` with the given value.
160
    ///
161
    /// Uses the `FloatChecker` to assert that the value is valid.
162
    #[inline]
163
    #[track_caller]
164
0
    pub fn new(value: F) -> Self {
165
0
        C::assert(value);
166
0
        Self::unchecked_new_generic(value)
167
0
    }
168
169
    #[inline]
170
0
    fn unchecked_new_generic(value: F) -> Self {
171
0
        NoisyFloat {
172
0
            value,
173
0
            checker: PhantomData,
174
0
        }
175
0
    }
176
177
    /// Tries to construct a `NoisyFloat` with the given value.
178
    ///
179
    /// Returns `None` if the value is invalid.
180
    #[inline]
181
    pub fn try_new(value: F) -> Option<Self> {
182
        if C::check(value) {
183
            Some(NoisyFloat {
184
                value,
185
                checker: PhantomData,
186
            })
187
        } else {
188
            None
189
        }
190
    }
191
192
    /// Converts the value in-place to a reference to a `NoisyFloat`.
193
    ///
194
    /// Uses the `FloatChecker` to assert that the value is valid.
195
    #[inline]
196
    #[track_caller]
197
    pub fn borrowed(value: &F) -> &Self {
198
        C::assert(*value);
199
        Self::unchecked_borrowed(value)
200
    }
201
202
    #[inline]
203
0
    fn unchecked_borrowed(value: &F) -> &Self {
204
        // This is safe because `NoisyFloat` is a thin wrapper around the
205
        // floating-point type.
206
0
        unsafe { &*(value as *const F as *const Self) }
207
0
    }
Unexecuted instantiation: <noisy_float::NoisyFloat<f64, noisy_float::checkers::NumChecker>>::unchecked_borrowed
Unexecuted instantiation: <noisy_float::NoisyFloat<f32, noisy_float::checkers::NumChecker>>::unchecked_borrowed
208
209
    /// Tries to convert the value in-place to a reference to a `NoisyFloat`.
210
    ///
211
    /// Returns `None` if the value is invalid.
212
    #[inline]
213
0
    pub fn try_borrowed(value: &F) -> Option<&Self> {
214
0
        if C::check(*value) {
215
0
            Some(Self::unchecked_borrowed(value))
216
        } else {
217
0
            None
218
        }
219
0
    }
Unexecuted instantiation: <noisy_float::NoisyFloat<f64, noisy_float::checkers::NumChecker>>::try_borrowed
Unexecuted instantiation: <noisy_float::NoisyFloat<f32, noisy_float::checkers::NumChecker>>::try_borrowed
220
221
    /// Converts the value in-place to a mutable reference to a `NoisyFloat`.
222
    ///
223
    /// Uses the `FloatChecker` to assert that the value is valid.
224
    #[inline]
225
    #[track_caller]
226
    pub fn borrowed_mut(value: &mut F) -> &mut Self {
227
        C::assert(*value);
228
        Self::unchecked_borrowed_mut(value)
229
    }
230
231
    #[inline]
232
    fn unchecked_borrowed_mut(value: &mut F) -> &mut Self {
233
        // This is safe because `NoisyFloat` is a thin wrapper around the
234
        // floating-point type.
235
        unsafe { &mut *(value as *mut F as *mut Self) }
236
    }
237
238
    /// Tries to convert the value in-place to a mutable reference to a `NoisyFloat`.
239
    ///
240
    /// Returns `None` if the value is invalid.
241
    #[inline]
242
    pub fn try_borrowed_mut(value: &mut F) -> Option<&mut Self> {
243
        if C::check(*value) {
244
            Some(Self::unchecked_borrowed_mut(value))
245
        } else {
246
            None
247
        }
248
    }
249
250
    /// Constructs a `NoisyFloat` with the given `f32` value.
251
    ///
252
    /// May panic not only by the `FloatChecker` but also
253
    /// by unwrapping the result of a `NumCast` invocation for type `F`,
254
    /// although the later should not occur in normal situations.
255
    #[inline]
256
    #[track_caller]
257
    pub fn from_f32(value: f32) -> Self {
258
        Self::new(F::from(value).unwrap())
259
    }
260
261
    /// Constructs a `NoisyFloat` with the given `f64` value.
262
    ///
263
    /// May panic not only by the `FloatChecker` but also
264
    /// by unwrapping the result of a `NumCast` invocation for type `F`,
265
    /// although the later should not occur in normal situations.
266
    #[inline]
267
    #[track_caller]
268
    pub fn from_f64(value: f64) -> Self {
269
        Self::new(F::from(value).unwrap())
270
    }
271
272
    /// Returns the underlying float value.
273
    #[inline]
274
0
    pub fn raw(self) -> F {
275
0
        self.value
276
0
    }
Unexecuted instantiation: <noisy_float::NoisyFloat<f64, noisy_float::checkers::NumChecker>>::raw
Unexecuted instantiation: <noisy_float::NoisyFloat<f32, noisy_float::checkers::NumChecker>>::raw
277
278
    /// Compares and returns the minimum of two values.
279
    ///
280
    /// This method exists to disambiguate between `num_traits::Float.min` and `std::cmp::Ord.min`.
281
    #[inline]
282
    pub fn min(self, other: Self) -> Self {
283
        Ord::min(self, other)
284
    }
285
286
    /// Compares and returns the maximum of two values.
287
    ///
288
    /// This method exists to disambiguate between `num_traits::Float.max` and `std::cmp::Ord.max`.
289
    #[inline]
290
    pub fn max(self, other: Self) -> Self {
291
        Ord::max(self, other)
292
    }
293
}
294
295
impl<F: Float + Default, C: FloatChecker<F>> Default for NoisyFloat<F, C> {
296
    #[inline]
297
    #[track_caller]
298
    fn default() -> Self {
299
        Self::new(F::default())
300
    }
301
}
302
303
impl<F: Float + fmt::Debug, C: FloatChecker<F>> fmt::Debug for NoisyFloat<F, C> {
304
    #[inline]
305
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
306
        fmt::Debug::fmt(&self.value, f)
307
    }
308
}
309
310
impl<F: Float + fmt::Display, C: FloatChecker<F>> fmt::Display for NoisyFloat<F, C> {
311
    #[inline]
312
0
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
313
0
        fmt::Display::fmt(&self.value, f)
314
0
    }
315
}
316
317
impl<F: Float + fmt::LowerExp, C: FloatChecker<F>> fmt::LowerExp for NoisyFloat<F, C> {
318
    #[inline]
319
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
320
        fmt::LowerExp::fmt(&self.value, f)
321
    }
322
}
323
324
impl<F: Float + fmt::UpperExp, C: FloatChecker<F>> fmt::UpperExp for NoisyFloat<F, C> {
325
    #[inline]
326
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
327
        fmt::UpperExp::fmt(&self.value, f)
328
    }
329
}
330
331
#[cfg(feature = "serde")]
332
impl<F: Float + Serialize, C: FloatChecker<F>> Serialize for NoisyFloat<F, C> {
333
    fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
334
        self.value.serialize(ser)
335
    }
336
}
337
338
#[cfg(feature = "serde")]
339
impl<'de, F: Float + Deserialize<'de>, C: FloatChecker<F>> Deserialize<'de> for NoisyFloat<F, C> {
340
    fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
341
        let value = F::deserialize(de)?;
342
        Self::try_new(value).ok_or_else(|| D::Error::custom("invalid NoisyFloat"))
343
    }
344
}
345
346
#[cfg(test)]
347
mod tests {
348
    extern crate std;
349
    use std::prelude::v1::*;
350
351
    use crate::prelude::*;
352
    #[cfg(feature = "serde")]
353
    use serde_derive::{Deserialize, Serialize};
354
    #[cfg(feature = "serde")]
355
    use serde_json;
356
    use std::{
357
        f32,
358
        f64::{self, consts},
359
        hash::{Hash, Hasher},
360
        mem::{align_of, size_of},
361
    };
362
363
    #[test]
364
    fn smoke_test() {
365
        assert_eq!(n64(1.0) + 2.0, 3.0);
366
        assert_ne!(n64(3.0), n64(2.9));
367
        assert!(r64(1.0) < 2.0);
368
        let mut value = n64(18.0);
369
        value %= n64(5.0);
370
        assert_eq!(-value, n64(-3.0));
371
        assert_eq!(r64(1.0).exp(), consts::E);
372
        assert_eq!((N64::try_new(1.0).unwrap() / N64::infinity()), 0.0);
373
        assert_eq!(N64::from_f32(f32::INFINITY), N64::from_f64(f64::INFINITY));
374
        assert_eq!(R64::try_new(f64::NEG_INFINITY), None);
375
        assert_eq!(N64::try_new(f64::NAN), None);
376
        assert_eq!(R64::try_new(f64::NAN), None);
377
        assert_eq!(N64::try_borrowed(&f64::NAN), None);
378
        let mut nan = f64::NAN;
379
        assert_eq!(N64::try_borrowed_mut(&mut nan), None);
380
    }
381
382
    #[test]
383
    fn ensure_layout() {
384
        assert_eq!(size_of::<N32>(), size_of::<f32>());
385
        assert_eq!(align_of::<N32>(), align_of::<f32>());
386
387
        assert_eq!(size_of::<N64>(), size_of::<f64>());
388
        assert_eq!(align_of::<N64>(), align_of::<f64>());
389
    }
390
391
    #[test]
392
    fn borrowed_casts() {
393
        assert_eq!(R64::borrowed(&3.12), &3.12);
394
        assert_eq!(N64::borrowed(&[f64::INFINITY; 2][0]), &f64::INFINITY);
395
        assert_eq!(N64::borrowed_mut(&mut 2.72), &mut 2.72);
396
    }
397
398
    #[test]
399
    fn test_convert() {
400
        assert_eq!(f32::from(r32(3.0)), 3.0f32);
401
        assert_eq!(f64::from(r32(5.0)), 5.0f64);
402
        assert_eq!(f64::from(r64(7.0)), 7.0f64);
403
    }
404
405
    #[test]
406
    #[cfg(debug_assertions)]
407
    #[should_panic]
408
    fn n64_nan() {
409
        let _ = n64(0.0) / n64(0.0);
410
    }
411
412
    #[test]
413
    #[cfg(debug_assertions)]
414
    #[should_panic]
415
    fn r64_nan() {
416
        let _ = r64(0.0) / r64(0.0);
417
    }
418
419
    #[test]
420
    #[cfg(debug_assertions)]
421
    #[should_panic]
422
    fn r64_infinity() {
423
        let _ = r64(1.0) / r64(0.0);
424
    }
425
426
    #[test]
427
    fn resolves_min_max() {
428
        assert_eq!(r64(1.0).min(r64(3.0)), r64(1.0));
429
        assert_eq!(r64(1.0).max(r64(3.0)), r64(3.0));
430
    }
431
432
    #[test]
433
    fn epsilon() {
434
        assert_eq!(R32::epsilon(), f32::EPSILON);
435
        assert_eq!(R64::epsilon(), f64::EPSILON);
436
    }
437
438
    #[test]
439
    fn test_try_into() {
440
        use std::convert::{TryFrom, TryInto};
441
        let _: R64 = 1.0.try_into().unwrap();
442
        let _ = R64::try_from(f64::INFINITY).unwrap_err();
443
    }
444
445
    struct TestHasher {
446
        bytes: Vec<u8>,
447
    }
448
449
    impl Hasher for TestHasher {
450
        fn finish(&self) -> u64 {
451
            panic!("unexpected Hasher.finish invocation")
452
        }
453
        fn write(&mut self, bytes: &[u8]) {
454
            self.bytes.extend_from_slice(bytes)
455
        }
456
    }
457
458
    fn hash_bytes<T: Hash>(value: T) -> Vec<u8> {
459
        let mut hasher = TestHasher { bytes: Vec::new() };
460
        value.hash(&mut hasher);
461
        hasher.bytes
462
    }
463
464
    #[test]
465
    fn test_hash() {
466
        assert_eq!(hash_bytes(r64(10.3)), hash_bytes(10.3f64.to_bits()));
467
        assert_ne!(hash_bytes(r64(10.3)), hash_bytes(10.4f64.to_bits()));
468
        assert_eq!(hash_bytes(r32(10.3)), hash_bytes(10.3f32.to_bits()));
469
        assert_ne!(hash_bytes(r32(10.3)), hash_bytes(10.4f32.to_bits()));
470
471
        assert_eq!(
472
            hash_bytes(N64::infinity()),
473
            hash_bytes(f64::INFINITY.to_bits())
474
        );
475
        assert_eq!(
476
            hash_bytes(N64::neg_infinity()),
477
            hash_bytes(f64::NEG_INFINITY.to_bits())
478
        );
479
480
        // positive and negative zero should have the same hashes
481
        assert_eq!(hash_bytes(r64(0.0)), hash_bytes(0.0f64.to_bits()));
482
        assert_eq!(hash_bytes(r64(-0.0)), hash_bytes(0.0f64.to_bits()));
483
        assert_eq!(hash_bytes(r32(0.0)), hash_bytes(0.0f32.to_bits()));
484
        assert_eq!(hash_bytes(r32(-0.0)), hash_bytes(0.0f32.to_bits()));
485
    }
486
487
    #[cfg(feature = "serde")]
488
    #[test]
489
    fn serialize_transparently_as_float() {
490
        let num = R32::new(3.14);
491
        let should_be = "3.14";
492
493
        let got = serde_json::to_string(&num).unwrap();
494
        assert_eq!(got, should_be);
495
    }
496
497
    #[cfg(feature = "serde")]
498
    #[test]
499
    fn deserialize_transparently_as_float() {
500
        let src = "3.14";
501
        let should_be = R32::new(3.14);
502
503
        let got: R32 = serde_json::from_str(src).unwrap();
504
        assert_eq!(got, should_be);
505
    }
506
507
    #[cfg(feature = "serde")]
508
    #[test]
509
    fn deserialize_invalid_float() {
510
        use crate::{FloatChecker, NoisyFloat};
511
        struct PositiveChecker;
512
        impl FloatChecker<f64> for PositiveChecker {
513
            fn check(value: f64) -> bool {
514
                value > 0.
515
            }
516
            fn assert(value: f64) {
517
                debug_assert!(Self::check(value))
518
            }
519
        }
520
521
        let src = "-1.0";
522
        let got: Result<NoisyFloat<f64, PositiveChecker>, _> = serde_json::from_str(src);
523
        assert!(got.is_err());
524
    }
525
526
    // Make sure you can use serde_derive with noisy floats.
527
    #[cfg(feature = "serde")]
528
    #[derive(Debug, PartialEq, Serialize, Deserialize)]
529
    struct Dummy {
530
        value: N64,
531
    }
532
533
    #[cfg(feature = "serde")]
534
    #[test]
535
    fn deserialize_struct_containing_n64() {
536
        let src = r#"{ "value": 3.14 }"#;
537
        let should_be = Dummy { value: n64(3.14) };
538
539
        let got: Dummy = serde_json::from_str(src).unwrap();
540
        assert_eq!(got, should_be);
541
    }
542
543
    #[cfg(feature = "serde")]
544
    #[test]
545
    fn serialize_struct_containing_n64() {
546
        let src = Dummy { value: n64(3.14) };
547
        let should_be = r#"{"value":3.14}"#;
548
549
        let got = serde_json::to_string(&src).unwrap();
550
        assert_eq!(got, should_be);
551
    }
552
553
    #[cfg(feature = "approx")]
554
    #[test]
555
    fn approx_assert_eq() {
556
        use approx::{assert_abs_diff_eq, assert_relative_eq, assert_ulps_eq};
557
558
        let lhs = r64(0.1000000000000001);
559
        let rhs = r64(0.1);
560
561
        assert_abs_diff_eq!(lhs, rhs);
562
        assert_relative_eq!(lhs, rhs);
563
        assert_ulps_eq!(lhs, rhs);
564
    }
565
566
    #[test]
567
    fn const_functions() {
568
        const A: N32 = N32::unchecked_new(1.0);
569
        const B: N64 = N64::unchecked_new(2.0);
570
        const C: R32 = R32::unchecked_new(3.0);
571
        const D: R64 = R64::unchecked_new(4.0);
572
573
        const A_RAW: f32 = A.const_raw();
574
        const B_RAW: f64 = B.const_raw();
575
        const C_RAW: f32 = C.const_raw();
576
        const D_RAW: f64 = D.const_raw();
577
578
        assert_eq!(A_RAW, 1.0);
579
        assert_eq!(B_RAW, 2.0);
580
        assert_eq!(C_RAW, 3.0);
581
        assert_eq!(D_RAW, 4.0);
582
    }
583
}