feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+1
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"bd9053fd5d23ea0d8edac0afe4e12d034a5e6207d80245be8e8f8fa6f9abb80a",".travis.yml":"80a9791cd3fbc359c1acafd79f867edfa2ce3852c3264ff26c93a975070fdb5b","Cargo.toml":"83c7f1956ce6ca5e4d64cc3d403e4288bb22d1bcff7155f056fe45d3f57fd315","Cargo.toml.orig":"c8ec09b5319aaa0d3e1766a991a2d451eb95e59510fb912ca6bfc1f2a79dee6b","LICENSE":"40be1e77825d7e49485a2e43d89bed29dfff29f8f529e71d3c683656021f0d08","README.md":"bed2395efa694cb1d74092e5614125ba4668201f0c653fe71d0fd567057e5c9b","src/eq.rs":"63978ad73d7144c3371491fd459d7e2ce9bdb672d121d9cb900556c5efd4cf92","src/lib.rs":"0be2c16ab1ca344bf8448e983e91b3ae41507d2e90e390433832ea83fdafb404","src/macros.rs":"893f5e5ffc6558ab3f8e7e381e49c8fb97660a51b42c9cdf3bc7d15c6ce2bd87","src/ratio.rs":"ec6c8338c1c45d49b9df2748069dba883105730a7bb6542dfeaac1b778c16feb","src/ulps.rs":"15528c3b0007b9f8b588f9113040973e819adf6245fa99da1497ec153e62c7a0","src/ulps_eq.rs":"87ad38eb13c31304962b29511d6f8215ab0b9f3b2b51545844678f81f83fb688"},"package":"98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "4844daf1ad47c9363a96ba8954d3c30d6a008591"
|
||||
}
|
||||
}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
language: rust
|
||||
rust: stable
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
authors = ["Mike Dilger <mike@mikedilger.com>"]
|
||||
description = "Floating point approximate comparison traits"
|
||||
documentation = "https://docs.rs/float-cmp"
|
||||
readme = "README.md"
|
||||
keywords = ["float", "comparison", "fuzzy", "approximate", "no_std"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/mikedilger/float-cmp"
|
||||
|
||||
[lib]
|
||||
name = "float_cmp"
|
||||
path = "src/lib.rs"
|
||||
test = true
|
||||
doctest = true
|
||||
doc = true
|
||||
[dependencies.num-traits]
|
||||
version = "0.2.1"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["ratio"]
|
||||
ratio = ["num-traits"]
|
||||
std = []
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2014-2020 Optimal Computing (NZ) Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
Vendored
+148
@@ -0,0 +1,148 @@
|
||||
# float-cmp
|
||||
|
||||
[](https://travis-ci.org/mikedilger/float-cmp)
|
||||
[](./LICENSE)
|
||||
|
||||
Documentation is available at https://docs.rs/float-cmp
|
||||
|
||||
float-cmp defines and implements traits for approximate comparison of floating point types
|
||||
which have fallen away from exact equality due to the limited precision available within
|
||||
floating point representations. Implementations of these traits are provided for `f32`
|
||||
and `f64` types.
|
||||
|
||||
When I was a kid in the '80s, the programming rule was "Never compare floating point
|
||||
numbers". If you can follow that rule and still get the outcome you desire, then more
|
||||
power to you. However, if you really do need to compare them, this crate provides a
|
||||
reasonable way to do so.
|
||||
|
||||
Another crate `efloat` offers another solution by providing a floating point type that
|
||||
tracks its error bounds as operations are performed on it, and thus can implement the
|
||||
`ApproxEq` trait in this crate more accurately, without specifying a `Margin`.
|
||||
|
||||
The recommended go-to solution (although it may not be appropriate in all cases) is the
|
||||
`approx_eq()` function in the `ApproxEq` trait (or better yet, the macros). For `f32`
|
||||
and `f64`, the `F32Margin` and `F64Margin` types are provided for specifying margins as
|
||||
both an epsilon value and an ULPs value, and defaults are provided via `Default`
|
||||
(although there is no perfect default value that is always appropriate, so beware).
|
||||
|
||||
Several other traits are provided including `Ulps`, `ApproxEqUlps`, `ApproxOrdUlps`, and
|
||||
`ApproxEqRatio`.
|
||||
|
||||
## The problem
|
||||
|
||||
Floating point operations must round answers to the nearest representable number. Multiple
|
||||
operations may result in an answer different from what you expect. In the following example,
|
||||
the assert will fail, even though the printed output says "0.45 == 0.45":
|
||||
|
||||
```rust
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
println!("{} == {}", a, b);
|
||||
assert!(a==b) // Fails, because they are not exactly equal
|
||||
```
|
||||
|
||||
This fails because the correct answer to most operations isn't exactly representable, and so
|
||||
your computer's processor chooses to represent the answer with the closest value it has
|
||||
available. This introduces error, and this error can accumulate as multiple operations are
|
||||
performed.
|
||||
|
||||
## The solution
|
||||
|
||||
With `ApproxEq`, we can get the answer we intend:
|
||||
```rust
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
println!("{} == {}", a, b);
|
||||
assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
```
|
||||
|
||||
## Some explanation
|
||||
|
||||
We use the term ULP (units of least precision, or units in the last place) to mean the
|
||||
difference between two adjacent floating point representations (adjacent meaning that there is
|
||||
no floating point number between them). This term is borrowed from prior work (personally I
|
||||
would have chosen "quanta"). The size of an ULP (measured as a float) varies
|
||||
depending on the exponents of the floating point numbers in question. That is a good thing,
|
||||
because as numbers fall away from equality due to the imprecise nature of their representation,
|
||||
they fall away in ULPs terms, not in absolute terms. Pure epsilon-based comparisons are
|
||||
absolute and thus don't map well to the nature of the additive error issue. They work fine
|
||||
for many ranges of numbers, but not for others (consider comparing -0.0000000028
|
||||
to +0.00000097).
|
||||
|
||||
## Using this crate
|
||||
|
||||
By default this crate enables the `ratio` module providing the `ApproxEqRatio` trait. This
|
||||
feature pulls in `num-traits`. If you disable this feature, you'll need to either enable
|
||||
`num-traits` directly or else enable the `std` feature; otherwise it won't compile. This crate
|
||||
is `#![no_std]` unless you enable the `std` feature.
|
||||
|
||||
You can use the `ApproxEq` trait directly like so:
|
||||
|
||||
```rust
|
||||
assert!( a.approx_eq(b, F32Margin { ulps: 2, epsilon: 0.0 }) );
|
||||
```
|
||||
|
||||
We have implemented `From<(f32,i32)>` for `F32Margin` (and similarly for `F64Margin`)
|
||||
so you can use this shorthand:
|
||||
|
||||
```rust
|
||||
assert!( a.approx_eq(b, (0.0, 2)) );
|
||||
```
|
||||
|
||||
With macros, it is easier to be explicit about which type of margin you wish to set,
|
||||
without mentioning the other one (the other one will be zero). But the downside is
|
||||
that you have to specify the type you are dealing with:
|
||||
|
||||
```rust
|
||||
assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) );
|
||||
assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) );
|
||||
assert!( approx_eq!(f32, a, b, (0.0, 2)) );
|
||||
assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) );
|
||||
assert!( approx_eq!(f32, a, b, F32Margin::default()) );
|
||||
assert!( approx_eq!(f32, a, b) ); // uses the default
|
||||
```
|
||||
|
||||
For most cases, I recommend you use a smallish integer for the `ulps` parameter (1 to 5
|
||||
or so), and a similar small multiple of the floating point's EPSILON constant (1.0 to 5.0
|
||||
or so), but there are *plenty* of cases where this is insufficient.
|
||||
|
||||
## Implementing these traits
|
||||
|
||||
You can implement `ApproxEq` for your own complex types like shown below.
|
||||
The floating point type `F` must be `Copy`, but for large types you can implement
|
||||
it for references to your type as shown.
|
||||
|
||||
```rust
|
||||
use float_cmp::ApproxEq;
|
||||
|
||||
pub struct Vec2<F> {
|
||||
pub x: F,
|
||||
pub y: F,
|
||||
}
|
||||
|
||||
impl<'a, M: Copy, F: Copy + ApproxEq<Margin=M>> ApproxEq for &'a Vec2<F> {
|
||||
type Margin = M;
|
||||
|
||||
fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
|
||||
let margin = margin.into();
|
||||
self.x.approx_eq(other.x, margin)
|
||||
&& self.y.approx_eq(other.y, margin)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Non floating-point types
|
||||
|
||||
`ApproxEq` can be implemented for non floating-point types as well, since `Margin` is
|
||||
an associated type.
|
||||
|
||||
The `efloat` crate implements (or soon will implement) `ApproxEq` for a compound type
|
||||
that tracks floating point error bounds by checking if the error bounds overlap.
|
||||
In that case `type Margin = ()`.
|
||||
|
||||
## Inspiration
|
||||
|
||||
This crate was inspired by this Random ASCII blog post:
|
||||
|
||||
[https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
|
||||
Vendored
+304
@@ -0,0 +1,304 @@
|
||||
// Copyright 2014-2020 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
use core::{f32, f64};
|
||||
#[cfg(feature = "num-traits")]
|
||||
#[allow(unused_imports)]
|
||||
use num_traits::float::FloatCore;
|
||||
use super::Ulps;
|
||||
|
||||
/// A trait for approximate equality comparisons.
|
||||
pub trait ApproxEq: Sized {
|
||||
/// This type type defines a margin within which two values are to be
|
||||
/// considered approximately equal. It must implement `Default` so that
|
||||
/// `approx_eq()` can be called on unknown types.
|
||||
type Margin: Copy + Default;
|
||||
|
||||
/// This method tests that the `self` and `other` values are equal within `margin`
|
||||
/// of each other.
|
||||
fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool;
|
||||
|
||||
/// This method tests that the `self` and `other` values are not within `margin`
|
||||
/// of each other.
|
||||
fn approx_ne<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
|
||||
!self.approx_eq(other, margin)
|
||||
}
|
||||
}
|
||||
|
||||
/// This type defines a margin within two `f32` values might be considered equal,
|
||||
/// and is intended as the associated type for the `ApproxEq` trait.
|
||||
///
|
||||
/// Two tests are used to determine approximate equality.
|
||||
///
|
||||
/// The first test considers two values approximately equal if they differ by <=
|
||||
/// `epsilon`. This will only succeed for very small numbers. Note that it may
|
||||
/// succeed even if the parameters are of differing signs, straddling zero.
|
||||
///
|
||||
/// The second test considers how many ULPs (units of least precision, units in
|
||||
/// the last place, which is the integer number of floating-point representations
|
||||
/// that the parameters are separated by) different the parameters are and considers
|
||||
/// them approximately equal if this is <= `ulps`. For large floating-point numbers,
|
||||
/// an ULP can be a rather large gap, but this kind of comparison is necessary
|
||||
/// because floating-point operations must round to the nearest representable value
|
||||
/// and so larger floating-point values accumulate larger errors.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct F32Margin {
|
||||
pub epsilon: f32,
|
||||
pub ulps: i32
|
||||
}
|
||||
impl Default for F32Margin {
|
||||
#[inline]
|
||||
fn default() -> F32Margin {
|
||||
F32Margin {
|
||||
epsilon: f32::EPSILON,
|
||||
ulps: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
impl F32Margin {
|
||||
#[inline]
|
||||
pub fn zero() -> F32Margin {
|
||||
F32Margin {
|
||||
epsilon: 0.0,
|
||||
ulps: 0
|
||||
}
|
||||
}
|
||||
pub fn epsilon(self, epsilon: f32) -> Self {
|
||||
F32Margin {
|
||||
epsilon,
|
||||
..self
|
||||
}
|
||||
}
|
||||
pub fn ulps(self, ulps: i32) -> Self {
|
||||
F32Margin {
|
||||
ulps,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<(f32, i32)> for F32Margin {
|
||||
fn from(m: (f32, i32)) -> F32Margin {
|
||||
F32Margin {
|
||||
epsilon: m.0,
|
||||
ulps: m.1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApproxEq for f32 {
|
||||
type Margin = F32Margin;
|
||||
|
||||
fn approx_eq<M: Into<Self::Margin>>(self, other: f32, margin: M) -> bool {
|
||||
let margin = margin.into();
|
||||
|
||||
// Check for exact equality first. This is often true, and so we get the
|
||||
// performance benefit of only doing one compare in most cases.
|
||||
self==other ||
|
||||
|
||||
// Perform epsilon comparison next
|
||||
((self - other).abs() <= margin.epsilon) ||
|
||||
|
||||
{
|
||||
// Perform ulps comparion last
|
||||
let diff: i32 = self.ulps(&other);
|
||||
saturating_abs_i32!(diff) <= margin.ulps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_test1() {
|
||||
let f: f32 = 0.0_f32;
|
||||
let g: f32 = -0.0000000000000005551115123125783_f32;
|
||||
assert!(f != g); // Should not be directly equal
|
||||
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test2() {
|
||||
let f: f32 = 0.0_f32;
|
||||
let g: f32 = -0.0_f32;
|
||||
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test3() {
|
||||
let f: f32 = 0.0_f32;
|
||||
let g: f32 = 0.00000000000000001_f32;
|
||||
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test4() {
|
||||
let f: f32 = 0.00001_f32;
|
||||
let g: f32 = 0.00000000000000001_f32;
|
||||
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test5() {
|
||||
let f: f32 = 0.1_f32;
|
||||
let mut sum: f32 = 0.0_f32;
|
||||
for _ in 0_isize..10_isize { sum += f; }
|
||||
let product: f32 = f * 10.0_f32;
|
||||
assert!(sum != product); // Should not be directly equal:
|
||||
assert!(sum.approx_eq(product, (f32::EPSILON, 1)) == true);
|
||||
assert!(sum.approx_eq(product, F32Margin::zero()) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test6() {
|
||||
let x: f32 = 1000000_f32;
|
||||
let y: f32 = 1000000.1_f32;
|
||||
assert!(x != y); // Should not be directly equal
|
||||
assert!(x.approx_eq(y, (0.0, 2)) == true); // 2 ulps does it
|
||||
// epsilon method no good here:
|
||||
assert!(x.approx_eq(y, (1000.0 * f32::EPSILON, 0)) == false);
|
||||
}
|
||||
|
||||
/// This type defines a margin within two `f64` values might be considered equal,
|
||||
/// and is intended as the associated type for the `ApproxEq` trait.
|
||||
///
|
||||
/// Two tests are used to determine approximate equality.
|
||||
///
|
||||
/// The first test considers two values approximately equal if they differ by <=
|
||||
/// `epsilon`. This will only succeed for very small numbers. Note that it may
|
||||
/// succeed even if the parameters are of differing signs, straddling zero.
|
||||
///
|
||||
/// The second test considers how many ULPs (units of least precision, units in
|
||||
/// the last place, which is the integer number of floating-point representations
|
||||
/// that the parameters are separated by) different the parameters are and considers
|
||||
/// them approximately equal if this is <= `ulps`. For large floating-point numbers,
|
||||
/// an ULP can be a rather large gap, but this kind of comparison is necessary
|
||||
/// because floating-point operations must round to the nearest representable value
|
||||
/// and so larger floating-point values accumulate larger errors.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct F64Margin {
|
||||
pub epsilon: f64,
|
||||
pub ulps: i64
|
||||
}
|
||||
impl Default for F64Margin {
|
||||
#[inline]
|
||||
fn default() -> F64Margin {
|
||||
F64Margin {
|
||||
epsilon: f64::EPSILON,
|
||||
ulps: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
impl F64Margin {
|
||||
#[inline]
|
||||
pub fn zero() -> F64Margin {
|
||||
F64Margin {
|
||||
epsilon: 0.0,
|
||||
ulps: 0
|
||||
}
|
||||
}
|
||||
pub fn epsilon(self, epsilon: f64) -> Self {
|
||||
F64Margin {
|
||||
epsilon,
|
||||
..self
|
||||
}
|
||||
}
|
||||
pub fn ulps(self, ulps: i64) -> Self {
|
||||
F64Margin {
|
||||
ulps,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<(f64, i64)> for F64Margin {
|
||||
fn from(m: (f64, i64)) -> F64Margin {
|
||||
F64Margin {
|
||||
epsilon: m.0,
|
||||
ulps: m.1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApproxEq for f64 {
|
||||
type Margin = F64Margin;
|
||||
|
||||
fn approx_eq<M: Into<Self::Margin>>(self, other: f64, margin: M) -> bool {
|
||||
let margin = margin.into();
|
||||
|
||||
// Check for exact equality first. This is often true, and so we get the
|
||||
// performance benefit of only doing one compare in most cases.
|
||||
self == other ||
|
||||
|
||||
// Perform epsilon comparison next
|
||||
((self - other).abs() <= margin.epsilon) ||
|
||||
|
||||
{
|
||||
// Perform ulps comparion last
|
||||
let diff: i64 = self.ulps(&other);
|
||||
saturating_abs_i64!(diff) <= margin.ulps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_test1() {
|
||||
let f: f64 = 0.0_f64;
|
||||
let g: f64 = -0.0000000000000005551115123125783_f64;
|
||||
assert!(f != g); // Should not be precisely equal.
|
||||
assert!(f.approx_eq(g, (3.0 * f64::EPSILON, 0)) == true); // 3e is enough.
|
||||
// ULPs test won't ever call these equal.
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test2() {
|
||||
let f: f64 = 0.0_f64;
|
||||
let g: f64 = -0.0_f64;
|
||||
assert!(f.approx_eq(g, (f64::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test3() {
|
||||
let f: f64 = 0.0_f64;
|
||||
let g: f64 = 1e-17_f64;
|
||||
assert!(f.approx_eq(g, (f64::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test4() {
|
||||
let f: f64 = 0.00001_f64;
|
||||
let g: f64 = 0.00000000000000001_f64;
|
||||
assert!(f.approx_eq(g, (f64::EPSILON, 0)) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test5() {
|
||||
let f: f64 = 0.1_f64;
|
||||
let mut sum: f64 = 0.0_f64;
|
||||
for _ in 0_isize..10_isize { sum += f; }
|
||||
let product: f64 = f * 10.0_f64;
|
||||
assert!(sum != product); // Should not be precisely equaly.
|
||||
assert!(sum.approx_eq(product, (f64::EPSILON, 0)) == true);
|
||||
assert!(sum.approx_eq(product, (0.0, 1)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test6() {
|
||||
let x: f64 = 1000000_f64;
|
||||
let y: f64 = 1000000.0000000003_f64;
|
||||
assert!(x != y); // Should not be precisely equal.
|
||||
assert!(x.approx_eq(y, (0.0, 3)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f64_code_triggering_issue_20() {
|
||||
assert_eq!((-25.0f64).approx_eq(25.0, (0.00390625, 1)), false);
|
||||
}
|
||||
|
||||
impl<T> ApproxEq for &[T]
|
||||
where T: Copy + ApproxEq {
|
||||
type Margin = <T as ApproxEq>::Margin;
|
||||
|
||||
fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
|
||||
let margin = margin.into();
|
||||
if self.len() != other.len() { return false; }
|
||||
self.iter().zip(other.iter()).all(|(a,b)| {
|
||||
a.approx_eq(*b, margin)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slices() {
|
||||
assert!( [1.33, 2.4, 2.5].approx_eq(&[1.33, 2.4, 2.5], (0.0, 0_i64)) );
|
||||
assert!( ! [1.33, 2.4, 2.6].approx_eq(&[1.33, 2.4, 2.5], (0.0, 0_i64)) );
|
||||
assert!( ! [1.33, 2.4].approx_eq(&[1.33, 2.4, 2.5], (0.0, 0_i64)) );
|
||||
assert!( ! [1.33, 2.4, 2.5].approx_eq(&[1.33, 2.4], (0.0, 0_i64)) );
|
||||
}
|
||||
|
||||
Vendored
+195
@@ -0,0 +1,195 @@
|
||||
// Copyright 2014-2020 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
//! # float-cmp
|
||||
//!
|
||||
//! float-cmp defines and implements traits for approximate comparison of floating point types
|
||||
//! which have fallen away from exact equality due to the limited precision available within
|
||||
//! floating point representations. Implementations of these traits are provided for `f32`
|
||||
//! and `f64` types.
|
||||
//!
|
||||
//! When I was a kid in the '80s, the programming rule was "Never compare floating point
|
||||
//! numbers". If you can follow that rule and still get the outcome you desire, then more
|
||||
//! power to you. However, if you really do need to compare them, this crate provides a
|
||||
//! reasonable way to do so.
|
||||
//!
|
||||
//! Another crate `efloat` offers another solution by providing a floating point type that
|
||||
//! tracks its error bounds as operations are performed on it, and thus can implement the
|
||||
//! `ApproxEq` trait in this crate more accurately, without specifying a `Margin`.
|
||||
//!
|
||||
//! The recommended go-to solution (although it may not be appropriate in all cases) is the
|
||||
//! `approx_eq()` function in the `ApproxEq` trait (or better yet, the macros). For `f32`
|
||||
//! and `f64`, the `F32Margin` and `F64Margin` types are provided for specifying margins as
|
||||
//! both an epsilon value and an ULPs value, and defaults are provided via `Default`
|
||||
//! (although there is no perfect default value that is always appropriate, so beware).
|
||||
//!
|
||||
//! Several other traits are provided including `Ulps`, `ApproxEqUlps`, `ApproxOrdUlps`, and
|
||||
//! `ApproxEqRatio`.
|
||||
//!
|
||||
//! ## The problem
|
||||
//!
|
||||
//! Floating point operations must round answers to the nearest representable number. Multiple
|
||||
//! operations may result in an answer different from what you expect. In the following example,
|
||||
//! the assert will fail, even though the printed output says "0.45 == 0.45":
|
||||
//!
|
||||
//! ```should_panic
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::ApproxEq;
|
||||
//! # fn main() {
|
||||
//! let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! println!("{} == {}", a, b);
|
||||
//! assert!(a==b) // Fails, because they are not exactly equal
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! This fails because the correct answer to most operations isn't exactly representable, and so
|
||||
//! your computer's processor chooses to represent the answer with the closest value it has
|
||||
//! available. This introduces error, and this error can accumulate as multiple operations are
|
||||
//! performed.
|
||||
//!
|
||||
//! ## The solution
|
||||
//!
|
||||
//! With `ApproxEq`, we can get the answer we intend:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use]
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::{ApproxEq, F32Margin};
|
||||
//! # fn main() {
|
||||
//! let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! println!("{} == {}", a, b);
|
||||
//! // They are equal, within 2 ulps
|
||||
//! assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Some explanation
|
||||
//!
|
||||
//! We use the term ULP (units of least precision, or units in the last place) to mean the
|
||||
//! difference between two adjacent floating point representations (adjacent meaning that there is
|
||||
//! no floating point number between them). This term is borrowed from prior work (personally I
|
||||
//! would have chosen "quanta"). The size of an ULP (measured as a float) varies
|
||||
//! depending on the exponents of the floating point numbers in question. That is a good thing,
|
||||
//! because as numbers fall away from equality due to the imprecise nature of their representation,
|
||||
//! they fall away in ULPs terms, not in absolute terms. Pure epsilon-based comparisons are
|
||||
//! absolute and thus don't map well to the nature of the additive error issue. They work fine
|
||||
//! for many ranges of numbers, but not for others (consider comparing -0.0000000028
|
||||
//! to +0.00000097).
|
||||
//!
|
||||
//! ## Using this crate
|
||||
//!
|
||||
//! By default this crate enables the `ratio` module providing the `ApproxEqRatio` trait. This
|
||||
//! feature pulls in `num-traits`. If you disable this feature, you'll need to either enable
|
||||
//! `num-traits` directly or else enable the `std` feature; otherwise it won't compile. This crate
|
||||
//! is `#![no_std]` unless you enable the `std` feature.
|
||||
//!
|
||||
//! You can use the `ApproxEq` trait directly like so:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::{ApproxEq, F32Margin};
|
||||
//! # fn main() {
|
||||
//! # let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! # let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! assert!( a.approx_eq(b, F32Margin { ulps: 2, epsilon: 0.0 }) );
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! We have implemented `From<(f32,i32)>` for `F32Margin` (and similarly for `F64Margin`)
|
||||
//! so you can use this shorthand:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::{ApproxEq, F32Margin};
|
||||
//! # fn main() {
|
||||
//! # let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! # let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! assert!( a.approx_eq(b, (0.0, 2)) );
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! With macros, it is easier to be explicit about which type of margin you wish to set,
|
||||
//! without mentioning the other one (the other one will be zero). But the downside is
|
||||
//! that you have to specify the type you are dealing with:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use]
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::{ApproxEq, F32Margin};
|
||||
//! # fn main() {
|
||||
//! # let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! # let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
//! assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) );
|
||||
//! assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) );
|
||||
//! assert!( approx_eq!(f32, a, b, (0.0, 2)) );
|
||||
//! assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) );
|
||||
//! assert!( approx_eq!(f32, a, b, F32Margin::default()) );
|
||||
//! assert!( approx_eq!(f32, a, b) ); // uses the default
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For most cases, I recommend you use a smallish integer for the `ulps` parameter (1 to 5
|
||||
//! or so), and a similar small multiple of the floating point's EPSILON constant (1.0 to 5.0
|
||||
//! or so), but there are *plenty* of cases where this is insufficient.
|
||||
//!
|
||||
//! ## Implementing these traits
|
||||
//!
|
||||
//! You can implement `ApproxEq` for your own complex types like shown below.
|
||||
//! The floating point type `F` must be `Copy`, but for large types you can implement
|
||||
//! it for references to your type as shown.
|
||||
//!
|
||||
//! ```
|
||||
//! use float_cmp::ApproxEq;
|
||||
//!
|
||||
//! pub struct Vec2<F> {
|
||||
//! pub x: F,
|
||||
//! pub y: F,
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a, M: Copy + Default, F: Copy + ApproxEq<Margin=M>> ApproxEq for &'a Vec2<F> {
|
||||
//! type Margin = M;
|
||||
//!
|
||||
//! fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
|
||||
//! let margin = margin.into();
|
||||
//! self.x.approx_eq(other.x, margin)
|
||||
//! && self.y.approx_eq(other.y, margin)
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Non floating-point types
|
||||
//!
|
||||
//! `ApproxEq` can be implemented for non floating-point types as well, since `Margin` is
|
||||
//! an associated type.
|
||||
//!
|
||||
//! The `efloat` crate implements (or soon will implement) `ApproxEq` for a compound type
|
||||
//! that tracks floating point error bounds by checking if the error bounds overlap.
|
||||
//! In that case `type Margin = ()`.
|
||||
//!
|
||||
//! ## Inspiration
|
||||
//!
|
||||
//! This crate was inspired by this Random ASCII blog post:
|
||||
//!
|
||||
//! [https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod ulps;
|
||||
pub use self::ulps::Ulps;
|
||||
|
||||
mod ulps_eq;
|
||||
pub use self::ulps_eq::ApproxEqUlps;
|
||||
|
||||
mod eq;
|
||||
pub use self::eq::{ApproxEq, F32Margin, F64Margin};
|
||||
|
||||
#[cfg(feature="ratio")]
|
||||
mod ratio;
|
||||
#[cfg(feature="ratio")]
|
||||
pub use self::ratio::ApproxEqRatio;
|
||||
Vendored
+189
@@ -0,0 +1,189 @@
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! approx_eq {
|
||||
($typ:ty, $lhs:expr, $rhs:expr) => {
|
||||
{
|
||||
let m = <$typ as $crate::ApproxEq>::Margin::default();
|
||||
<$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m)
|
||||
}
|
||||
};
|
||||
($typ:ty, $lhs:expr, $rhs:expr $(, $set:ident = $val:expr)*) => {
|
||||
{
|
||||
let m = <$typ as $crate::ApproxEq>::Margin::zero()$(.$set($val))*;
|
||||
<$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m)
|
||||
}
|
||||
};
|
||||
($typ:ty, $lhs:expr, $rhs:expr, $marg:expr) => {
|
||||
{
|
||||
<$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, $marg)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_approx_eq {
|
||||
($typ:ty, $lhs:expr, $rhs:expr) => {
|
||||
{
|
||||
match (&$lhs, &$rhs) {
|
||||
(left_val, right_val) => {
|
||||
if !$crate::approx_eq!($typ, *left_val, *right_val) {
|
||||
panic!(
|
||||
r#"assertion failed: `(left approx_eq right)`
|
||||
left: `{:?}`,
|
||||
right: `{:?}`"#,
|
||||
left_val, right_val,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($typ:ty, $lhs:expr, $rhs:expr $(, $set:ident = $val:expr)*) => {
|
||||
{
|
||||
match (&$lhs, &$rhs) {
|
||||
(left_val, right_val) => {
|
||||
if !$crate::approx_eq!($typ, *left_val, *right_val $(, $set = $val)*) {
|
||||
panic!(
|
||||
r#"assertion failed: `(left approx_eq right)`
|
||||
left: `{:?}`,
|
||||
right: `{:?}`"#,
|
||||
left_val, right_val,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($typ:ty, $lhs:expr, $rhs:expr, $marg:expr) => {
|
||||
{
|
||||
match (&$lhs, &$rhs) {
|
||||
(left_val, right_val) => {
|
||||
if !$crate::approx_eq!($typ, *left_val, *right_val, $marg) {
|
||||
panic!(
|
||||
r#"assertion failed: `(left approx_eq right)`
|
||||
left: `{:?}`,
|
||||
right: `{:?}`"#,
|
||||
left_val, right_val,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Until saturating_abs() comes out of nightly, we have to code it ourselves.
|
||||
macro_rules! saturating_abs_i32 {
|
||||
($val:expr) => {
|
||||
if $val.is_negative() {
|
||||
match $val.checked_neg() {
|
||||
Some(v) => v,
|
||||
None => core::i32::MAX
|
||||
}
|
||||
} else {
|
||||
$val
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! saturating_abs_i64 {
|
||||
($val:expr) => {
|
||||
if $val.is_negative() {
|
||||
match $val.checked_neg() {
|
||||
Some(v) => v,
|
||||
None => core::i64::MAX
|
||||
}
|
||||
} else {
|
||||
$val
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro() {
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
assert!( approx_eq!(f32, a, b) ); // uses the default
|
||||
assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) );
|
||||
assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) );
|
||||
assert!( approx_eq!(f32, a, b, (0.0, 2)) );
|
||||
|
||||
assert_approx_eq!(f32, a, b); // uses the default
|
||||
assert_approx_eq!(f32, a, b, ulps = 2);
|
||||
assert_approx_eq!(f32, a, b, epsilon = 0.00000003);
|
||||
assert_approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2);
|
||||
assert_approx_eq!(f32, a, b, (0.0, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_2() {
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64) );
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, ulps=3) );
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, epsilon=0.0000000004) );
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0000000004, 0)) );
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0, 3)) );
|
||||
|
||||
assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64);
|
||||
assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, ulps=3);
|
||||
assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, epsilon=0.0000000004);
|
||||
assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0000000004, 0));
|
||||
assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0, 3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_3() {
|
||||
use crate::F32Margin;
|
||||
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) );
|
||||
assert!( approx_eq!(f32, a, b, F32Margin::default()) );
|
||||
|
||||
assert_approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 });
|
||||
assert_approx_eq!(f32, a, b, F32Margin::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_macro_4() {
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 1.0;
|
||||
|
||||
assert_approx_eq!(f32, a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_macro_5() {
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 1.0;
|
||||
|
||||
assert_approx_eq!(f32, a, b, ulps = 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_macro_6() {
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 1.0;
|
||||
|
||||
assert_approx_eq!(f32, a, b, epsilon = 0.00000003);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_macro_7() {
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 1.0;
|
||||
|
||||
assert_approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_macro_8() {
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 1.0;
|
||||
|
||||
assert_approx_eq!(f32, a, b, (0.0, 2));
|
||||
}
|
||||
Vendored
+142
@@ -0,0 +1,142 @@
|
||||
// Copyright 2014-2020 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
use core::cmp::PartialOrd;
|
||||
use core::ops::{Sub, Div, Neg};
|
||||
use num_traits::Zero;
|
||||
|
||||
/// ApproxEqRatio is a trait for approximate equality comparisons bounding the ratio
|
||||
/// of the difference to the larger.
|
||||
pub trait ApproxEqRatio : Div<Output = Self> + Sub<Output = Self> + Neg<Output = Self>
|
||||
+ PartialOrd + Zero + Sized + Copy
|
||||
{
|
||||
/// This method tests if `self` and `other` are nearly equal by bounding the
|
||||
/// difference between them to some number much less than the larger of the two.
|
||||
/// This bound is set as the ratio of the difference to the larger.
|
||||
fn approx_eq_ratio(&self, other: &Self, ratio: Self) -> bool {
|
||||
|
||||
// Not equal if signs are not equal
|
||||
if *self < Self::zero() && *other > Self::zero() { return false; }
|
||||
if *self > Self::zero() && *other < Self::zero() { return false; }
|
||||
|
||||
// Handle all zero cases
|
||||
match (*self == Self::zero(), *other == Self::zero()) {
|
||||
(true,true) => return true,
|
||||
(true,false) => return false,
|
||||
(false,true) => return false,
|
||||
_ => { }
|
||||
}
|
||||
|
||||
// abs
|
||||
let (s,o) = if *self < Self::zero() {
|
||||
(-*self, -*other)
|
||||
} else {
|
||||
(*self, *other)
|
||||
};
|
||||
|
||||
let (smaller,larger) = if s < o {
|
||||
(s,o)
|
||||
} else {
|
||||
(o,s)
|
||||
};
|
||||
let difference: Self = larger.sub(smaller);
|
||||
let actual_ratio: Self = difference.div(larger);
|
||||
actual_ratio < ratio
|
||||
}
|
||||
|
||||
/// This method tests if `self` and `other` are not nearly equal by bounding the
|
||||
/// difference between them to some number much less than the larger of the two.
|
||||
/// This bound is set as the ratio of the difference to the larger.
|
||||
#[inline]
|
||||
fn approx_ne_ratio(&self, other: &Self, ratio: Self) -> bool {
|
||||
!self.approx_eq_ratio(other, ratio)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApproxEqRatio for f32 { }
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test1() {
|
||||
let x: f32 = 0.00004_f32;
|
||||
let y: f32 = 0.00004001_f32;
|
||||
assert!(x.approx_eq_ratio(&y, 0.00025));
|
||||
assert!(y.approx_eq_ratio(&x, 0.00025));
|
||||
assert!(x.approx_ne_ratio(&y, 0.00024));
|
||||
assert!(y.approx_ne_ratio(&x, 0.00024));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test2() {
|
||||
let x: f32 = 0.00000000001_f32;
|
||||
let y: f32 = 0.00000000005_f32;
|
||||
assert!(x.approx_eq_ratio(&y, 0.81));
|
||||
assert!(y.approx_ne_ratio(&x, 0.79));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test_zero_eq_zero_returns_true() {
|
||||
let x: f32 = 0.0_f32;
|
||||
assert!(x.approx_eq_ratio(&x,0.1) == true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test_zero_ne_zero_returns_false() {
|
||||
let x: f32 = 0.0_f32;
|
||||
assert!(x.approx_ne_ratio(&x,0.1) == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test_against_a_zero_is_false() {
|
||||
let x: f32 = 0.0_f32;
|
||||
let y: f32 = 0.1_f32;
|
||||
assert!(x.approx_eq_ratio(&y,0.1) == false);
|
||||
assert!(y.approx_eq_ratio(&x,0.1) == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test_negative_numbers() {
|
||||
let x: f32 = -3.0_f32;
|
||||
let y: f32 = -4.0_f32;
|
||||
// -3 and -4 should not be equal at a ratio of 0.1
|
||||
assert!(x.approx_eq_ratio(&y,0.1) == false);
|
||||
}
|
||||
|
||||
impl ApproxEqRatio for f64 { }
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test1() {
|
||||
let x: f64 = 0.000000004_f64;
|
||||
let y: f64 = 0.000000004001_f64;
|
||||
assert!(x.approx_eq_ratio(&y, 0.00025));
|
||||
assert!(y.approx_eq_ratio(&x, 0.00025));
|
||||
assert!(x.approx_ne_ratio(&y, 0.00024));
|
||||
assert!(y.approx_ne_ratio(&x, 0.00024));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test2() {
|
||||
let x: f64 = 0.0000000000000001_f64;
|
||||
let y: f64 = 0.0000000000000005_f64;
|
||||
assert!(x.approx_eq_ratio(&y, 0.81));
|
||||
assert!(y.approx_ne_ratio(&x, 0.79));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test_zero_eq_zero_returns_true() {
|
||||
let x: f64 = 0.0_f64;
|
||||
assert!(x.approx_eq_ratio(&x,0.1) == true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test_zero_ne_zero_returns_false() {
|
||||
let x: f64 = 0.0_f64;
|
||||
assert!(x.approx_ne_ratio(&x,0.1) == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test_negative_numbers() {
|
||||
let x: f64 = -3.0_f64;
|
||||
let y: f64 = -4.0_f64;
|
||||
// -3 and -4 should not be equal at a ratio of 0.1
|
||||
assert!(x.approx_eq_ratio(&y,0.1) == false);
|
||||
}
|
||||
Vendored
+232
@@ -0,0 +1,232 @@
|
||||
// Copyright 2014-2020 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
#[cfg(feature="num_traits")]
|
||||
use num_traits::NumCast;
|
||||
|
||||
/// A trait for floating point numbers which computes the number of representable
|
||||
/// values or ULPs (Units of Least Precision) that separate the two given values.
|
||||
#[cfg(feature="num_traits")]
|
||||
pub trait Ulps {
|
||||
type U: Copy + NumCast;
|
||||
|
||||
/// The number of representable values or ULPs (Units of Least Precision) that
|
||||
/// separate `self` and `other`. The result `U` is an integral value, and will
|
||||
/// be zero if `self` and `other` are exactly equal.
|
||||
fn ulps(&self, other: &Self) -> <Self as Ulps>::U;
|
||||
|
||||
/// The next representable number above this one
|
||||
fn next(&self) -> Self;
|
||||
|
||||
/// The previous representable number below this one
|
||||
fn prev(&self) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(not(feature="num_traits"))]
|
||||
pub trait Ulps {
|
||||
type U: Copy;
|
||||
|
||||
/// The number of representable values or ULPs (Units of Least Precision) that
|
||||
/// separate `self` and `other`. The result `U` is an integral value, and will
|
||||
/// be zero if `self` and `other` are exactly equal.
|
||||
fn ulps(&self, other: &Self) -> <Self as Ulps>::U;
|
||||
|
||||
/// The next representable number above this one
|
||||
fn next(&self) -> Self;
|
||||
|
||||
/// The previous representable number below this one
|
||||
fn prev(&self) -> Self;
|
||||
}
|
||||
|
||||
impl Ulps for f32 {
|
||||
type U = i32;
|
||||
|
||||
fn ulps(&self, other: &f32) -> i32 {
|
||||
|
||||
// IEEE754 defined floating point storage representation to
|
||||
// maintain their order when their bit patterns are interpreted as
|
||||
// integers. This is a huge boon to the task at hand, as we can
|
||||
// reinterpret them as integers to find out how many ULPs apart any
|
||||
// two floats are
|
||||
|
||||
// Setup integer representations of the input
|
||||
let ai32: i32 = self.to_bits() as i32;
|
||||
let bi32: i32 = other.to_bits() as i32;
|
||||
|
||||
ai32.wrapping_sub(bi32)
|
||||
}
|
||||
|
||||
fn next(&self) -> Self {
|
||||
if self.is_infinite() && *self > 0.0 {
|
||||
*self
|
||||
} else if *self == -0.0 && self.is_sign_negative() {
|
||||
0.0
|
||||
} else {
|
||||
let mut u = self.to_bits();
|
||||
if *self >= 0.0 {
|
||||
u += 1;
|
||||
} else {
|
||||
u -= 1;
|
||||
}
|
||||
f32::from_bits(u)
|
||||
}
|
||||
}
|
||||
|
||||
fn prev(&self) -> Self {
|
||||
if self.is_infinite() && *self < 0.0 {
|
||||
*self
|
||||
} else if *self == 0.0 && self.is_sign_positive() {
|
||||
-0.0
|
||||
} else {
|
||||
let mut u = self.to_bits();
|
||||
if *self <= -0.0 {
|
||||
u += 1;
|
||||
} else {
|
||||
u -= 1;
|
||||
}
|
||||
f32::from_bits(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test1() {
|
||||
let x: f32 = 1000000_f32;
|
||||
let y: f32 = 1000000.1_f32;
|
||||
assert!(x.ulps(&y) == -2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test2() {
|
||||
let pzero: f32 = f32::from_bits(0x00000000_u32);
|
||||
let nzero: f32 = f32::from_bits(0x80000000_u32);
|
||||
assert!(pzero.ulps(&nzero) == -2147483648);
|
||||
}
|
||||
#[test]
|
||||
fn f32_ulps_test3() {
|
||||
let pinf: f32 = f32::from_bits(0x7f800000_u32);
|
||||
let ninf: f32 = f32::from_bits(0xff800000_u32);
|
||||
assert!(pinf.ulps(&ninf) == -2147483648);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test4() {
|
||||
let x: f32 = f32::from_bits(0x63a7f026_u32);
|
||||
let y: f32 = f32::from_bits(0x63a7f023_u32);
|
||||
assert!(x.ulps(&y) == 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test5() {
|
||||
let x: f32 = 2.0;
|
||||
let ulps: i32 = x.to_bits() as i32;
|
||||
let x2: f32 = <f32>::from_bits(ulps as u32);
|
||||
assert_eq!(x, x2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test6() {
|
||||
let negzero: f32 = -0.;
|
||||
let zero: f32 = 0.;
|
||||
assert_eq!(negzero.next(), zero);
|
||||
assert_eq!(zero.prev(), negzero);
|
||||
assert!(negzero.prev() < 0.0);
|
||||
assert!(zero.next() > 0.0);
|
||||
}
|
||||
|
||||
impl Ulps for f64 {
|
||||
type U = i64;
|
||||
|
||||
fn ulps(&self, other: &f64) -> i64 {
|
||||
|
||||
// IEEE754 defined floating point storage representation to
|
||||
// maintain their order when their bit patterns are interpreted as
|
||||
// integers. This is a huge boon to the task at hand, as we can
|
||||
// reinterpret them as integers to find out how many ULPs apart any
|
||||
// two floats are
|
||||
|
||||
// Setup integer representations of the input
|
||||
let ai64: i64 = self.to_bits() as i64;
|
||||
let bi64: i64 = other.to_bits() as i64;
|
||||
|
||||
ai64.wrapping_sub(bi64)
|
||||
}
|
||||
|
||||
fn next(&self) -> Self {
|
||||
if self.is_infinite() && *self > 0.0 {
|
||||
*self
|
||||
} else if *self == -0.0 && self.is_sign_negative() {
|
||||
0.0
|
||||
} else {
|
||||
let mut u = self.to_bits();
|
||||
if *self >= 0.0 {
|
||||
u += 1;
|
||||
} else {
|
||||
u -= 1;
|
||||
}
|
||||
f64::from_bits(u)
|
||||
}
|
||||
}
|
||||
|
||||
fn prev(&self) -> Self {
|
||||
if self.is_infinite() && *self < 0.0 {
|
||||
*self
|
||||
} else if *self == 0.0 && self.is_sign_positive() {
|
||||
-0.0
|
||||
} else {
|
||||
let mut u = self.to_bits();
|
||||
if *self <= -0.0 {
|
||||
u += 1;
|
||||
} else {
|
||||
u -= 1;
|
||||
}
|
||||
f64::from_bits(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test1() {
|
||||
let x: f64 = 1000000_f64;
|
||||
let y: f64 = 1000000.00000001_f64;
|
||||
assert!(x.ulps(&y) == -86);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test2() {
|
||||
let pzero: f64 = f64::from_bits(0x0000000000000000_u64);
|
||||
let nzero: f64 = f64::from_bits(0x8000000000000000_u64);
|
||||
assert!(pzero.ulps(&nzero) == -9223372036854775808i64);
|
||||
}
|
||||
#[test]
|
||||
fn f64_ulps_test3() {
|
||||
let pinf: f64 = f64::from_bits(0x7f80000000000000_u64);
|
||||
let ninf: f64 = f64::from_bits(0xff80000000000000_u64);
|
||||
assert!(pinf.ulps(&ninf) == -9223372036854775808i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test4() {
|
||||
let x: f64 = f64::from_bits(0xd017f6cc63a7f026_u64);
|
||||
let y: f64 = f64::from_bits(0xd017f6cc63a7f023_u64);
|
||||
assert!(x.ulps(&y) == 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test5() {
|
||||
let x: f64 = 2.0;
|
||||
let ulps: i64 = x.to_bits() as i64;
|
||||
let x2: f64 = <f64>::from_bits(ulps as u64);
|
||||
assert_eq!(x, x2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test6() {
|
||||
let negzero: f64 = -0.;
|
||||
let zero: f64 = 0.;
|
||||
assert_eq!(negzero.next(), zero);
|
||||
assert_eq!(zero.prev(), negzero);
|
||||
assert!(negzero.prev() < 0.0);
|
||||
assert!(zero.next() > 0.0);
|
||||
}
|
||||
|
||||
Vendored
+118
@@ -0,0 +1,118 @@
|
||||
// Copyright 2014-2020 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
#[cfg(feature = "num-traits")]
|
||||
#[allow(unused_imports)]
|
||||
use num_traits::float::FloatCore;
|
||||
use super::Ulps;
|
||||
|
||||
/// ApproxEqUlps is a trait for approximate equality comparisons.
|
||||
/// The associated type Flt is a floating point type which implements Ulps, and is
|
||||
/// required so that this trait can be implemented for compound types (e.g. vectors),
|
||||
/// not just for the floats themselves.
|
||||
pub trait ApproxEqUlps {
|
||||
type Flt: Ulps;
|
||||
|
||||
/// This method tests for `self` and `other` values to be approximately equal
|
||||
/// within ULPs (Units of Least Precision) floating point representations.
|
||||
/// Differing signs are always unequal with this method, and zeroes are only
|
||||
/// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more
|
||||
/// appropriate.
|
||||
fn approx_eq_ulps(&self, other: &Self, ulps: <Self::Flt as Ulps>::U) -> bool;
|
||||
|
||||
/// This method tests for `self` and `other` values to be not approximately
|
||||
/// equal within ULPs (Units of Least Precision) floating point representations.
|
||||
/// Differing signs are always unequal with this method, and zeroes are only
|
||||
/// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more
|
||||
/// appropriate.
|
||||
#[inline]
|
||||
fn approx_ne_ulps(&self, other: &Self, ulps: <Self::Flt as Ulps>::U) -> bool {
|
||||
!self.approx_eq_ulps(other, ulps)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApproxEqUlps for f32 {
|
||||
type Flt = f32;
|
||||
|
||||
fn approx_eq_ulps(&self, other: &f32, ulps: i32) -> bool {
|
||||
// -0 and +0 are drastically far in ulps terms, so
|
||||
// we need a special case for that.
|
||||
if *self==*other { return true; }
|
||||
|
||||
// Handle differing signs as a special case, even if
|
||||
// they are very close, most people consider them
|
||||
// unequal.
|
||||
if self.is_sign_positive() != other.is_sign_positive() { return false; }
|
||||
|
||||
let diff: i32 = self.ulps(other);
|
||||
diff >= -ulps && diff <= ulps
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ulps_test1() {
|
||||
let f: f32 = 0.1_f32;
|
||||
let mut sum: f32 = 0.0_f32;
|
||||
for _ in 0_isize..10_isize { sum += f; }
|
||||
let product: f32 = f * 10.0_f32;
|
||||
assert!(sum != product); // Should not be directly equal:
|
||||
assert!(sum.approx_eq_ulps(&product,1) == true); // But should be close
|
||||
assert!(sum.approx_eq_ulps(&product,0) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_ulps_test2() {
|
||||
let x: f32 = 1000000_f32;
|
||||
let y: f32 = 1000000.1_f32;
|
||||
assert!(x != y); // Should not be directly equal
|
||||
assert!(x.approx_eq_ulps(&y,2) == true);
|
||||
assert!(x.approx_eq_ulps(&y,1) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_ulps_test_zeroes() {
|
||||
let x: f32 = 0.0_f32;
|
||||
let y: f32 = -0.0_f32;
|
||||
assert!(x.approx_eq_ulps(&y,0) == true);
|
||||
}
|
||||
|
||||
impl ApproxEqUlps for f64 {
|
||||
type Flt = f64;
|
||||
|
||||
fn approx_eq_ulps(&self, other: &f64, ulps: i64) -> bool {
|
||||
// -0 and +0 are drastically far in ulps terms, so
|
||||
// we need a special case for that.
|
||||
if *self==*other { return true; }
|
||||
|
||||
// Handle differing signs as a special case, even if
|
||||
// they are very close, most people consider them
|
||||
// unequal.
|
||||
if self.is_sign_positive() != other.is_sign_positive() { return false; }
|
||||
|
||||
let diff: i64 = self.ulps(other);
|
||||
diff >= -ulps && diff <= ulps
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ulps_test1() {
|
||||
let f: f64 = 0.1_f64;
|
||||
let mut sum: f64 = 0.0_f64;
|
||||
for _ in 0_isize..10_isize { sum += f; }
|
||||
let product: f64 = f * 10.0_f64;
|
||||
assert!(sum != product); // Should not be directly equal:
|
||||
assert!(sum.approx_eq_ulps(&product,1) == true); // But should be close
|
||||
assert!(sum.approx_eq_ulps(&product,0) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_ulps_test2() {
|
||||
let x: f64 = 1000000_f64;
|
||||
let y: f64 = 1000000.0000000003_f64;
|
||||
assert!(x != y); // Should not be directly equal
|
||||
assert!(x.approx_eq_ulps(&y,3) == true);
|
||||
assert!(x.approx_eq_ulps(&y,2) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_ulps_test_zeroes() {
|
||||
let x: f64 = 0.0_f64;
|
||||
let y: f64 = -0.0_f64;
|
||||
assert!(x.approx_eq_ulps(&y,0) == true);
|
||||
}
|
||||
Reference in new issue
Block a user