├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── HOW-TO-RELEASE-CRATE.txt ├── HOW-TO-RELEASE-DOCS.txt ├── LICENSE-APACHE ├── LICENSE-MIT ├── LICENSE.md ├── README.md ├── badges.md ├── book ├── book.toml └── src │ ├── SUMMARY.md │ ├── api_documentation.md │ ├── background.md │ ├── background │ ├── float_comparison_algorithms.md │ └── resources.md │ ├── how_to.md │ ├── how_to │ ├── compare_composite_types.md │ ├── compare_custom_types.md │ ├── compare_floating_point_numbers.md │ ├── derive_the_traits.md │ ├── interpret_assert_failure_messages.md │ └── manually_implement_the_traits.md │ ├── introduction.md │ ├── tutorials.md │ └── tutorials │ └── basic_usage.md ├── codecov.yml ├── docs-build.bat ├── docs-index.html ├── float_eq ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── crates-io.md ├── src │ ├── lib.rs │ ├── macros.rs │ ├── trait_impls.rs │ ├── trait_impls │ │ ├── arrays.rs │ │ ├── core_types.rs │ │ ├── num_complex.rs │ │ ├── primitives.rs │ │ ├── std_types.rs │ │ └── tuples.rs │ └── traits.rs └── tests │ ├── derive_float_eq_error_msg.rs │ ├── derive_tests.rs │ ├── derive_tests │ ├── assert_float_eq │ │ ├── assert_float_eq_enum.rs │ │ ├── assert_float_eq_enum.stderr │ │ ├── assert_float_eq_generic.rs │ │ ├── assert_float_eq_generic.stderr │ │ ├── assert_float_eq_struct.rs │ │ ├── assert_float_eq_struct_no_fields.rs │ │ ├── assert_float_eq_tuple_struct.rs │ │ └── assert_float_eq_unit.rs │ ├── assert_float_eq_all │ │ ├── assert_float_eq_all_enum.rs │ │ ├── assert_float_eq_all_enum.stderr │ │ ├── assert_float_eq_all_generic.rs │ │ ├── assert_float_eq_all_generic.stderr │ │ ├── assert_float_eq_all_struct.rs │ │ ├── assert_float_eq_all_struct_no_fields.rs │ │ ├── assert_float_eq_all_tuple_struct.rs │ │ └── assert_float_eq_all_unit.rs │ ├── debug_ulps_diff │ │ ├── debug_ulps_diff_duplicate_type_name.rs │ │ ├── debug_ulps_diff_duplicate_type_name.stderr │ │ ├── debug_ulps_diff_enum.rs │ │ ├── debug_ulps_diff_enum.stderr │ │ ├── debug_ulps_diff_generic.rs │ │ ├── debug_ulps_diff_generic.stderr │ │ ├── debug_ulps_diff_missing_type_name.rs │ │ ├── debug_ulps_diff_missing_type_name.stderr │ │ ├── debug_ulps_diff_struct.rs │ │ ├── debug_ulps_diff_struct_custom_debug.rs │ │ ├── debug_ulps_diff_struct_no_fields.rs │ │ ├── debug_ulps_diff_tuple_struct.rs │ │ └── debug_ulps_diff_unit.rs │ ├── derive_float_eq │ │ ├── derive_float_eq.rs │ │ ├── derive_float_eq_all.rs │ │ ├── derive_float_eq_all_custom_debug.rs │ │ ├── derive_float_eq_missing_debug_ulps_diff.rs │ │ ├── derive_float_eq_missing_debug_ulps_diff.stderr │ │ ├── derive_float_eq_missing_ulps_tol.rs │ │ ├── derive_float_eq_missing_ulps_tol.stderr │ │ └── derive_float_eq_tuple_struct.rs │ ├── float_eq │ │ ├── float_eq_enum.rs │ │ ├── float_eq_enum.stderr │ │ ├── float_eq_generic.rs │ │ ├── float_eq_generic.stderr │ │ ├── float_eq_struct.rs │ │ ├── float_eq_struct_no_fields.rs │ │ ├── float_eq_tuple_struct.rs │ │ └── float_eq_unit.rs │ ├── float_eq_all │ │ ├── float_eq_all_duplicate_tol.rs │ │ ├── float_eq_all_duplicate_tol.stderr │ │ ├── float_eq_all_enum.rs │ │ ├── float_eq_all_enum.stderr │ │ ├── float_eq_all_generic.rs │ │ ├── float_eq_all_generic.stderr │ │ ├── float_eq_all_missing_tol.rs │ │ ├── float_eq_all_missing_tol.stderr │ │ ├── float_eq_all_struct.rs │ │ ├── float_eq_all_struct_no_fields.rs │ │ ├── float_eq_all_tuple_struct.rs │ │ └── float_eq_all_unit.rs │ ├── float_eq_attribute │ │ ├── float_eq_malformed_param.rs │ │ ├── float_eq_malformed_param.stderr │ │ ├── float_eq_malformed_value.rs │ │ ├── float_eq_malformed_value.stderr │ │ ├── float_eq_no_params_list.rs │ │ ├── float_eq_no_params_list.stderr │ │ ├── float_eq_unknown_param.rs │ │ └── float_eq_unknown_param.stderr │ └── ulps_tol │ │ ├── ulps_tol_duplicate_type_name.rs │ │ ├── ulps_tol_duplicate_type_name.stderr │ │ ├── ulps_tol_enum.rs │ │ ├── ulps_tol_enum.stderr │ │ ├── ulps_tol_generic.rs │ │ ├── ulps_tol_generic.stderr │ │ ├── ulps_tol_missing_type_name.rs │ │ ├── ulps_tol_missing_type_name.stderr │ │ ├── ulps_tol_struct.rs │ │ ├── ulps_tol_struct_custom_debug.rs │ │ ├── ulps_tol_struct_no_fields.rs │ │ ├── ulps_tol_tuple_struct.rs │ │ └── ulps_tol_unit.rs │ ├── my_complex.rs │ ├── my_complex_cmp_f32.rs │ ├── my_complex_generic.rs │ ├── unit_tests.rs │ └── unit_tests │ ├── arrays.rs │ ├── core_types.rs │ ├── macros.rs │ ├── num_complex.rs │ ├── primitives.rs │ ├── primitives │ ├── assert_float_eq.rs │ ├── eq_abs.rs │ ├── eq_rmax.rs │ ├── eq_rmin.rs │ └── eq_ulps.rs │ ├── std_types.rs │ └── tuples.rs ├── float_eq_derive ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── crates-io.md └── src │ ├── lib.rs │ └── read.rs └── generate-readme.bat /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/actions-rs/meta/blob/master/recipes/matrix.md 2 | 3 | on: push 4 | 5 | name: Build 6 | 7 | jobs: 8 | tests: 9 | runs-on: ubuntu-latest 10 | continue-on-error: ${{ matrix.experimental }} 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | rust: 15 | - stable 16 | - beta 17 | #- nightly 18 | #- 1.51.0 # MSRV 19 | params: ['', 20 | '--all-features', 21 | '--no-default-features', 22 | '--no-default-features --features derive,num'] 23 | experimental: [false] 24 | include: 25 | - rust: nightly 26 | params: '' 27 | experimental: true 28 | - rust: nightly 29 | params: '--all-features' 30 | experimental: true 31 | - rust: nightly 32 | params: '--no-default-features' 33 | experimental: true 34 | - rust: nightly 35 | params: '--no-default-features --features derive,num' 36 | experimental: true 37 | 38 | steps: 39 | - uses: actions/checkout@v2 40 | 41 | - uses: actions-rs/toolchain@v1 42 | with: 43 | profile: minimal 44 | toolchain: ${{ matrix.rust }} 45 | override: true 46 | components: clippy 47 | 48 | - uses: actions-rs/cargo@v1 49 | with: 50 | command: build 51 | args: ${{ matrix.params }} 52 | 53 | - uses: actions-rs/cargo@v1 54 | with: 55 | command: test 56 | args: ${{ matrix.params }} 57 | 58 | - uses: actions-rs/cargo@v1 59 | with: 60 | command: clippy 61 | args: ${{ matrix.params }} -- -D warnings 62 | 63 | coverage-codecov: 64 | needs: tests 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v2 68 | 69 | - uses: actions-rs/toolchain@v1 70 | with: 71 | toolchain: stable 72 | override: true 73 | 74 | - name: Run tarpaulin for codecov.io 75 | uses: actions-rs/tarpaulin@v0.1 76 | with: 77 | version: '0.18.0' 78 | args: '--ciserver github-ci --all-features --ignore-tests' 79 | 80 | - name: Upload to codecov.io 81 | uses: codecov/codecov-action@v2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .vscode/launch.json 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [1.0.1] - 2022-10-12 10 | 11 | ### Fixed 12 | - Fixed a bug where field visibility was not respected when deriving the Ulps 13 | type. 14 | - https://github.com/jtempest/float_eq-rs/issues/24 15 | - Fixed documentation to refer to `ComplexUlps32` rather than the incorrect 16 | `Complex32Ulps`. 17 | - https://github.com/jtempest/float_eq-rs/issues/23 18 | 19 | ## [1.0.0] - 2022-06-02 20 | Added some resource links to the documentation and promoted the crate to 21 | release version 1.0.0, as it has been stable for some time now. 22 | 23 | ## [0.7.0] - 2021-10-03 24 | Bumped up the version number since this release includes breaking API changes. 25 | 26 | ### Changed 27 | - Derived types no longer have a default set of derived traits, use the newly 28 | added `ulps_tol_derive` and `debug_ulps_diff_derive` options to `#[float_eq]` 29 | to provide a list of traits to be derived instead. 30 | - The "derive" feature no longer requires the "full" feature from `syn`. 31 | - Separated out and clarified the documentation for implementing the various 32 | extension traits. 33 | 34 | ## [0.6.1] - 2021-07-31 35 | 36 | ### Changed 37 | - Clearer documentation for the `FloatEqDebugUlpsDiff` and `FloatEqUlpsTol` 38 | traits. 39 | - Updated derive tests to match current compiler output. 40 | - Switched to using github actions and codecov.io for continuous integration 41 | and coverage. 42 | 43 | ## [0.6.0] - 2021-05-03 44 | Bumped up the version number since this release includes breaking API changes. 45 | 46 | ### Changed 47 | - Renamed many ambiguous uses of epsilon/eps/max_diff to tolerance/tol, which is 48 | the mathematical term of art. 49 | - Implementation of traits for arrays of any size using const generics. 50 | - Updated `num-complex` dependency to 0.4. 51 | - Reworked the documentation, added a tutorial. 52 | 53 | ### Fixed 54 | - Safer behaviour when initialising debug output for array trait impls, 55 | including the removal of a source of undefined behaviour. 56 | 57 | ## [0.5.0] - 2020-08-24 58 | Bumped up the version number since this release includes breaking API changes 59 | to the extension traits. 60 | 61 | ### Changed 62 | - Renamed `FloatEqDebug` to `AssertFloatEq`, and similarly `FloatEqAllDebug` to 63 | `AssertFloatEqAll`. This is to provide a more obvious link between these 64 | traits and the assert capability. The `FloatDiff` trait has been folded into 65 | `AssertFloatEq` both for clarity and because it is geared towards debug 66 | context and was not as general as the name implied. 67 | - Expanded `rel` checks into `rmax` (aka `rel`), `rmin`, `r1st` and `r2nd` to 68 | select which operand to scale epsilon's granularity to. 69 | - Significant rewrite of the documentation to clarify aspects of comparison and 70 | to have a slightly more streamlined structure with better defined sections. 71 | - Updated `num-complex` dependency to 0.3. 72 | 73 | ## [0.4.1] - 2020-06-22 74 | 75 | ### Added 76 | - The `derive_float_eq` helper macro to simplify deriving traits. 77 | 78 | ### Fixed 79 | - Update README usage documentation to the correct version, correctly documented 80 | the "num" feature. 81 | 82 | ## [0.4.0] - 2020-06-19 83 | Bumped up the version number since this release includes breaking API changes. 84 | 85 | ### Added 86 | - `#[derive]` support for all traits on tuple structs and structs with named 87 | fields. 88 | - The `FloatUlps` trait, which is a way to syntactically tie a floating point 89 | type to its ULPs representation. The prefered way to express this is now to 90 | implement `FloatUlps for T` and then use `Ulps` for the ULPs type. The 91 | other traits have been reworked to reflect this. 92 | - Blanket trait impls for comparing slices, Option, Vec, VecDeque, LinkedList, 93 | BTreeMap and HashMap. 94 | 95 | ### Changed 96 | - Blanket array impls now allow for comparison of arrays of different types. 97 | - `FloatEqAll::Epsilon` is now `AllEpsilon` and `FloatEqAllDebug::DebugEpsilon` 98 | is now `AllDebugEpsilon` to help avoid ambiguity. 99 | 100 | ## [0.3.1] 2020-06-03 101 | 102 | ### Added 103 | - Blanket trait impls for comparing mutable and immutable reference types and 104 | the contents of Cell, RefCell, Rc, Arc and Box instances. 105 | - More descriptive documentation for combining types of check. 106 | 107 | ## [0.3.0] - 2020-05-30 108 | Bumped up the version number since this release includes breaking API changes. 109 | 110 | ### Added 111 | - The `FloatEqAll` and `FloatEqAllDebug` traits, which allow for `abs_all`, 112 | `rel_all` and `ulps_all` to be used in the `float_eq` and `assert_float_eq` 113 | families of macros, which take a single epsilon value to be applied uniformly 114 | across all fields being compared. The behaviour of `FloatEq` has now changed 115 | to apply epsilon values structurally, on a per-field basis, since that is a 116 | more general behaviour (e.g. all tuples may sensibly be `FloatEq` but not 117 | `FloatEqAll`). If existing checks against arrays or `num::Complex` break, 118 | switching to use the `_all` variants ought to fix them. 119 | - Support for tuples of up to size 12 (inclusive). 120 | - Documentation on how to interpret assert error messages. 121 | 122 | ### Changed 123 | - ULPs checks now treat `NaN` values as not equal, to match the default partial 124 | equality behaviour of floats. 125 | - Equality of infinities is now consistent, both internally and with respect to 126 | general IEEE floating point behaviour. 127 | - `FloatEq` now more specifically means equality based on a structurally defined 128 | epsilon type. See the notes on `FloatEqAll` above. 129 | - `FloatDiff` is much more rigorously defined, in particular `ulps_diff` now 130 | returns `Option`, see the API documentation for details. 131 | - `FloatEq`'s `DiffEpsilon` is now `Epsilon`, and `UlpsDiffEpsilon` is now 132 | `UlpsTol`. This reduces visual noise around the usage and harmonizes the 133 | naming with `FloatEqDebug`'s associated types. 134 | - Directed docs.rs to build documentation for all features. 135 | 136 | ## [0.2.0] - 2020-04-12 137 | Bumped up the version number since this release includes breaking API changes. 138 | 139 | ### Added 140 | - Implementation of traits for arrays of size 0 to 32 (inclusive) where the type 141 | allows it. Epsilon is assumed to be uniform across the array being compared. 142 | - The 'num' feature, which when enabled provides support for comparison of 143 | `num::Complex` instances. 144 | - Documentation to help with implementing the traits. 145 | 146 | ### Changed 147 | - `FloatDiff`, `FloatEq` and `FloatEqDebug` along with the macros that use them 148 | now allow for a different `Rhs` type to be specified instead of assuming it is 149 | always `Self`. 150 | - The somewhat awkward `FloatEq::rel_epsilon` was removed in favour of a more 151 | equitable `FloatEqDebug` trait for displaying debug information. 152 | - Asserts now correctly dereference parameters. 153 | - Somewhat more streamlined assert error messages that allow for easier direct 154 | comparison of diffs and epsilons. 155 | - Switched back to being 'experimental' on crates.io since the API might change 156 | a lot in the next few versions. 157 | 158 | ## [0.1.3] - 2020-04-07 159 | ### Added 160 | - Added codecov.io and coveralls.io support. 161 | - Added 'actively-developed' maintenance badge. 162 | - Basic usage documentation. 163 | 164 | ## [0.1.2] - 2020-04-07 165 | ### Added 166 | - Support for `no_std`. 167 | - Added the crate to the No Standard Library crates.io category. 168 | - Tests for `Float::abs_diff` on NaNs. 169 | - Travis CI build. 170 | - A Contributing section to the readme. 171 | 172 | ### Fixed 173 | - FloatDiff tests for f64 no longer test f32 values (oops!). 174 | 175 | ## [0.1.1] - 2020-04-05 176 | ### Added 177 | - Added this changelog. 178 | - Reworked the READMEs to be a bit more granular and provide more appropriate 179 | information. 180 | - The API documentation `lib.rs` has been pared down a bit and several 181 | sections moved to other places. 182 | - `crates-io.md` contains a short project introduction, links to alternative 183 | crates and future plans. 184 | - `README.md` is generated from `crates-io.md` and `LICENSE.md`. 185 | - Updated Cargo.toml: 186 | - Included a link to the documentation. 187 | - Added the crate to the Development Tools (Debugging) category. 188 | 189 | ## [0.1.0] - 2020-04-05 190 | ### Added 191 | - Initial release. 192 | 193 | [Unreleased]: https://github.com/jtempest/float_eq-rs/compare/1.0.1...HEAD 194 | [1.0.1]: https://github.com/jtempest/float_eq-rs/releases/tag/1.0.1 195 | [1.0.0]: https://github.com/jtempest/float_eq-rs/releases/tag/1.0.0 196 | [0.7.0]: https://github.com/jtempest/float_eq-rs/releases/tag/0.7.0 197 | [0.6.1]: https://github.com/jtempest/float_eq-rs/releases/tag/0.6.1 198 | [0.6.0]: https://github.com/jtempest/float_eq-rs/releases/tag/0.6.0 199 | [0.5.0]: https://github.com/jtempest/float_eq-rs/releases/tag/0.5.0 200 | [0.4.1]: https://github.com/jtempest/float_eq-rs/releases/tag/0.4.1 201 | [0.4.0]: https://github.com/jtempest/float_eq-rs/releases/tag/0.4.0 202 | [0.3.1]: https://github.com/jtempest/float_eq-rs/releases/tag/0.3.1 203 | [0.3.0]: https://github.com/jtempest/float_eq-rs/releases/tag/0.3.0 204 | [0.2.0]: https://github.com/jtempest/float_eq-rs/releases/tag/0.2.0 205 | [0.1.3]: https://github.com/jtempest/float_eq-rs/releases/tag/0.1.3 206 | [0.1.2]: https://github.com/jtempest/float_eq-rs/releases/tag/0.1.2 207 | [0.1.1]: https://github.com/jtempest/float_eq-rs/releases/tag/0.1.1 208 | [0.1.0]: https://github.com/jtempest/float_eq-rs/releases/tag/0.1.0 -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "float_eq", 4 | "float_eq_derive" 5 | ] -------------------------------------------------------------------------------- /HOW-TO-RELEASE-CRATE.txt: -------------------------------------------------------------------------------- 1 | # A checklist/sequence of actions to take to package a new build and upload it 2 | # to crates.io. 3 | 4 | - Ensure all tests pass: 5 | -`cargo hack test --feature-powerset` 6 | - Make sure documentation is up to date: 7 | - `./docs-build.bat --open` 8 | - crates-io.md 9 | - Run generate-readme.bat 10 | - Make sure CHANGELOG is up to date: 11 | - Change Unreleased -> version 12 | - add link to tag (note that this won't work until the tag has been pushed) 13 | - add release date 14 | - Update version numbers in: 15 | - float_eq/Cargo.toml 16 | - float_eq_derive/Cargo.toml 17 | - float_eq/crates-io.md 18 | - book/book.toml 19 | - book/src/how_to/derive_the_traits.md 20 | - Make release tag in git 21 | - git tag -s x.y.z -m "x.y.z release" 22 | - git push origin --tags 23 | - Check CI is building 24 | - Check links in CHANGELOG and badges work to new release tag 25 | - Release docs (HOW-TO-RELEASE-DOCS.txt) 26 | - Check `cargo package` for float_eq_derive 27 | - `cargo publish` float_eq_derive 28 | - Check `cargo package` for float_eq 29 | - `cargo publish` for float_eq 30 | - Update CHANGELOG.md to Reinstate Unreleased section 31 | - Update version numbers to x.y.z-pre with an incremented z in Cargo.tomls -------------------------------------------------------------------------------- /HOW-TO-RELEASE-DOCS.txt: -------------------------------------------------------------------------------- 1 | # Follow these commands to deploy the cargo documentation and md book to the 2 | # github pages branch. This is not a script since if something goes wrong during 3 | # the process the repo could become seriously messed up, so be careful. 4 | 5 | # 1) Build docs 6 | cargo clean 7 | ./docs-build.bat 8 | 9 | # 2) Temporarily add gh-pages branch to workspace (be careful!) 10 | git worktree add gh-pages gh-pages 11 | 12 | # 3) Refresh contents of gh-pages branch 13 | Remove-Item -Recurse gh-pages/* 14 | New-Item -Name gh-pages/book/ -ItemType Directory 15 | Copy-Item -Recurse target/book/* gh-pages/book/ 16 | New-Item -Name gh-pages/doc/ -ItemType Directory 17 | Copy-Item -Recurse target/doc/* gh-pages/doc/ 18 | Copy-Item docs-index.html gh-pages/index.html 19 | 20 | # 4) Commit new gh-pages content (be careful!) 21 | Set-Location gh-pages 22 | git add -A 23 | git commit -S -m "docs deployment" 24 | git push origin gh-pages 25 | 26 | # 5) Remove temporary gh-pages branch from workspace 27 | Set-Location .. 28 | git worktree remove gh-pages --force 29 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 jtempest 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | #### License 2 | 3 | 4 | Licensed under either of Apache License, Version 5 | 2.0 or MIT license at your option. 6 | 7 | 8 |
9 | 10 | 11 | Unless you explicitly state otherwise, any contribution intentionally submitted 12 | for inclusion in float_eq by you, as defined in the Apache-2.0 license, shall be 13 | dual licensed as above, without any additional terms or conditions. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # float_eq 7 | 8 | [![Build](https://github.com/jtempest/float_eq-rs/actions/workflows/build.yml/badge.svg)](https://github.com/jtempest/float_eq-rs/actions/workflows/build.yml) 9 | [![codecov](https://codecov.io/gh/jtempest/float_eq-rs/branch/main/graph/badge.svg?token=QR20064LIR)](https://codecov.io/gh/jtempest/float_eq-rs) 10 | [![crate](https://img.shields.io/crates/v/float_eq.svg)](https://crates.io/crates/float_eq) 11 | [![documentation](https://docs.rs/float_eq/badge.svg)](https://docs.rs/float_eq) 12 | 13 | Compare IEEE floating point primitives, structs and collections for equality. 14 | 15 | This crate provides an API with a focus on making the choices of comparison 16 | algorithm(s) and tolerances intuitive to implementers and maintainers, and of 17 | providing clear output for debugging and development iteration. 18 | 19 | This readme is a quick tour of the crate. For introductory material, guides and 20 | discussion see [the float_eq guide]. 21 | 22 | ## Usage 23 | 24 | Add this to your cargo.toml: 25 | 26 | ``` 27 | [dependencies] 28 | float_eq = "1" 29 | ``` 30 | 31 | And, if you're using the 2015 edition, this to your crate root: 32 | 33 | ```rust 34 | extern crate float_eq; 35 | ``` 36 | 37 | Then, you can import items with `use`: 38 | 39 | ```rust 40 | use float_eq::{assert_float_eq, float_eq}; 41 | ``` 42 | 43 | ## Comparisons 44 | 45 | This crate provides boolean comparison operations: 46 | 47 | ```rust 48 | if (float_eq!(y_pos, 0.0, abs <= 0.000_1)) { 49 | //... 50 | } 51 | ``` 52 | 53 | And asserts: 54 | 55 | ```rust 56 | const RECIP_REL_TOL: f32 = 0.000_366_210_94; 57 | assert_float_eq!(x.recip(), 10.0, r2nd <= RECIP_REL_TOL); 58 | ``` 59 | 60 | Using absolute tolerance, relative tolerance or ULPs based [comparison 61 | algorithms]. 62 | 63 | ## Composite types 64 | 65 | Composite types may implement the provided extension traits to be compared on a 66 | field-by-field basis: 67 | 68 | ```rust 69 | let a = Complex32 { re: 2.0, im: 4.000_002 }; 70 | let b = Complex32 { re: 2.000_000_5, im: 4.0 }; 71 | 72 | assert_float_eq!(a, b, ulps <= ComplexUlps32 { re: 2, im: 4 }); 73 | ``` 74 | 75 | ...and if they are homogeneous, with a uniformly applied tolerance across all 76 | fields: 77 | 78 | ```rust 79 | assert_float_eq!(a, b, ulps_all <= 4); 80 | ``` 81 | 82 | Arrays of any size are supported: 83 | 84 | ```rust 85 | let a = [1.0, -2.0, 3.0]; 86 | let b = [-1.0, 2.0, 3.5]; 87 | assert_float_eq!(a, b, abs <= [2.0, 4.0, 0.5]); 88 | assert_float_eq!(a, b, abs_all <= 4.0); 89 | ``` 90 | 91 | As are tuples up to size 12 (inclusive): 92 | 93 | ```rust 94 | let a = (1.0f32, 2.0f64); 95 | let b = (1.5f32, -2.0f64); 96 | assert_float_eq!(a, b, r2nd <= (0.5, 2.0)); 97 | ``` 98 | 99 | Many standard and core types like `Vec` are supported: 100 | 101 | ```rust 102 | let a = vec![1.0, -2.0, 3.0]; 103 | let b = vec![-1.0, 2.0, 3.5]; 104 | assert_float_eq!(a, b, rmax <= vec![2.0, 2.0, 0.25]); 105 | assert_float_eq!(a, b, rmax_all <= 2.0); 106 | ``` 107 | 108 | There are blanket trait impls for comparing mutable and immutable reference 109 | types, the contents of `Cell`, `RefCell`, `Rc`, `Arc` and `Box` instances, as 110 | well as for slices, `Option`, `Vec`, `VecDeque`, `LinkedList`, `BTreeMap` and 111 | `HashMap`. 112 | 113 | ## Derivable 114 | 115 | The extension traits may be derived for non-generic structs and tuple structs: 116 | 117 | ```rust 118 | #[derive_float_eq( 119 | ulps_tol = "PointUlps", 120 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 121 | debug_ulps_diff = "PointUlpsDebugUlpsDiff", 122 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq", 123 | all_tol = "f64" 124 | )] 125 | #[derive(Debug, PartialEq, Clone, Copy)] 126 | pub struct Point { 127 | pub x: f64, 128 | pub y: f64, 129 | } 130 | 131 | let a = Point { x: 1.0, y: -2.0 }; 132 | let c = Point { 133 | x: 1.000_000_000_000_000_9, 134 | y: -2.000_000_000_000_001_3 135 | }; 136 | assert_float_eq!(a, c, ulps <= PointUlps { x: 4, y: 3 }); 137 | assert_float_eq!(a, c, ulps_all <= 4); 138 | ``` 139 | 140 | ## Error messages 141 | 142 | Asserts provide additional useful context information. For example: 143 | 144 | ```rust 145 | assert_float_eq!(4.0f32, 4.000_008, rmax <= 0.000_001); 146 | ``` 147 | 148 | Panics with this error message: 149 | 150 | ``` 151 | thread 'main' panicked at 'assertion failed: `float_eq!(left, right, rmax <= t)` 152 | left: `4.0`, 153 | right: `4.000008`, 154 | abs_diff: `0.000008106232`, 155 | ulps_diff: `Some(17)`, 156 | [rmax] t: `0.000004000008`', assert_failure.rs:15:5 157 | ``` 158 | 159 | Where `[rmax] t` shows the tolerance value that the absolute difference was 160 | compared against after being appropriately scaled. 161 | 162 | ## Optional features 163 | 164 | This crate can be used without the standard library (`#![no_std]`) by disabling 165 | the default `std` feature. Use this in `Cargo.toml`: 166 | 167 | ``` 168 | [dependencies.float_eq] 169 | version = "1" 170 | default-features = false 171 | ``` 172 | 173 | Other optional features: 174 | - **derive** — provides custom derive macros for all traits. 175 | - **num** — blanket trait impls for `num::Complex` where it is instanced with a 176 | compatible type. 177 | 178 | ## Related efforts 179 | 180 | The [`approx`], [`float-cmp`], [`assert_float_eq`] and [`is_close`] crates provide 181 | similar floating point comparison capabilities to `float_eq`. The [`almost`] crate 182 | divides its API into comparison of floats against zero and non-zero values. The 183 | [`efloat`] crate provides an `f32` equivalent type that tracks the maximum 184 | possible error bounds that may have occured due to rounding. 185 | 186 | The [`ieee754`] crate is not a comparison library, but provides useful 187 | functionality for decomposing floats into their component parts, iterating over 188 | representable values and working with ULPs directly, amoung other things. 189 | 190 | ## Contributing 191 | 192 | Constructive feedback, suggestions and contributions welcomed, please 193 | [open an issue]. 194 | 195 | ## Changelog 196 | 197 | Release information is available in [CHANGELOG.md](CHANGELOG.md). 198 | 199 | [comparison algorithms]: https://jtempest.github.io/float_eq-rs/book/background/float_comparison_algorithms.html 200 | [open an issue]: https://github.com/jtempest/float_eq-rs/issues/ 201 | [the float_eq guide]: https://jtempest.github.io/float_eq-rs/book/introduction.html 202 | [`almost`]: https://crates.io/crates/almost 203 | [`approx`]: https://crates.io/crates/approx 204 | [`assert_float_eq`]: https://crates.io/crates/assert_float_eq 205 | [`efloat`]: https://crates.io/crates/efloat 206 | [`float-cmp`]: https://crates.io/crates/float-cmp 207 | [`ieee754`]: https://crates.io/crates/ieee754 208 | [`is_close`]: https://docs.rs/is_close/latest/is_close/ 209 | 210 |
211 | 212 | #### License 213 | 214 | 215 | Licensed under either of Apache License, Version 216 | 2.0 or MIT license at your option. 217 | 218 | 219 |
220 | 221 | 222 | Unless you explicitly state otherwise, any contribution intentionally submitted 223 | for inclusion in float_eq by you, as defined in the Apache-2.0 license, shall be 224 | dual licensed as above, without any additional terms or conditions. 225 | -------------------------------------------------------------------------------- /badges.md: -------------------------------------------------------------------------------- 1 | [![Build](https://github.com/jtempest/float_eq-rs/actions/workflows/build.yml/badge.svg)](https://github.com/jtempest/float_eq-rs/actions/workflows/build.yml) 2 | [![codecov](https://codecov.io/gh/jtempest/float_eq-rs/branch/main/graph/badge.svg?token=QR20064LIR)](https://codecov.io/gh/jtempest/float_eq-rs) 3 | [![crate](https://img.shields.io/crates/v/float_eq.svg)](https://crates.io/crates/float_eq) 4 | [![documentation](https://docs.rs/float_eq/badge.svg)](https://docs.rs/float_eq) 5 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["jtempest"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Documentation for float_eq 1.0.1" 7 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](./introduction.md) 4 | - [Tutorials](./tutorials.md) 5 | - [Basic usage](./tutorials/basic_usage.md) 6 | - [How to](./how_to.md) 7 | - [Compare floating point numbers](./how_to/compare_floating_point_numbers.md) 8 | - [Compare composite types](./how_to/compare_composite_types.md) 9 | - [Interpret assert failure messages](./how_to/interpret_assert_failure_messages.md) 10 | - [Compare custom types](./how_to/compare_custom_types.md) 11 | - [Derive the traits](./how_to/derive_the_traits.md) 12 | - [Manually implement the traits](./how_to/manually_implement_the_traits.md) 13 | - [Background](./background.md) 14 | - [Float comparison algorithms](./background/float_comparison_algorithms.md) 15 | - [Resources](./background/resources.md) 16 | - [API documentation](./api_documentation.md) -------------------------------------------------------------------------------- /book/src/api_documentation.md: -------------------------------------------------------------------------------- 1 | # API documentation 2 | 3 | The latest API documentation is available [here] or at [docs.rs]. 4 | 5 | [here]: ../doc/float_eq/index.md 6 | [docs.rs]: https://docs.rs/float_eq/ 7 | -------------------------------------------------------------------------------- /book/src/background.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | - [Float comparison algorithms](./background/float_comparison_algorithms.md) 4 | - [Resources](./background/resources.md) -------------------------------------------------------------------------------- /book/src/background/float_comparison_algorithms.md: -------------------------------------------------------------------------------- 1 | # Floating point comparison algorithms 2 | 3 | Descriptions of the underlying comparison algorithms used by [float_eq]. 4 | 5 | ## Absolute tolerance comparison 6 | 7 | ``` 8 | abs <= tol 9 | ``` 10 | 11 | A check to see how far apart two expressions are by comparing the absolute 12 | difference between them to an absolute tolerance. Mathematically, this is: 13 | 14 | ``` 15 | |a - b| <= tol 16 | ``` 17 | 18 | Equivalent to, using `f32` as an example: 19 | 20 | ```rust 21 | fn float_eq_abs(a: f32, b: f32, tol: f32) -> bool { 22 | // the PartialEq check covers equality of infinities 23 | a == b || (a - b).abs() <= tol 24 | } 25 | ``` 26 | 27 | This is the simplest method of testing the equality of two floats and may be 28 | sufficient if you know the absolute margin of error for your calculation given 29 | the values being tested. However, absolute tolerance tests *do not* work well for 30 | general comparison of floating point numbers, because they do not take into 31 | account that normal values' granularity changes with their magnitude. Thus any 32 | given choice of `tol` is likely to work for one specific exponent's range and 33 | poorly outside of it. 34 | 35 | In some circumstances absolute tolerance comparisons are required. If you wish 36 | to compare against zero, an infinity, or subnormal values then the assumptions 37 | that relative tolerance or ULPs based checks make about how neighbouring values 38 | are related to one another break down. Similarly, if the underlying mathematics 39 | of your algorithm is [numerically unstable], for example if it is prone to 40 | [catastrophic cancellation], then you may find that you need to reach for an 41 | absolute tolerance comparison. 42 | 43 | ## Relative tolerance comparison 44 | 45 | ``` 46 | r1st <= tol 47 | r2nd <= tol 48 | rmax <= tol 49 | rmin <= tol 50 | ``` 51 | 52 | A check to see how far apart two expressions are by comparing the absolute 53 | difference between them to an tolerance that is scaled to the granularity of 54 | one of the inputs. Mathematically, this is: 55 | 56 | ``` 57 | |a - b| <= func(|a|, |b|) * tol 58 | ``` 59 | 60 | Equivalent to, using `f32` as an example: 61 | 62 | ```rust 63 | fn float_eq_relative(a: f32, b: f32, tol: f32) -> bool { 64 | // the PartialEq check covers equality of infinities 65 | a == b || { 66 | let chosen = func(a.abs(), b.abs()); 67 | (a - b).abs() <= (chosen * tol) 68 | } 69 | } 70 | ``` 71 | 72 | Where `func` is one of: 73 | - `r1st`: the first input (`a`) 74 | - `r2nd`: the second input (`b`) 75 | - `rmax`: the larger magnitude (aka `rel` for legacy reasons) 76 | - `rmin`: the smaller magnitude 77 | 78 | If you are checking for equality versus an expected normal floating point value 79 | then you may wish to calculate the tolerance based on that value and so using 80 | `r1st` or `r2nd` will allow you to select it. If you are generally testing two 81 | normal floating point values then `rmax` is a good general choice. If either 82 | number may also be subnormal or close to zero, then you may need to calculate a 83 | tolerance based on an intermediate value for an absolute tolerance check 84 | instead. 85 | 86 | Choice of `tol` will depend on the tolerances inherent in the specific 87 | mathematical function or algorithm you have implemented. Note that a tolerance 88 | of `n * EPSILON` (e.g. `f32::EPSILON`) will test that two expressions are within 89 | `n` representable values of another. However, you should be aware that the 90 | errors inherent in your inputs and calculations are likely to be much greater 91 | than the small rounding errors this form would imply. 92 | 93 | ## Units in the Last Place (ULPs) comparison 94 | 95 | ``` 96 | ulps <= tol 97 | ``` 98 | 99 | A check to see how far apart two expressions are by comparing the number of 100 | representable values between them. This works by interpreting the bitwise 101 | representation of the input values as integers and comparing the absolute 102 | difference between those. Equivalent to, using `f32` as an example: 103 | 104 | ```rust 105 | fn float_eq_ulps(a: f32, b: f32, tol: u32) -> bool { 106 | if a.is_nan() || b.is_nan() { 107 | false // NaNs are never equal 108 | } else if a.is_sign_positive() != b.is_sign_positive() { 109 | a == b // values of different signs are only equal if both are zero. 110 | } else { 111 | let a_bits = a.to_bits(); 112 | let b_bits = b.to_bits(); 113 | let max = a_bits.max(b_bits); 114 | let min = a_bits.min(b_bits); 115 | (max - min) <= tol 116 | } 117 | } 118 | ``` 119 | 120 | Thanks to a deliberate quirk in the way the [underlying format] of IEEE floats 121 | was designed, this is a measure of how near two values are that scales with 122 | their relative granularity. Note that `tol` is an unsigned integer, so for 123 | example `ulps <= 4` means *"check that a and b are equal to within a distance of 124 | four or less representable values"*. 125 | 126 | ULPs comparisons are very similar to relative tolerance checks, and as such are 127 | useful for testing equality of normal floats but not for comparisons with zero 128 | or infinity. Additionally, because floats use their most significant bit to 129 | indicate their sign, ULPs comparisons are not valid for comparing values with 130 | different signs. 131 | 132 | [catastrophic cancellation]: https://en.wikipedia.org/wiki/Catastrophic_cancellation 133 | [float_eq]: http://crates.io/crates/float_eq 134 | [numerically unstable]: https://nhigham.com/2020/08/04/what-is-numerical-stability/ 135 | [underlying format]: https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/ -------------------------------------------------------------------------------- /book/src/background/resources.md: -------------------------------------------------------------------------------- 1 | # Resources 2 | 3 | An assortment of articles, papers and books that provide insight into how to approach floating point numerics and error analysis. 4 | 5 | - [The Art of Computer Programming Vol. 2: Seminumerical Algorithms, p214-283](https://www-cs-faculty.stanford.edu/~knuth/taocp.html) - on the accuracy of floating point. 6 | - [Comparing Floating Point Numbers, 2012 Edition](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) - the rest of this series is very good, too. 7 | - [Converting Integers to Floats Using Hyperfocus](https://blog.m-ou.se/floats/) 8 | - [Demystifying Floating Point Precision](https://blog.demofox.org/2017/11/21/floating-point-precision/) 9 | - [Floating-Point Questions Are Endless on stackoverflow.com](https://www.exploringbinary.com/floating-point-questions-are-endless-on-stackoverflow-com/) 10 | - [Floating point error](https://matthew-brett.github.io/teaching/floating_error.html) 11 | - [Float toy](https://evanw.github.io/float-toy/) - a fantastic little demo page for gaining an insight into what the bits in a floating point number represent. 12 | - [IEEE Standard 754 Floating Point Numbers](https://www.geeksforgeeks.org/ieee-standard-754-floating-point-numbers/) 13 | - [Interval arithmetic](https://en.wikipedia.org/wiki/Interval_arithmetic) 14 | - [Loss of significance](https://en.wikipedia.org/wiki/Loss_of_significance) 15 | - [Miscalculating Area and Angles of a Needle-like Triangle](https://people.eecs.berkeley.edu/~wkahan/Triangle.pdf) 16 | - [Numerical Computing with IEEE Floating Point Arithmetic](https://epubs.siam.org/doi/book/10.1137/1.9780898718072) 17 | - [Numerical Stability](https://en.wikipedia.org/wiki/Numerical_stability) 18 | - [On the dynamical stability of Principia's modified Jool system](https://github.com/mockingbirdnest/Principia/wiki/On-the-dynamical-stability-of-Principia's-modified-Jool-system) 19 | - [Posit: thin triangle other tricks (REVEALED!)](http://marc-b-reynolds.github.io/math/2019/02/06/Posit1.html) 20 | - [Round-off/Truncation Errors & Numerical Cancellation](https://home.iitk.ac.in/~pranab/ESO208/rajesh/03-04/Errors.pdf) 21 | - [Sterbenz lemma](https://en.wikipedia.org/wiki/Sterbenz_lemma) 22 | - [Truncation error](https://en.wikipedia.org/wiki/Truncation_error) 23 | - [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) - a comprehensive mathematical treatment. 24 | - [What Every Programmer Should Know About Floating-Point Arithmetic](https://floating-point-gui.de/) - a useful introductory guide with cheat sheets for common programming languages. 25 | - [What is Numerical Stability?](https://nhigham.com/2020/08/04/what-is-numerical-stability/) 26 | - [Why 0.1 Does Not Exist In Floating-Point](https://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/) -------------------------------------------------------------------------------- /book/src/how_to.md: -------------------------------------------------------------------------------- 1 | # How to 2 | 3 | - [Compare floating point numbers](./how_to/compare_floating_point_numbers.md) 4 | - [Compare composite types](./how_to/compare_composite_types.md) 5 | - [Interpret assert failure messages](./how_to/interpret_assert_failure_messages.md) 6 | - [Compare custom types](./how_to/compare_custom_types.md) 7 | - [Derive the traits](./how_to/derive_the_traits.md) 8 | - [Manually implement the traits](./how_to/manually_implement_the_traits.md) -------------------------------------------------------------------------------- /book/src/how_to/compare_composite_types.md: -------------------------------------------------------------------------------- 1 | # How to compare composite types 2 | 3 | 1) Composite types such as arrays, tuples and structs may be compared by 4 | specifying a per-field tolerance in an instance of the same type: 5 | 6 | ```rust 7 | let a = [1.0, -2.0, 3.0]; 8 | let b = [-1.0, 2.0, 3.5]; 9 | assert_float_eq!(a, b, abs <= [2.0, 4.0, 0.5]); 10 | 11 | let c = Complex32 { re: 2.0, im: 4.000_002 }; 12 | let d = Complex32 { re: 2.000_000_5, im: 4.0 }; 13 | assert_float_eq!(c, d, rmax <= Complex32 { re: 0.000_000_25, im: 0.000_000_5 }); 14 | assert_float_eq!(c, d, ulps <= ComplexUlps32 { re: 2, im: 4 }); 15 | ``` 16 | 17 | 2) Homogeneous types may also support the `_all` variants of the checks, which 18 | allow you to specify a single tolerance to use across all fields: 19 | 20 | ```rust 21 | assert_float_eq!(a, b, abs_all <= 4.0); 22 | 23 | assert_float_eq!(c, d, rmax_all <= 0.000_000_5); 24 | assert_float_eq!(c, d, ulps_all <= 4); 25 | ``` 26 | 27 | 3) Checks may be extended over new types by implementing the [extension traits]. 28 | 29 | *Note that to compare num::Complex32 you will need to enable the "num" feature 30 | for float_eq.* 31 | 32 | [extension traits]: ./compare_custom_types.md -------------------------------------------------------------------------------- /book/src/how_to/compare_custom_types.md: -------------------------------------------------------------------------------- 1 | # How to compare custom types 2 | 3 | To extend `float_eq` functionality over a new type requires implenting the 4 | relevant extension traits. 5 | 6 | 1) If your type is a struct or tuple struct that consists of fields that already 7 | implement the required traits, then you may use a derive macro. See [How to 8 | derive the traits]. 9 | 10 | 2) If your type cannot have the traits derived for it, of if you do not wish to 11 | enable the "derive" feature, see [How to manually implement the traits]. 12 | 13 | [How to derive the traits]: ./derive_the_traits.md 14 | [How to manually implement the traits]: ./manually_implement_the_traits.md -------------------------------------------------------------------------------- /book/src/how_to/compare_floating_point_numbers.md: -------------------------------------------------------------------------------- 1 | # How to compare floating point numbers 2 | 3 | 1) Determine which [comparison algorithm] best suits your purposes. 4 | 5 | 2) Determine the tolerance required based on the sources of the error in how the 6 | numbers were entered/calculated. 7 | 8 | 3) If you need a boolean result, then use [`float_eq!`] or [`float_ne!`]. The 9 | two numbers should be the first and second operands, and then the tolerance is 10 | the value after the `<=`. For example, to compare two `f32` numbers stored in 11 | `a` and `b` using a relative tolerance of `tol` scaled to the magnitude of the 12 | second operand: 13 | 14 | ```rust 15 | float_eq!(a, b, r2nd <= tol) 16 | ``` 17 | 18 | 4) If instead you wish to assert that the two numbers are equal and panic if 19 | they are not, you can use [`assert_float_eq!`] or [`assert_float_ne!`]. The 20 | syntax is the same, and may optionally include formatted text after the 21 | comparisons as with standard Rust asserts: 22 | 23 | ```rust 24 | assert_float_eq!(a, b, r2nd <= tol); 25 | assert_float_eq!(a, b, r2nd <= tol, "Example context: {}", context); 26 | ``` 27 | 28 | [comparison algorithm]: ../background/float_comparison_algorithms.md 29 | [`assert_float_eq!`]: ../../doc/float_eq/macro.assert_float_eq.html 30 | [`assert_float_ne!`]: ../../doc/float_eq/macro.assert_float_ne.html 31 | [`float_eq!`]: ../../doc/float_eq/macro.float_eq.html 32 | [`float_ne!`]: ../../doc/float_eq/macro.float_ne.html -------------------------------------------------------------------------------- /book/src/how_to/derive_the_traits.md: -------------------------------------------------------------------------------- 1 | # How to derive the traits 2 | 3 | This article will explain how to enable a new type for use with the [float_eq!], 4 | [float_ne!], [assert_float_eq!] and [assert_float_ne!] macros. However, deriving 5 | the necessary traits is currently only possible if this type is a struct or 6 | tuple struct and is not generic. If not, or if you do not wish to enable the 7 | derive feature, see [How to manually implement the traits]. 8 | 9 | ## Enabling the derive feature 10 | 11 | Enable the optional "derive" feature in your Cargo.toml: 12 | 13 | ```toml 14 | [dependencies.float_eq] 15 | version = "1" 16 | features = ["derive"] 17 | ``` 18 | 19 | ## Deriving the required traits 20 | 21 | Add [`#[derive_float_eq]`](../../doc/float_eq/attr.derive_float_eq.html) to the 22 | new type. For example: 23 | 24 | ```rust 25 | #[derive_float_eq( 26 | ulps_tol = "PointUlps", 27 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 28 | debug_ulps_diff = "PointDebugUlpsDiff", 29 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 30 | )] 31 | #[derive(Debug, PartialEq, Clone, Copy)] 32 | struct Point { 33 | pub x: f64, 34 | pub y: f64, 35 | } 36 | ``` 37 | 38 | The parameters: 39 | - `ulps_tol`: required, will name a new type to provide per-field [ULPs] tolerances. 40 | - `ulps_tol_derive`: optional, provides a list of traits to derive on the `ulps_tol` type. 41 | - `debug_ulps_diff`: required, will name a new type used to display per-field [ULPs] differences. 42 | - `debug_ulps_diff_derive`: optional, provides a list of traits to derive on the `debug_ulps_diff` type. 43 | 44 | This will implement two new types: 45 | 46 | ```rust 47 | #[derive(Debug, Clone, Copy, PartialEq)] 48 | struct PointUlps { 49 | pub x: UlpsTol, 50 | pub y: UlpsTol, 51 | } 52 | 53 | #[derive(Debug, Clone, Copy, PartialEq)] 54 | struct PointDebugUlpsDiff { 55 | pub x: DebugUlpsDiff, 56 | pub y: DebugUlpsDiff, 57 | } 58 | ``` 59 | 60 | This will also implement [FloatEqUlpsTol], [FloatEq], [FloatEqDebugUlpsDiff] and 61 | [AssertFloatEq] for your type. You may now compare it as a composite type: 62 | 63 | ```rust 64 | let a = Point { x: 1.0, y: -2.0 }; 65 | let b = Point { x: 1.1, y: -2.2 }; 66 | let c = Point { x: 1.000_000_000_000_000_9, y: -2.000_000_000_000_001_3 }; 67 | let eps = f64::EPSILON; 68 | 69 | assert_float_eq!(a, b, abs <= Point { x: 0.15, y: 0.25 }); 70 | assert_float_eq!(a, c, rmax <= Point { x: 4.0 * eps, y: 5.0 * eps }); 71 | assert_float_eq!(a, c, ulps <= PointUlps { x: 4, y: 3 }); 72 | ``` 73 | 74 | ## Enabling the `_all` variants of checks 75 | 76 | If your type is homogeneous, that is if it consists of fields that are all the 77 | same underlying floating point type, then you may provide the optional `all_tol` 78 | parameter to `#[derive_float_eq]` with that underlying type (usually `f32` or 79 | `f64`). This will enable the `_all` variants of checks for it: 80 | 81 | ```rust 82 | #[derive_float_eq( 83 | ulps_tol = "PointUlps", 84 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 85 | debug_ulps_diff = "PointDebugUlpsDiff", 86 | debug_ulps_diflf_derive = "Clone, Copy, Debug, PartialEq", 87 | all_tol = "f64" 88 | )] 89 | #[derive(Debug, PartialEq, Clone, Copy)] 90 | struct Point { 91 | pub x: f64, 92 | pub y: f64, 93 | } 94 | ``` 95 | 96 | This will additionally implement the [FloatEqAll] and [AssertFloatEqAll] for 97 | your type. You may now compare it using the `_all` check variants: 98 | 99 | ```rust 100 | let a = Point { x: 1.0, y: -2.0 }; 101 | let b = Point { x: 1.1, y: -2.2 }; 102 | let c = Point { x: 1.000_000_000_000_000_9, y: -2.000_000_000_000_001_3 }; 103 | let eps = f64::EPSILON; 104 | 105 | assert_float_eq!(a, b, abs_all <= 0.25); 106 | assert_float_eq!(a, c, rmax_all <= 5.0 * eps); 107 | assert_float_eq!(a, c, ulps_all <= 4); 108 | ``` 109 | 110 | ## Deriving individual traits 111 | 112 | The `#[derive_float_eq]` macro is recommended but if required you may implement 113 | the traits by individually deriving them. The `#[float_eq]` attribute should be 114 | used to pass through the relevant parameters. For example: 115 | 116 | ```rust 117 | #[derive( 118 | Debug, PartialEq, Clone, Copy, FloatEqUlpsTol, FloatEq, 119 | FloatEqDebugUlpsDiff, AssertFloatEq 120 | )] 121 | #[float_eq( 122 | ulps_tol = "PointUlps", 123 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 124 | debug_ulps_diff = "PointDebugUlpsDiff", 125 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 126 | )] 127 | struct Point { 128 | pub x: f64, 129 | pub y: f64, 130 | } 131 | ``` 132 | 133 | The [float_eq!] and [float_ne!] macros require [FloatEqUlpsTol] and [FloatEq] 134 | and may optionally use [FloatEqAll]. Likewise, [assert_float_eq!] and 135 | [assert_float_ne!] require [FloatEqDebugUlpsDiff] and [AssertFloatEq] and may 136 | optionally use [AssertFloatEqAll]. 137 | 138 | | Trait | Requires | Parameters | 139 | |------------------------|-------------------------------|---------------------------------------------| 140 | | [FloatEqUlpsTol] | | `ulps_tol`, `ulps_tol_derive` | 141 | | [FloatEq] | FloatEqUlpsTol | | 142 | | [FloatEqAll] | FloatEq | `all_tol` | 143 | | [FloatEqDebugUlpsDiff] | | `debug_ulps_diff`, `debug_ulps_diff_derive` | 144 | | [AssertFloatEq] | FloatEq, FloatEqDebugUlpsDiff | | 145 | | [AssertFloatEqAll] | AssertFloatEq, FloatEqAll | `all_tol` | 146 | 147 | [float_eq!]: ../../doc/float_eq/macro.float_eq.html 148 | [float_ne!]: ../../doc/float_eq/macro.float_ne.html 149 | [assert_float_eq!]: ../../doc/float_eq/macro.assert_float_eq.html 150 | [assert_float_ne!]: ../../doc/float_eq/macro.assert_float_ne.html 151 | [AssertFloatEq]: ../../doc/float_eq/trait.AssertFloatEq.html 152 | [AssertFloatEqAll]: ../../doc/float_eq/trait.AssertFloatEqAll.html 153 | [FloatEq]: ../../doc/float_eq/trait.FloatEq.html 154 | [FloatEqAll]: ../../doc/float_eq/trait.FloatEqAll.html 155 | [FloatEqDebugUlpsDiff]: ../../doc/float_eq/trait.FloatEqDebugUlpsDiff.html 156 | [FloatEqUlpsTol]: ../../doc/float_eq/trait.FloatEqUlpsTol.html 157 | [How to manually implement the traits]: ./manually_implement_the_traits.md 158 | [ULPs]: ../background/float_comparison_algorithms.md#units-in-the-last-place-ulps-comparison -------------------------------------------------------------------------------- /book/src/how_to/interpret_assert_failure_messages.md: -------------------------------------------------------------------------------- 1 | # How to interpret assert failure messages 2 | 3 | Assertion failure messages provide context information that hopefully helps 4 | in determining how a check failed. For example, this line: 5 | 6 | ```rust 7 | assert_float_eq!(4.0f32, 4.000_008, rmax <= 0.000_001); 8 | ``` 9 | 10 | Panics with this error message: 11 | 12 | ```text 13 | thread 'main' panicked at 'assertion failed: `float_eq!(left, right, rmax <= t)` 14 | left: `4.0`, 15 | right: `4.000008`, 16 | abs_diff: `0.000008106232`, 17 | ulps_diff: `Some(17)`, 18 | [rmax] t: `0.000004000008`', assert_failure.rs:15:5 19 | ``` 20 | 21 | Where: 22 | - **rmax <= t** - indicates the type of comparison which was carried out. 23 | - **left** - the value of the first operand. 24 | - **right** - the value of the second operand. 25 | - **abs_diff** - the absolute difference between `left` and `right`. 26 | - **ulps_diff** - the difference between `left` and `right` in ULPs. If it is 27 | None, that is because they have different signs or at least one is `NaN`. 28 | - **[rmax] t** - the tolerance used in the comparison against the relevant 29 | difference, here `abs_diff`, *after* it has been scaled relative to an operand, 30 | in this case `max(left, right)` since it is `rmax`. -------------------------------------------------------------------------------- /book/src/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | [Floating point types] have [a reputation] for being [difficult to use] compared 4 | to integer types. This is partly because they have unintuitive sources of 5 | rounding error but it also stems from the kinds of calculations they are used 6 | for, which contain plentiful other sources of [numeric error]. 7 | 8 | The [float_eq] crate provides an API for comparing floating point primitives, 9 | structs and collections for equality that allows users to communicate their 10 | reasoning and intent with respect to numeric error. 11 | 12 | This guide is not designed to be read from cover to cover, but instead is a 13 | series of categorised articles. If you are unsure of how floats work or why they 14 | are considered difficult to use then begin with the [Basic Usage](./tutorials/basic_usage.md) 15 | guide, otherwise check out one of the categories for more: 16 | 17 | - [Tutorials](./tutorials.md): getting started. 18 | - [How to guides](./how_to.md): solutions to specific problems. 19 | - [Background](./background.md): explanation and discussion. 20 | - [API documentation](./api_documentation.md): detailed technical reference. 21 | 22 | [a reputation]: https://www.exploringbinary.com/floating-point-questions-are-endless-on-stackoverflow-com/ 23 | [difficult to use]: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ 24 | [float_eq]: http://crates.io/crates/float_eq 25 | [Floating point types]: https://floating-point-gui.de/formats/fp/ 26 | [numeric error]: https://en.wikipedia.org/wiki/Numerical_error 27 | 28 | ----------- 29 | 30 | Thanks go to everyone who has provided feedback and patiently answered my questions, particularly Robin Leroy. This guide's structure was inspired by the [Diátaxis Framework]. 31 | 32 | [Diátaxis Framework]: https://diataxis.fr/ -------------------------------------------------------------------------------- /book/src/tutorials.md: -------------------------------------------------------------------------------- 1 | # Tutorials 2 | 3 | - [Basic Usage](./tutorials/basic_usage.md) -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "float_eq/tests/*.rs" 3 | - "float_eq/tests/**/*" 4 | - "float_eq_derive/**/*" -------------------------------------------------------------------------------- /docs-build.bat: -------------------------------------------------------------------------------- 1 | @echo OFF 2 | cargo doc -p float_eq --all-features --no-deps 3 | mdbook build book -d ../target/book %* -------------------------------------------------------------------------------- /docs-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Redirecting to https://jtempest.github.io/float_eq-rs/book/ 4 | 5 | -------------------------------------------------------------------------------- /float_eq/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "float_eq" 3 | version = "1.0.2-pre" 4 | authors = ["jtempest"] 5 | license = "MIT OR Apache-2.0" 6 | description = "Compare IEEE floating point primitives, structs and collections for equality." 7 | homepage = "https://jtempest.github.io/float_eq-rs/" 8 | repository = "https://github.com/jtempest/float_eq-rs" 9 | documentation = "https://jtempest.github.io/float_eq-rs/book/" 10 | keywords = ["approximate", "assert", "comparison", "equality", "float"] 11 | categories = ["algorithms", "development-tools::debugging", "no-std"] 12 | readme = "crates-io.md" 13 | include = ["Cargo.toml", "src/**/*.rs", "crates-io.md", "LICENSE-APACHE", "LICENSE-MIT"] 14 | edition = "2018" 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [badges] 20 | maintenance = { status = "experimental" } 21 | travis-ci = { repository = "jtempest/float_eq-rs" } 22 | # Turned off for now since coveralls has better support for testing multiple 23 | # feature sets, and thus is more representative. 24 | # codecov = { repository = "jtempest/float_eq-rs" } 25 | coveralls = { repository = "jtempest/float_eq-rs" } 26 | 27 | [dev-dependencies] 28 | trybuild = "1" 29 | 30 | [dependencies.num-complex] 31 | version = "0.4" 32 | optional = true 33 | 34 | [dependencies.float_eq_derive] 35 | version = "=1.0.2-pre" 36 | optional = true 37 | path = "../float_eq_derive" 38 | 39 | [features] 40 | default = ["std"] 41 | std = [] 42 | num = ["num-complex"] 43 | derive = ["float_eq_derive"] -------------------------------------------------------------------------------- /float_eq/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 jtempest 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /float_eq/crates-io.md: -------------------------------------------------------------------------------- 1 | # float_eq 2 | 3 | Compare IEEE floating point primitives, structs and collections for equality. 4 | 5 | This crate provides an API with a focus on making the choices of comparison 6 | algorithm(s) and tolerances intuitive to implementers and maintainers, and of 7 | providing clear output for debugging and development iteration. 8 | 9 | This readme is a quick tour of the crate. For introductory material, guides and 10 | discussion see [the float_eq guide]. 11 | 12 | ## Usage 13 | 14 | Add this to your cargo.toml: 15 | 16 | ``` 17 | [dependencies] 18 | float_eq = "1" 19 | ``` 20 | 21 | And, if you're using the 2015 edition, this to your crate root: 22 | 23 | ```rust 24 | extern crate float_eq; 25 | ``` 26 | 27 | Then, you can import items with `use`: 28 | 29 | ```rust 30 | use float_eq::{assert_float_eq, float_eq}; 31 | ``` 32 | 33 | ## Comparisons 34 | 35 | This crate provides boolean comparison operations: 36 | 37 | ```rust 38 | if (float_eq!(y_pos, 0.0, abs <= 0.000_1)) { 39 | //... 40 | } 41 | ``` 42 | 43 | And asserts: 44 | 45 | ```rust 46 | const RECIP_REL_TOL: f32 = 0.000_366_210_94; 47 | assert_float_eq!(x.recip(), 10.0, r2nd <= RECIP_REL_TOL); 48 | ``` 49 | 50 | Using absolute tolerance, relative tolerance or ULPs based [comparison 51 | algorithms]. 52 | 53 | ## Composite types 54 | 55 | Composite types may implement the provided extension traits to be compared on a 56 | field-by-field basis: 57 | 58 | ```rust 59 | let a = Complex32 { re: 2.0, im: 4.000_002 }; 60 | let b = Complex32 { re: 2.000_000_5, im: 4.0 }; 61 | 62 | assert_float_eq!(a, b, ulps <= ComplexUlps32 { re: 2, im: 4 }); 63 | ``` 64 | 65 | ...and if they are homogeneous, with a uniformly applied tolerance across all 66 | fields: 67 | 68 | ```rust 69 | assert_float_eq!(a, b, ulps_all <= 4); 70 | ``` 71 | 72 | Arrays of any size are supported: 73 | 74 | ```rust 75 | let a = [1.0, -2.0, 3.0]; 76 | let b = [-1.0, 2.0, 3.5]; 77 | assert_float_eq!(a, b, abs <= [2.0, 4.0, 0.5]); 78 | assert_float_eq!(a, b, abs_all <= 4.0); 79 | ``` 80 | 81 | As are tuples up to size 12 (inclusive): 82 | 83 | ```rust 84 | let a = (1.0f32, 2.0f64); 85 | let b = (1.5f32, -2.0f64); 86 | assert_float_eq!(a, b, r2nd <= (0.5, 2.0)); 87 | ``` 88 | 89 | Many standard and core types like `Vec` are supported: 90 | 91 | ```rust 92 | let a = vec![1.0, -2.0, 3.0]; 93 | let b = vec![-1.0, 2.0, 3.5]; 94 | assert_float_eq!(a, b, rmax <= vec![2.0, 2.0, 0.25]); 95 | assert_float_eq!(a, b, rmax_all <= 2.0); 96 | ``` 97 | 98 | There are blanket trait impls for comparing mutable and immutable reference 99 | types, the contents of `Cell`, `RefCell`, `Rc`, `Arc` and `Box` instances, as 100 | well as for slices, `Option`, `Vec`, `VecDeque`, `LinkedList`, `BTreeMap` and 101 | `HashMap`. 102 | 103 | ## Derivable 104 | 105 | The extension traits may be derived for non-generic structs and tuple structs: 106 | 107 | ```rust 108 | #[derive_float_eq( 109 | ulps_tol = "PointUlps", 110 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 111 | debug_ulps_diff = "PointUlpsDebugUlpsDiff", 112 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq", 113 | all_tol = "f64" 114 | )] 115 | #[derive(Debug, PartialEq, Clone, Copy)] 116 | pub struct Point { 117 | pub x: f64, 118 | pub y: f64, 119 | } 120 | 121 | let a = Point { x: 1.0, y: -2.0 }; 122 | let c = Point { 123 | x: 1.000_000_000_000_000_9, 124 | y: -2.000_000_000_000_001_3 125 | }; 126 | assert_float_eq!(a, c, ulps <= PointUlps { x: 4, y: 3 }); 127 | assert_float_eq!(a, c, ulps_all <= 4); 128 | ``` 129 | 130 | ## Error messages 131 | 132 | Asserts provide additional useful context information. For example: 133 | 134 | ```rust 135 | assert_float_eq!(4.0f32, 4.000_008, rmax <= 0.000_001); 136 | ``` 137 | 138 | Panics with this error message: 139 | 140 | ``` 141 | thread 'main' panicked at 'assertion failed: `float_eq!(left, right, rmax <= t)` 142 | left: `4.0`, 143 | right: `4.000008`, 144 | abs_diff: `0.000008106232`, 145 | ulps_diff: `Some(17)`, 146 | [rmax] t: `0.000004000008`', assert_failure.rs:15:5 147 | ``` 148 | 149 | Where `[rmax] t` shows the tolerance value that the absolute difference was 150 | compared against after being appropriately scaled. 151 | 152 | ## Optional features 153 | 154 | This crate can be used without the standard library (`#![no_std]`) by disabling 155 | the default `std` feature. Use this in `Cargo.toml`: 156 | 157 | ``` 158 | [dependencies.float_eq] 159 | version = "1" 160 | default-features = false 161 | ``` 162 | 163 | Other optional features: 164 | - **derive** — provides custom derive macros for all traits. 165 | - **num** — blanket trait impls for `num::Complex` where it is instanced with a 166 | compatible type. 167 | 168 | ## Related efforts 169 | 170 | The [`approx`], [`float-cmp`], [`assert_float_eq`] and [`is_close`] crates provide 171 | similar floating point comparison capabilities to `float_eq`. The [`almost`] crate 172 | divides its API into comparison of floats against zero and non-zero values. The 173 | [`efloat`] crate provides an `f32` equivalent type that tracks the maximum 174 | possible error bounds that may have occured due to rounding. 175 | 176 | The [`ieee754`] crate is not a comparison library, but provides useful 177 | functionality for decomposing floats into their component parts, iterating over 178 | representable values and working with ULPs directly, amoung other things. 179 | 180 | ## Contributing 181 | 182 | Constructive feedback, suggestions and contributions welcomed, please 183 | [open an issue]. 184 | 185 | ## Changelog 186 | 187 | Release information is available in [CHANGELOG.md](CHANGELOG.md). 188 | 189 | [comparison algorithms]: https://jtempest.github.io/float_eq-rs/book/background/float_comparison_algorithms.html 190 | [open an issue]: https://github.com/jtempest/float_eq-rs/issues/ 191 | [the float_eq guide]: https://jtempest.github.io/float_eq-rs/book/introduction.html 192 | [`almost`]: https://crates.io/crates/almost 193 | [`approx`]: https://crates.io/crates/approx 194 | [`assert_float_eq`]: https://crates.io/crates/assert_float_eq 195 | [`efloat`]: https://crates.io/crates/efloat 196 | [`float-cmp`]: https://crates.io/crates/float-cmp 197 | [`ieee754`]: https://crates.io/crates/ieee754 198 | [`is_close`]: https://docs.rs/is_close/latest/is_close/ 199 | -------------------------------------------------------------------------------- /float_eq/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Compare IEEE floating point primitives, structs and collections for equality. 2 | //! 3 | //! This is API reference documentation. For introductory material, guides and 4 | //! discussion see [the float_eq guide]. 5 | //! 6 | //! # Basic usage 7 | //! 8 | //! This crate provides boolean comparisons via [`float_eq!`] and [`float_ne!`]: 9 | //! 10 | //! ``` 11 | //! use float_eq::float_eq; 12 | //! 13 | //! # let y_pos = 0.000_1; 14 | //! if (float_eq!(y_pos, 0.0, abs <= 0.000_1)) { 15 | //! //... 16 | //! } 17 | //! ``` 18 | //! 19 | //! And asserts via [`assert_float_eq!`] and [`assert_float_ne!`]: 20 | //! 21 | //! ``` 22 | //! use float_eq::assert_float_eq; 23 | //! 24 | //! const TOL: f32 = 0.000_366_210_94; 25 | //! assert_float_eq!(0.1f32.recip(), 10.0, r2nd <= TOL); 26 | //! ``` 27 | //! 28 | //! Each of which invokes a specific comparison algorithm with an explictly 29 | //! provided toelrance. In these examples: 30 | //! 31 | //! - `abs <= 0.000_1` is an absolute tolerance comparison with a tolerance of `0.000_1`. 32 | //! - `r2nd <= TOL` is a relative tolerance comparison with a tolerance of `TOL`, 33 | //! scaled to the precision of the second operand. 34 | //! 35 | //! # Comparison algorithms 36 | //! 37 | //! These are always of the form `CHECK <= tol`, where `CHECK` is one of: 38 | //! 39 | //! - `abs`: an [absolute tolerance comparison]. 40 | //! - `rmax`: a [relative tolerance comparison], scaled to the precision of the larger operand/field. 41 | //! - `rmin`: a [relative tolerance comparison], scaled to the precision of the smaller operand/field. 42 | //! - `r1st`: a [relative tolerance comparison], scaled to the precision of the first operand/field. 43 | //! - `r2nd`: a [relative tolerance comparison], scaled to the precision of the second operand/field. 44 | //! - `ulps`: an [ULPs comparison]. 45 | //! 46 | //! When comparing homogeneous composite types that implement [`FloatEqAll`], 47 | //! variants that use a uniform `tol` across all fields are also available: 48 | //! 49 | //! - `abs_all`: an [absolute tolerance comparison]. 50 | //! - `rmax_all`: a [relative tolerance comparison], scaled to the precision of the larger field. 51 | //! - `rmin_all`: a [relative tolerance comparison], scaled to the precision of the smaller field. 52 | //! - `r1st_all`: a [relative tolerance comparison], scaled to the precision of the first field. 53 | //! - `r2nd_all`: a [relative tolerance comparison], scaled to the precision of the second field. 54 | //! - `ulps_all`: an [ULPs comparison]. 55 | //! 56 | //! *Note: `rel` and `rel_all` are legacy aliases for `rmax` and `rmax_all`, but 57 | //! using the more precise algorithm names is recommended.* 58 | //! 59 | //! # Combining checks 60 | //! 61 | //! If multiple checks are specified in either a boolean comparison or an assert, 62 | //! they are applied left to right and will shortcut on success. For example: 63 | //! 64 | //! ``` 65 | //! # use float_eq::float_eq; 66 | //! # let a = 0.1; let b = 0.1; let abs_tol = 0.0; let ulps_tol: u64 = 0; 67 | //! float_eq!(a, b, abs <= abs_tol, ulps <= ulps_tol) 68 | //! # ; 69 | //! ``` 70 | //! 71 | //! Is equivalent to: 72 | //! 73 | //! ``` 74 | //! # use float_eq::float_eq; 75 | //! # let a = 0.1; let b = 0.1; let abs_tol = 0.0; let ulps_tol: u64 = 0; 76 | //! float_eq!(a, b, abs <= abs_tol) || float_eq!(a, b, ulps <= ulps_tol) 77 | //! # ; 78 | //! ``` 79 | //! 80 | //! # Extending float_eq over custom types 81 | //! 82 | //! See [How to compare custom types]. 83 | //! 84 | //! [How to compare custom types]: https://jtempest.github.io/float_eq-rs/book/how_to/compare_custom_types.html 85 | //! [the float_eq guide]: https://jtempest.github.io/float_eq-rs/book/index.html 86 | //! [absolute tolerance comparison]: https://jtempest.github.io/float_eq-rs/book/background/float_comparison_algorithms.html#absolute-tolerance-comparison 87 | //! [relative tolerance comparison]: https://jtempest.github.io/float_eq-rs/book/background/float_comparison_algorithms.html#relative-tolerance-comparison 88 | //! [ULPs comparison]: https://jtempest.github.io/float_eq-rs/book/background/float_comparison_algorithms.html#units-in-the-last-place-ulps-comparison 89 | 90 | #![warn(missing_docs)] 91 | #![cfg_attr(not(feature = "std"), no_std)] 92 | 93 | #[macro_use] 94 | mod macros; 95 | pub use crate::macros::*; 96 | 97 | mod traits; 98 | pub use crate::traits::*; 99 | 100 | mod trait_impls; 101 | pub use crate::trait_impls::*; 102 | 103 | #[cfg(feature = "float_eq_derive")] 104 | pub use float_eq_derive::*; 105 | -------------------------------------------------------------------------------- /float_eq/src/trait_impls.rs: -------------------------------------------------------------------------------- 1 | mod arrays; 2 | mod core_types; 3 | mod primitives; 4 | mod tuples; 5 | 6 | #[cfg(feature = "std")] 7 | mod std_types; 8 | 9 | #[cfg(feature = "num")] 10 | mod num_complex; 11 | #[cfg(feature = "num")] 12 | pub use self::num_complex::*; 13 | -------------------------------------------------------------------------------- /float_eq/src/trait_impls/arrays.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | AssertFloatEq, AssertFloatEqAll, DebugUlpsDiff, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, 3 | FloatEqUlpsTol, UlpsTol, 4 | }; 5 | use core::mem::MaybeUninit; 6 | 7 | // Uses the same technique as MaybeUninit::uninit_array. 8 | #[inline(always)] 9 | fn uninit_array() -> [MaybeUninit; N] { 10 | unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() } 11 | } 12 | 13 | // Uses the same technique as MaybeUninit::array_assume_init. 14 | #[inline(always)] 15 | unsafe fn array_assume_init(array: [MaybeUninit; N]) -> [T; N] { 16 | (&array as *const _ as *const [T; N]).read() 17 | } 18 | 19 | impl FloatEqUlpsTol for [T; N] 20 | where 21 | UlpsTol: Sized, 22 | { 23 | type UlpsTol = [UlpsTol; N]; 24 | } 25 | 26 | impl FloatEqDebugUlpsDiff for [T; N] { 27 | type DebugUlpsDiff = [DebugUlpsDiff; N]; 28 | } 29 | 30 | impl FloatEq<[B; N]> for [A; N] 31 | where 32 | A: FloatEq, 33 | A::Tol: Sized, 34 | UlpsTol: Sized, 35 | { 36 | type Tol = [A::Tol; N]; 37 | 38 | #[inline] 39 | fn eq_abs(&self, other: &[B; N], tol: &Self::Tol) -> bool { 40 | for i in 0..N { 41 | if !self[i].eq_abs(&other[i], &tol[i]) { 42 | return false; 43 | } 44 | } 45 | true 46 | } 47 | 48 | #[inline] 49 | fn eq_rmax(&self, other: &[B; N], tol: &Self::Tol) -> bool { 50 | for i in 0..N { 51 | if !self[i].eq_rmax(&other[i], &tol[i]) { 52 | return false; 53 | } 54 | } 55 | true 56 | } 57 | 58 | #[inline] 59 | fn eq_rmin(&self, other: &[B; N], tol: &Self::Tol) -> bool { 60 | for i in 0..N { 61 | if !self[i].eq_rmin(&other[i], &tol[i]) { 62 | return false; 63 | } 64 | } 65 | true 66 | } 67 | 68 | #[inline] 69 | fn eq_r1st(&self, other: &[B; N], tol: &Self::Tol) -> bool { 70 | for i in 0..N { 71 | if !self[i].eq_r1st(&other[i], &tol[i]) { 72 | return false; 73 | } 74 | } 75 | true 76 | } 77 | 78 | #[inline] 79 | fn eq_r2nd(&self, other: &[B; N], tol: &Self::Tol) -> bool { 80 | for i in 0..N { 81 | if !self[i].eq_r2nd(&other[i], &tol[i]) { 82 | return false; 83 | } 84 | } 85 | true 86 | } 87 | 88 | #[inline] 89 | fn eq_ulps(&self, other: &[B; N], tol: &UlpsTol) -> bool { 90 | for i in 0..N { 91 | if !self[i].eq_ulps(&other[i], &tol[i]) { 92 | return false; 93 | } 94 | } 95 | true 96 | } 97 | } 98 | 99 | impl FloatEqAll<[B; N]> for [A; N] 100 | where 101 | A: FloatEqAll, 102 | { 103 | type AllTol = A::AllTol; 104 | 105 | #[inline] 106 | fn eq_abs_all(&self, other: &[B; N], tol: &Self::AllTol) -> bool { 107 | self.iter() 108 | .zip(other.iter()) 109 | .all(|(a, b)| a.eq_abs_all(b, tol)) 110 | } 111 | 112 | #[inline] 113 | fn eq_rmax_all(&self, other: &[B; N], tol: &Self::AllTol) -> bool { 114 | self.iter() 115 | .zip(other.iter()) 116 | .all(|(a, b)| a.eq_rmax_all(b, tol)) 117 | } 118 | 119 | #[inline] 120 | fn eq_rmin_all(&self, other: &[B; N], tol: &Self::AllTol) -> bool { 121 | self.iter() 122 | .zip(other.iter()) 123 | .all(|(a, b)| a.eq_rmin_all(b, tol)) 124 | } 125 | 126 | #[inline] 127 | fn eq_r1st_all(&self, other: &[B; N], tol: &Self::AllTol) -> bool { 128 | self.iter() 129 | .zip(other.iter()) 130 | .all(|(a, b)| a.eq_r1st_all(b, tol)) 131 | } 132 | 133 | #[inline] 134 | fn eq_r2nd_all(&self, other: &[B; N], tol: &Self::AllTol) -> bool { 135 | self.iter() 136 | .zip(other.iter()) 137 | .all(|(a, b)| a.eq_r2nd_all(b, tol)) 138 | } 139 | 140 | #[inline] 141 | fn eq_ulps_all(&self, other: &[B; N], tol: &UlpsTol) -> bool { 142 | self.iter() 143 | .zip(other.iter()) 144 | .all(|(a, b)| a.eq_ulps_all(b, tol)) 145 | } 146 | } 147 | 148 | impl AssertFloatEq<[B; N]> for [A; N] 149 | where 150 | A: AssertFloatEq, 151 | A::Tol: Sized, 152 | A::DebugTol: Sized, 153 | UlpsTol: Sized, 154 | UlpsTol: Sized, 155 | { 156 | type DebugAbsDiff = [A::DebugAbsDiff; N]; 157 | type DebugTol = [A::DebugTol; N]; 158 | 159 | #[inline] 160 | fn debug_abs_diff(&self, other: &[B; N]) -> Self::DebugAbsDiff { 161 | let mut result: [MaybeUninit; N] = uninit_array(); 162 | for i in 0..N { 163 | result[i] = MaybeUninit::new(self[i].debug_abs_diff(&other[i])); 164 | } 165 | unsafe { array_assume_init(result) } 166 | } 167 | 168 | #[inline] 169 | fn debug_ulps_diff(&self, other: &[B; N]) -> DebugUlpsDiff { 170 | let mut result: [MaybeUninit>; N] = uninit_array(); 171 | for i in 0..N { 172 | result[i] = MaybeUninit::new(self[i].debug_ulps_diff(&other[i])); 173 | } 174 | unsafe { array_assume_init(result) } 175 | } 176 | 177 | #[inline] 178 | fn debug_abs_tol(&self, other: &[B; N], tol: &Self::Tol) -> Self::DebugTol { 179 | let mut result: [MaybeUninit; N] = uninit_array(); 180 | for i in 0..N { 181 | result[i] = MaybeUninit::new(self[i].debug_abs_tol(&other[i], &tol[i])); 182 | } 183 | unsafe { array_assume_init(result) } 184 | } 185 | 186 | #[inline] 187 | fn debug_rmax_tol(&self, other: &[B; N], tol: &Self::Tol) -> Self::DebugTol { 188 | let mut result: [MaybeUninit; N] = uninit_array(); 189 | for i in 0..N { 190 | result[i] = MaybeUninit::new(self[i].debug_rmax_tol(&other[i], &tol[i])); 191 | } 192 | unsafe { array_assume_init(result) } 193 | } 194 | 195 | #[inline] 196 | fn debug_rmin_tol(&self, other: &[B; N], tol: &Self::Tol) -> Self::DebugTol { 197 | let mut result: [MaybeUninit; N] = uninit_array(); 198 | for i in 0..N { 199 | result[i] = MaybeUninit::new(self[i].debug_rmin_tol(&other[i], &tol[i])); 200 | } 201 | unsafe { array_assume_init(result) } 202 | } 203 | 204 | #[inline] 205 | fn debug_r1st_tol(&self, other: &[B; N], tol: &Self::Tol) -> Self::DebugTol { 206 | let mut result: [MaybeUninit; N] = uninit_array(); 207 | for i in 0..N { 208 | result[i] = MaybeUninit::new(self[i].debug_r1st_tol(&other[i], &tol[i])); 209 | } 210 | unsafe { array_assume_init(result) } 211 | } 212 | 213 | #[inline] 214 | fn debug_r2nd_tol(&self, other: &[B; N], tol: &Self::Tol) -> Self::DebugTol { 215 | let mut result: [MaybeUninit; N] = uninit_array(); 216 | for i in 0..N { 217 | result[i] = MaybeUninit::new(self[i].debug_r2nd_tol(&other[i], &tol[i])); 218 | } 219 | unsafe { array_assume_init(result) } 220 | } 221 | 222 | #[inline] 223 | fn debug_ulps_tol(&self, other: &[B; N], tol: &UlpsTol) -> UlpsTol { 224 | let mut result: [MaybeUninit>; N] = uninit_array(); 225 | for i in 0..N { 226 | result[i] = MaybeUninit::new(self[i].debug_ulps_tol(&other[i], &tol[i])); 227 | } 228 | unsafe { array_assume_init(result) } 229 | } 230 | } 231 | 232 | impl AssertFloatEqAll<[B; N]> for [A; N] 233 | where 234 | A: AssertFloatEqAll, 235 | UlpsTol: Sized, 236 | { 237 | type AllDebugTol = [A::AllDebugTol; N]; 238 | 239 | #[inline] 240 | fn debug_abs_all_tol(&self, other: &[B; N], tol: &Self::AllTol) -> Self::AllDebugTol { 241 | let mut result: [MaybeUninit; N] = uninit_array(); 242 | for i in 0..N { 243 | result[i] = MaybeUninit::new(self[i].debug_abs_all_tol(&other[i], tol)); 244 | } 245 | unsafe { array_assume_init(result) } 246 | } 247 | 248 | #[inline] 249 | fn debug_rmax_all_tol(&self, other: &[B; N], tol: &Self::AllTol) -> Self::AllDebugTol { 250 | let mut result: [MaybeUninit; N] = uninit_array(); 251 | for i in 0..N { 252 | result[i] = MaybeUninit::new(self[i].debug_rmax_all_tol(&other[i], tol)); 253 | } 254 | unsafe { array_assume_init(result) } 255 | } 256 | 257 | #[inline] 258 | fn debug_rmin_all_tol(&self, other: &[B; N], tol: &Self::AllTol) -> Self::AllDebugTol { 259 | let mut result: [MaybeUninit; N] = uninit_array(); 260 | for i in 0..N { 261 | result[i] = MaybeUninit::new(self[i].debug_rmin_all_tol(&other[i], tol)); 262 | } 263 | unsafe { array_assume_init(result) } 264 | } 265 | 266 | #[inline] 267 | fn debug_r1st_all_tol(&self, other: &[B; N], tol: &Self::AllTol) -> Self::AllDebugTol { 268 | let mut result: [MaybeUninit; N] = uninit_array(); 269 | for i in 0..N { 270 | result[i] = MaybeUninit::new(self[i].debug_r1st_all_tol(&other[i], tol)); 271 | } 272 | unsafe { array_assume_init(result) } 273 | } 274 | 275 | #[inline] 276 | fn debug_r2nd_all_tol(&self, other: &[B; N], tol: &Self::AllTol) -> Self::AllDebugTol { 277 | let mut result: [MaybeUninit; N] = uninit_array(); 278 | for i in 0..N { 279 | result[i] = MaybeUninit::new(self[i].debug_r2nd_all_tol(&other[i], tol)); 280 | } 281 | unsafe { array_assume_init(result) } 282 | } 283 | 284 | #[inline] 285 | fn debug_ulps_all_tol( 286 | &self, 287 | other: &[B; N], 288 | tol: &UlpsTol, 289 | ) -> UlpsTol { 290 | let mut result: [MaybeUninit>; N] = uninit_array(); 291 | for i in 0..N { 292 | result[i] = MaybeUninit::new(self[i].debug_ulps_all_tol(&other[i], tol)); 293 | } 294 | unsafe { array_assume_init(result) } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /float_eq/src/trait_impls/num_complex.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | AssertFloatEq, AssertFloatEqAll, DebugUlpsDiff, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, 3 | FloatEqUlpsTol, UlpsTol, 4 | }; 5 | use num_complex::Complex; 6 | 7 | /// The absolute difference between two floating point [`num::Complex`] instances 8 | /// in ULPs. 9 | /// 10 | /// The `T` in [`num::Complex`] is constrained by `Clone` and `PartialEq`, so this 11 | /// implements those too. 12 | /// 13 | /// [`num::Complex`]: https://docs.rs/num/0.3.0/num/struct.Complex.html 14 | #[allow(clippy::derive_partial_eq_without_eq)] // Most likely this is going to use floats, and we don't want to derive Eq for those 15 | #[derive(Clone, Debug, PartialEq)] 16 | pub struct ComplexUlps { 17 | /// Real portion of the complex number in ULPs. 18 | pub re: T, 19 | /// Imaginary portion of the complex number in ULPs. 20 | pub im: T, 21 | } 22 | 23 | impl ComplexUlps { 24 | /// Create a new ComplexUlps 25 | pub fn new(re: T, im: T) -> Self { 26 | ComplexUlps { re, im } 27 | } 28 | } 29 | 30 | impl FloatEqUlpsTol for Complex 31 | where 32 | UlpsTol: Sized, 33 | { 34 | type UlpsTol = ComplexUlps>; 35 | } 36 | 37 | impl FloatEqDebugUlpsDiff for Complex { 38 | type DebugUlpsDiff = ComplexUlps>; 39 | } 40 | 41 | /// [`ComplexUlps`] type matching [`num::Complex32`]. 42 | /// 43 | /// [`num::Complex32`]: https://docs.rs/num-complex/0.3/num_complex/type.Complex32.html 44 | pub type ComplexUlps32 = UlpsTol>; 45 | 46 | /// [`ComplexUlps`] type matching [`num::Complex64`]. 47 | /// 48 | /// [`num::Complex64`]: https://docs.rs/num-complex/0.3/num_complex/type.Complex64.html 49 | pub type ComplexUlps64 = UlpsTol>; 50 | 51 | impl FloatEq for Complex 52 | where 53 | T::Tol: Sized, 54 | UlpsTol: Sized, 55 | { 56 | type Tol = Complex; 57 | 58 | #[inline] 59 | fn eq_abs(&self, other: &Self, tol: &Self::Tol) -> bool { 60 | self.re.eq_abs(&other.re, &tol.re) && self.im.eq_abs(&other.im, &tol.im) 61 | } 62 | 63 | #[inline] 64 | fn eq_rmax(&self, other: &Self, tol: &Self::Tol) -> bool { 65 | self.re.eq_rmax(&other.re, &tol.re) && self.im.eq_rmax(&other.im, &tol.im) 66 | } 67 | 68 | #[inline] 69 | fn eq_rmin(&self, other: &Self, tol: &Self::Tol) -> bool { 70 | self.re.eq_rmin(&other.re, &tol.re) && self.im.eq_rmin(&other.im, &tol.im) 71 | } 72 | 73 | #[inline] 74 | fn eq_r1st(&self, other: &Self, tol: &Self::Tol) -> bool { 75 | self.re.eq_r1st(&other.re, &tol.re) && self.im.eq_r1st(&other.im, &tol.im) 76 | } 77 | 78 | #[inline] 79 | fn eq_r2nd(&self, other: &Self, tol: &Self::Tol) -> bool { 80 | self.re.eq_r2nd(&other.re, &tol.re) && self.im.eq_r2nd(&other.im, &tol.im) 81 | } 82 | 83 | #[inline] 84 | fn eq_ulps(&self, other: &Self, tol: &UlpsTol) -> bool { 85 | self.re.eq_ulps(&other.re, &tol.re) && self.im.eq_ulps(&other.im, &tol.im) 86 | } 87 | } 88 | 89 | impl FloatEqAll for Complex { 90 | type AllTol = T::AllTol; 91 | 92 | #[inline] 93 | fn eq_abs_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 94 | self.re.eq_abs_all(&other.re, tol) && self.im.eq_abs_all(&other.im, tol) 95 | } 96 | 97 | #[inline] 98 | fn eq_rmax_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 99 | self.re.eq_rmax_all(&other.re, tol) && self.im.eq_rmax_all(&other.im, tol) 100 | } 101 | 102 | #[inline] 103 | fn eq_rmin_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 104 | self.re.eq_rmin_all(&other.re, tol) && self.im.eq_rmin_all(&other.im, tol) 105 | } 106 | 107 | #[inline] 108 | fn eq_r1st_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 109 | self.re.eq_r1st_all(&other.re, tol) && self.im.eq_r1st_all(&other.im, tol) 110 | } 111 | 112 | #[inline] 113 | fn eq_r2nd_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 114 | self.re.eq_r2nd_all(&other.re, tol) && self.im.eq_r2nd_all(&other.im, tol) 115 | } 116 | 117 | #[inline] 118 | fn eq_ulps_all(&self, other: &Self, tol: &UlpsTol) -> bool { 119 | self.re.eq_ulps_all(&other.re, tol) && self.im.eq_ulps_all(&other.im, tol) 120 | } 121 | } 122 | 123 | impl AssertFloatEq for Complex 124 | where 125 | T: AssertFloatEq, 126 | T::Tol: Sized, 127 | T::DebugTol: Sized, 128 | UlpsTol: Sized, 129 | UlpsTol: Sized, 130 | { 131 | type DebugAbsDiff = Complex; 132 | type DebugTol = Complex; 133 | 134 | #[inline] 135 | fn debug_abs_diff(&self, other: &Self) -> Self::DebugAbsDiff { 136 | Self::DebugAbsDiff { 137 | re: self.re.debug_abs_diff(&other.re), 138 | im: self.im.debug_abs_diff(&other.im), 139 | } 140 | } 141 | 142 | #[inline] 143 | fn debug_ulps_diff(&self, other: &Self) -> DebugUlpsDiff { 144 | DebugUlpsDiff:: { 145 | re: self.re.debug_ulps_diff(&other.re), 146 | im: self.im.debug_ulps_diff(&other.im), 147 | } 148 | } 149 | 150 | #[inline] 151 | fn debug_abs_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 152 | Self::DebugTol { 153 | re: self.re.debug_abs_tol(&other.re, &tol.re), 154 | im: self.im.debug_abs_tol(&other.im, &tol.im), 155 | } 156 | } 157 | 158 | #[inline] 159 | fn debug_rmax_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 160 | Self::DebugTol { 161 | re: self.re.debug_rmax_tol(&other.re, &tol.re), 162 | im: self.im.debug_rmax_tol(&other.im, &tol.im), 163 | } 164 | } 165 | 166 | #[inline] 167 | fn debug_rmin_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 168 | Self::DebugTol { 169 | re: self.re.debug_rmin_tol(&other.re, &tol.re), 170 | im: self.im.debug_rmin_tol(&other.im, &tol.im), 171 | } 172 | } 173 | 174 | #[inline] 175 | fn debug_r1st_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 176 | Self::DebugTol { 177 | re: self.re.debug_r1st_tol(&other.re, &tol.re), 178 | im: self.im.debug_r1st_tol(&other.im, &tol.im), 179 | } 180 | } 181 | 182 | #[inline] 183 | fn debug_r2nd_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 184 | Self::DebugTol { 185 | re: self.re.debug_r2nd_tol(&other.re, &tol.re), 186 | im: self.im.debug_r2nd_tol(&other.im, &tol.im), 187 | } 188 | } 189 | 190 | #[inline] 191 | fn debug_ulps_tol(&self, other: &Self, tol: &UlpsTol) -> UlpsTol 192 | where 193 | UlpsTol: Sized, 194 | { 195 | UlpsTol:: { 196 | re: self.re.debug_ulps_tol(&other.re, &tol.re), 197 | im: self.im.debug_ulps_tol(&other.im, &tol.im), 198 | } 199 | } 200 | } 201 | 202 | impl AssertFloatEqAll for Complex 203 | where 204 | T: AssertFloatEqAll, 205 | T::AllDebugTol: Sized, 206 | UlpsTol: Sized, 207 | { 208 | type AllDebugTol = Complex; 209 | 210 | #[inline] 211 | fn debug_abs_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 212 | Self::AllDebugTol { 213 | re: self.re.debug_abs_all_tol(&other.re, tol), 214 | im: self.im.debug_abs_all_tol(&other.im, tol), 215 | } 216 | } 217 | 218 | #[inline] 219 | fn debug_rmax_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 220 | Self::AllDebugTol { 221 | re: self.re.debug_rmax_all_tol(&other.re, tol), 222 | im: self.im.debug_rmax_all_tol(&other.im, tol), 223 | } 224 | } 225 | 226 | #[inline] 227 | fn debug_rmin_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 228 | Self::AllDebugTol { 229 | re: self.re.debug_rmin_all_tol(&other.re, tol), 230 | im: self.im.debug_rmin_all_tol(&other.im, tol), 231 | } 232 | } 233 | 234 | #[inline] 235 | fn debug_r1st_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 236 | Self::AllDebugTol { 237 | re: self.re.debug_r1st_all_tol(&other.re, tol), 238 | im: self.im.debug_r1st_all_tol(&other.im, tol), 239 | } 240 | } 241 | 242 | #[inline] 243 | fn debug_r2nd_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 244 | Self::AllDebugTol { 245 | re: self.re.debug_r2nd_all_tol(&other.re, tol), 246 | im: self.im.debug_r2nd_all_tol(&other.im, tol), 247 | } 248 | } 249 | 250 | #[inline] 251 | fn debug_ulps_all_tol( 252 | &self, 253 | other: &Self, 254 | tol: &UlpsTol, 255 | ) -> UlpsTol 256 | where 257 | UlpsTol: Sized, 258 | { 259 | UlpsTol:: { 260 | re: self.re.debug_ulps_all_tol(&other.re, tol), 261 | im: self.im.debug_ulps_all_tol(&other.im, tol), 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /float_eq/src/trait_impls/primitives.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | use crate::{ 4 | AssertFloatEq, AssertFloatEqAll, DebugUlpsDiff, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, 5 | FloatEqUlpsTol, UlpsTol, 6 | }; 7 | 8 | macro_rules! impl_traits { 9 | ($float:ident, $uint:ident) => { 10 | mod $float { 11 | #[cfg(feature = "std")] 12 | #[inline] 13 | pub(crate) fn abs(value: $float) -> $float { 14 | // use the intrinsic for std builds 15 | value.abs() 16 | } 17 | 18 | #[cfg(not(feature = "std"))] 19 | #[inline] 20 | pub(crate) fn abs(value: $float) -> $float { 21 | // mask away only the sign bit for no_std builds since the abs 22 | // method is not available 23 | const MASK: $uint = !(1 << ((::core::mem::size_of::<$float>() * 8) - 1)); 24 | $float::from_bits(value.to_bits() & MASK) 25 | } 26 | } 27 | 28 | impl FloatEqUlpsTol for $float { 29 | type UlpsTol = $uint; 30 | } 31 | 32 | impl FloatEqDebugUlpsDiff for $float { 33 | type DebugUlpsDiff = Option<$uint>; 34 | } 35 | 36 | impl FloatEq for $float { 37 | type Tol = Self; 38 | 39 | #[inline] 40 | fn eq_abs(&self, other: &Self, tol: &Self::Tol) -> bool { 41 | // the PartialEq check covers equality of infinities 42 | self == other || $float::abs(self - other).le(tol) 43 | } 44 | 45 | #[inline] 46 | fn eq_rmax(&self, other: &Self, tol: &Self::Tol) -> bool { 47 | // the PartialEq check covers equality of infinities 48 | self == other || { 49 | let largest = $float::abs(*self).max($float::abs(*other)); 50 | let tol = largest * tol; 51 | $float::abs(self - other) <= tol 52 | } 53 | } 54 | 55 | #[inline] 56 | fn eq_rmin(&self, other: &Self, tol: &Self::Tol) -> bool { 57 | // the PartialEq check covers equality of infinities 58 | self == other || { 59 | let largest = $float::abs(*self).min($float::abs(*other)); 60 | let tol = largest * tol; 61 | $float::abs(self - other) <= tol 62 | } 63 | } 64 | 65 | #[inline] 66 | fn eq_r1st(&self, other: &Self, tol: &Self::Tol) -> bool { 67 | // the PartialEq check covers equality of infinities 68 | self == other || { 69 | let tol = $float::abs(*self) * tol; 70 | $float::abs(self - other) <= tol 71 | } 72 | } 73 | 74 | #[inline] 75 | fn eq_r2nd(&self, other: &Self, tol: &Self::Tol) -> bool { 76 | // the PartialEq check covers equality of infinities 77 | self == other || { 78 | let tol = $float::abs(*other) * tol; 79 | $float::abs(self - other) <= tol 80 | } 81 | } 82 | 83 | #[inline] 84 | fn eq_ulps(&self, other: &Self, tol: &UlpsTol) -> bool { 85 | if self.is_nan() || other.is_nan() { 86 | false // NaNs are never equal 87 | } else if self.is_sign_positive() != other.is_sign_positive() { 88 | self == other // account for zero == negative zero 89 | } else { 90 | let a = self.to_bits(); 91 | let b = other.to_bits(); 92 | let max = a.max(b); 93 | let min = a.min(b); 94 | (max - min).le(tol) 95 | } 96 | } 97 | } 98 | 99 | impl FloatEqAll for $float { 100 | type AllTol = $float; 101 | 102 | #[inline] 103 | fn eq_abs_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 104 | self.eq_abs(other, tol) 105 | } 106 | 107 | #[inline] 108 | fn eq_rmax_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 109 | self.eq_rmax(other, tol) 110 | } 111 | 112 | #[inline] 113 | fn eq_rmin_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 114 | self.eq_rmin(other, tol) 115 | } 116 | 117 | #[inline] 118 | fn eq_r1st_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 119 | self.eq_r1st(other, tol) 120 | } 121 | 122 | #[inline] 123 | fn eq_r2nd_all(&self, other: &Self, tol: &Self::AllTol) -> bool { 124 | self.eq_r2nd(other, tol) 125 | } 126 | 127 | #[inline] 128 | fn eq_ulps_all(&self, other: &Self, tol: &UlpsTol) -> bool { 129 | self.eq_ulps(other, tol) 130 | } 131 | } 132 | 133 | impl AssertFloatEq for $float { 134 | type DebugAbsDiff = Self; 135 | type DebugTol = Self::Tol; 136 | 137 | #[inline] 138 | fn debug_abs_diff(&self, other: &Self) -> Self::DebugAbsDiff { 139 | $float::abs(self - other) 140 | } 141 | 142 | #[inline] 143 | fn debug_ulps_diff(&self, other: &Self) -> DebugUlpsDiff { 144 | if self == other { 145 | Some(0) 146 | } else if self.is_nan() || other.is_nan() { 147 | None 148 | } else if self.is_sign_positive() != other.is_sign_positive() { 149 | None 150 | } else { 151 | let a = self.to_bits(); 152 | let b = other.to_bits(); 153 | let max = a.max(b); 154 | let min = a.min(b); 155 | Some(max - min) 156 | } 157 | } 158 | 159 | #[inline] 160 | fn debug_abs_tol(&self, _other: &Self, tol: &Self::Tol) -> Self::DebugTol { 161 | *tol 162 | } 163 | 164 | #[inline] 165 | fn debug_rmax_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 166 | $float::abs(*self).max($float::abs(*other)) * tol 167 | } 168 | 169 | #[inline] 170 | fn debug_rmin_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 171 | $float::abs(*self).min($float::abs(*other)) * tol 172 | } 173 | 174 | #[inline] 175 | fn debug_r1st_tol(&self, _other: &Self, tol: &Self::Tol) -> Self::DebugTol { 176 | $float::abs(*self) * tol 177 | } 178 | 179 | #[inline] 180 | fn debug_r2nd_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 181 | $float::abs(*other) * tol 182 | } 183 | 184 | #[inline] 185 | fn debug_ulps_tol( 186 | &self, 187 | _other: &Self, 188 | tol: &UlpsTol, 189 | ) -> UlpsTol { 190 | *tol 191 | } 192 | } 193 | 194 | impl AssertFloatEqAll for $float { 195 | type AllDebugTol = Self::AllTol; 196 | 197 | #[inline] 198 | fn debug_abs_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 199 | self.debug_abs_tol(other, tol) 200 | } 201 | 202 | #[inline] 203 | fn debug_rmax_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 204 | self.debug_rmax_tol(other, tol) 205 | } 206 | 207 | #[inline] 208 | fn debug_rmin_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 209 | self.debug_rmin_tol(other, tol) 210 | } 211 | 212 | #[inline] 213 | fn debug_r1st_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 214 | self.debug_r1st_tol(other, tol) 215 | } 216 | 217 | #[inline] 218 | fn debug_r2nd_all_tol(&self, other: &Self, tol: &Self::AllTol) -> Self::AllDebugTol { 219 | self.debug_r2nd_tol(other, tol) 220 | } 221 | 222 | #[inline] 223 | fn debug_ulps_all_tol( 224 | &self, 225 | other: &Self, 226 | tol: &UlpsTol, 227 | ) -> UlpsTol { 228 | self.debug_ulps_tol(other, tol) 229 | } 230 | } 231 | }; 232 | } 233 | 234 | impl_traits!(f32, u32); 235 | impl_traits!(f64, u64); 236 | -------------------------------------------------------------------------------- /float_eq/src/trait_impls/tuples.rs: -------------------------------------------------------------------------------- 1 | use crate::{AssertFloatEq, DebugUlpsDiff, FloatEq, FloatEqDebugUlpsDiff, FloatEqUlpsTol, UlpsTol}; 2 | use core::fmt; 3 | 4 | impl FloatEqUlpsTol for () { 5 | type UlpsTol = (); 6 | } 7 | 8 | impl FloatEqDebugUlpsDiff for () { 9 | type DebugUlpsDiff = (); 10 | } 11 | 12 | impl FloatEq for () { 13 | type Tol = (); 14 | 15 | #[inline] 16 | fn eq_abs(&self, _other: &(), _tol: &Self::Tol) -> bool { 17 | true 18 | } 19 | 20 | #[inline] 21 | fn eq_rmax(&self, _other: &(), _tol: &Self::Tol) -> bool { 22 | true 23 | } 24 | 25 | #[inline] 26 | fn eq_rmin(&self, _other: &(), _tol: &Self::Tol) -> bool { 27 | true 28 | } 29 | 30 | #[inline] 31 | fn eq_r1st(&self, _other: &(), _tol: &Self::Tol) -> bool { 32 | true 33 | } 34 | 35 | #[inline] 36 | fn eq_r2nd(&self, _other: &(), _tol: &Self::Tol) -> bool { 37 | true 38 | } 39 | 40 | #[inline] 41 | fn eq_ulps(&self, _other: &(), _tol: &UlpsTol) -> bool { 42 | true 43 | } 44 | } 45 | 46 | impl AssertFloatEq for () { 47 | type DebugAbsDiff = (); 48 | type DebugTol = (); 49 | 50 | #[inline] 51 | fn debug_abs_diff(&self, _other: &()) -> Self::DebugAbsDiff {} 52 | 53 | #[inline] 54 | fn debug_ulps_diff(&self, _other: &()) -> DebugUlpsDiff {} 55 | 56 | #[inline] 57 | fn debug_abs_tol(&self, _other: &(), _tol: &Self::Tol) -> Self::DebugTol {} 58 | 59 | #[inline] 60 | fn debug_rmax_tol(&self, _other: &(), _tol: &Self::Tol) -> Self::DebugTol {} 61 | 62 | #[inline] 63 | fn debug_rmin_tol(&self, _other: &(), _tol: &Self::Tol) -> Self::DebugTol {} 64 | 65 | #[inline] 66 | fn debug_r1st_tol(&self, _other: &(), _tol: &Self::Tol) -> Self::DebugTol {} 67 | 68 | #[inline] 69 | fn debug_r2nd_tol(&self, _other: &(), _tol: &Self::Tol) -> Self::DebugTol {} 70 | 71 | #[inline] 72 | fn debug_ulps_tol(&self, _other: &(), _tol: &UlpsTol) -> UlpsTol {} 73 | } 74 | 75 | // Non-unit type tuple impls, as for std PartialEq implementation 76 | macro_rules! tuple_impls { 77 | ($( 78 | $Tuple:ident { 79 | $(($idx:tt) -> $T:ident)+ 80 | } 81 | )+) => { 82 | $( 83 | impl<$($T:FloatEqUlpsTol),+> FloatEqUlpsTol for ($($T,)+) 84 | where 85 | last_type!($($T,)+): ?Sized, 86 | $(UlpsTol<$T>: Sized,)+ 87 | { 88 | type UlpsTol = ($(UlpsTol<$T>,)+); 89 | } 90 | 91 | impl<$($T:FloatEqDebugUlpsDiff),+> FloatEqDebugUlpsDiff for ($($T,)+) 92 | { 93 | type DebugUlpsDiff = ($(DebugUlpsDiff<$T>,)+); 94 | } 95 | 96 | impl<$($T:FloatEq),+> FloatEq for ($($T,)+) 97 | where 98 | last_type!($($T,)+): ?Sized, 99 | $($T::Tol: Sized,)+ 100 | $(UlpsTol<$T::Tol>: Sized,)+ 101 | { 102 | type Tol = ($($T::Tol,)+); 103 | 104 | #[inline] 105 | fn eq_abs(&self, other: &Self, tol: &Self::Tol) -> bool { 106 | $(self.$idx.eq_abs(&other.$idx, &tol.$idx))&&+ 107 | } 108 | 109 | #[inline] 110 | fn eq_rmax(&self, other: &Self, tol: &Self::Tol) -> bool { 111 | $(self.$idx.eq_rmax(&other.$idx, &tol.$idx))&&+ 112 | } 113 | 114 | #[inline] 115 | fn eq_rmin(&self, other: &Self, tol: &Self::Tol) -> bool { 116 | $(self.$idx.eq_rmin(&other.$idx, &tol.$idx))&&+ 117 | } 118 | 119 | #[inline] 120 | fn eq_r1st(&self, other: &Self, tol: &Self::Tol) -> bool { 121 | $(self.$idx.eq_r1st(&other.$idx, &tol.$idx))&&+ 122 | } 123 | 124 | #[inline] 125 | fn eq_r2nd(&self, other: &Self, tol: &Self::Tol) -> bool { 126 | $(self.$idx.eq_r2nd(&other.$idx, &tol.$idx))&&+ 127 | } 128 | 129 | #[inline] 130 | fn eq_ulps(&self, other: &Self, tol: &UlpsTol) -> bool { 131 | $(self.$idx.eq_ulps(&other.$idx, &tol.$idx))&&+ 132 | } 133 | } 134 | 135 | impl<$($T:AssertFloatEq + fmt::Debug),+> AssertFloatEq for ($($T,)+) 136 | where 137 | last_type!($($T,)+): ?Sized, 138 | $($T::Tol: Sized,)+ 139 | $($T::DebugTol: Sized,)+ 140 | $(UlpsTol<$T::Tol>: Sized,)+ 141 | $(UlpsTol<$T::DebugTol>: Sized,)+ 142 | { 143 | type DebugAbsDiff = ($($T::DebugAbsDiff,)+); 144 | type DebugTol = ($($T::DebugTol,)+); 145 | 146 | #[inline] 147 | fn debug_abs_diff(&self, other: &Self) -> Self::DebugAbsDiff { 148 | ($(self.$idx.debug_abs_diff(&other.$idx),)+) 149 | } 150 | 151 | #[inline] 152 | fn debug_ulps_diff(&self, other: &Self) -> DebugUlpsDiff { 153 | ($(self.$idx.debug_ulps_diff(&other.$idx),)+) 154 | } 155 | 156 | #[inline] 157 | fn debug_abs_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 158 | ($(self.$idx.debug_abs_tol(&other.$idx, &tol.$idx),)+) 159 | } 160 | 161 | #[inline] 162 | fn debug_rmax_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 163 | ($(self.$idx.debug_rmax_tol(&other.$idx, &tol.$idx),)+) 164 | } 165 | 166 | #[inline] 167 | fn debug_rmin_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 168 | ($(self.$idx.debug_rmin_tol(&other.$idx, &tol.$idx),)+) 169 | } 170 | 171 | #[inline] 172 | fn debug_r1st_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 173 | ($(self.$idx.debug_r1st_tol(&other.$idx, &tol.$idx),)+) 174 | } 175 | 176 | #[inline] 177 | fn debug_r2nd_tol(&self, other: &Self, tol: &Self::Tol) -> Self::DebugTol { 178 | ($(self.$idx.debug_r2nd_tol(&other.$idx, &tol.$idx),)+) 179 | } 180 | 181 | #[inline] 182 | fn debug_ulps_tol(&self, other: &Self, tol: &UlpsTol) -> UlpsTol { 183 | ($(self.$idx.debug_ulps_tol(&other.$idx, &tol.$idx),)+) 184 | } 185 | } 186 | )+ 187 | }; 188 | } 189 | 190 | macro_rules! last_type { 191 | ($a:ident,) => { $a }; 192 | ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; 193 | } 194 | 195 | tuple_impls! { 196 | Tuple1 { 197 | (0) -> A 198 | } 199 | Tuple2 { 200 | (0) -> A 201 | (1) -> B 202 | } 203 | Tuple3 { 204 | (0) -> A 205 | (1) -> B 206 | (2) -> C 207 | } 208 | Tuple4 { 209 | (0) -> A 210 | (1) -> B 211 | (2) -> C 212 | (3) -> D 213 | } 214 | Tuple5 { 215 | (0) -> A 216 | (1) -> B 217 | (2) -> C 218 | (3) -> D 219 | (4) -> E 220 | } 221 | Tuple6 { 222 | (0) -> A 223 | (1) -> B 224 | (2) -> C 225 | (3) -> D 226 | (4) -> E 227 | (5) -> F 228 | } 229 | Tuple7 { 230 | (0) -> A 231 | (1) -> B 232 | (2) -> C 233 | (3) -> D 234 | (4) -> E 235 | (5) -> F 236 | (6) -> G 237 | } 238 | Tuple8 { 239 | (0) -> A 240 | (1) -> B 241 | (2) -> C 242 | (3) -> D 243 | (4) -> E 244 | (5) -> F 245 | (6) -> G 246 | (7) -> H 247 | } 248 | Tuple9 { 249 | (0) -> A 250 | (1) -> B 251 | (2) -> C 252 | (3) -> D 253 | (4) -> E 254 | (5) -> F 255 | (6) -> G 256 | (7) -> H 257 | (8) -> I 258 | } 259 | Tuple10 { 260 | (0) -> A 261 | (1) -> B 262 | (2) -> C 263 | (3) -> D 264 | (4) -> E 265 | (5) -> F 266 | (6) -> G 267 | (7) -> H 268 | (8) -> I 269 | (9) -> J 270 | } 271 | Tuple11 { 272 | (0) -> A 273 | (1) -> B 274 | (2) -> C 275 | (3) -> D 276 | (4) -> E 277 | (5) -> F 278 | (6) -> G 279 | (7) -> H 280 | (8) -> I 281 | (9) -> J 282 | (10) -> K 283 | } 284 | Tuple12 { 285 | (0) -> A 286 | (1) -> B 287 | (2) -> C 288 | (3) -> D 289 | (4) -> E 290 | (5) -> F 291 | (6) -> G 292 | (7) -> H 293 | (8) -> I 294 | (9) -> J 295 | (10) -> K 296 | (11) -> L 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /float_eq/tests/derive_float_eq_error_msg.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use float_eq::{assert_float_eq, derive_float_eq}; 4 | 5 | #[derive_float_eq( 6 | ulps_tol = "MyComplex32Ulps", 7 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 8 | debug_ulps_diff = "MyComplex32UlpsDiff", 9 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 10 | )] 11 | #[derive(Debug, Clone, Copy, PartialEq)] 12 | struct MyComplex32 { 13 | re: f32, 14 | im: f32, 15 | } 16 | 17 | #[test] 18 | #[should_panic(expected = "`float_eq!(left, right, ulps <= t)` 19 | left: `MyComplex32 { re: 1.0, im: 2.0 }`, 20 | right: `MyComplex32 { re: 1.0000005, im: -5.0 }`, 21 | abs_diff: `MyComplex32 { re: 4.7683716e-7, im: 7.0 }`, 22 | ulps_diff: `MyComplex32UlpsDiff { re: Some(4), im: None }`, 23 | [ulps] t: `MyComplex32Ulps { re: 5, im: 2 }`")] 24 | fn failed_assert() { 25 | assert_float_eq!( 26 | MyComplex32 { re: 1.0, im: 2.0 }, 27 | MyComplex32 { 28 | re: 1.000_000_5, 29 | im: -5.0 30 | }, 31 | ulps <= MyComplex32Ulps { re: 5, im: 2 }, 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | #[test] 4 | fn tests() { 5 | let t = trybuild::TestCases::new(); 6 | 7 | // FloatEqUlpsTol 8 | t.pass("tests/derive_tests/ulps_tol/ulps_tol_struct.rs"); 9 | t.pass("tests/derive_tests/ulps_tol/ulps_tol_struct_custom_debug.rs"); 10 | t.pass("tests/derive_tests/ulps_tol/ulps_tol_struct_no_fields.rs"); 11 | t.pass("tests/derive_tests/ulps_tol/ulps_tol_tuple_struct.rs"); 12 | t.pass("tests/derive_tests/ulps_tol/ulps_tol_unit.rs"); 13 | t.compile_fail("tests/derive_tests/ulps_tol/ulps_tol_enum.rs"); 14 | t.compile_fail("tests/derive_tests/ulps_tol/ulps_tol_generic.rs"); 15 | t.compile_fail("tests/derive_tests/ulps_tol/ulps_tol_missing_type_name.rs"); 16 | t.compile_fail("tests/derive_tests/ulps_tol/ulps_tol_duplicate_type_name.rs"); 17 | 18 | // FloatEqDebugUlpsDiff 19 | t.pass("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_struct.rs"); 20 | t.pass("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_struct_custom_debug.rs"); 21 | t.pass("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_struct_no_fields.rs"); 22 | t.pass("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_tuple_struct.rs"); 23 | t.pass("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_unit.rs"); 24 | t.compile_fail("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_enum.rs"); 25 | t.compile_fail("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_generic.rs"); 26 | t.compile_fail("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_missing_type_name.rs"); 27 | t.compile_fail("tests/derive_tests/debug_ulps_diff/debug_ulps_diff_duplicate_type_name.rs"); 28 | 29 | // FloatEq 30 | t.pass("tests/derive_tests/float_eq/float_eq_struct.rs"); 31 | t.pass("tests/derive_tests/float_eq/float_eq_struct_no_fields.rs"); 32 | t.pass("tests/derive_tests/float_eq/float_eq_tuple_struct.rs"); 33 | t.pass("tests/derive_tests/float_eq/float_eq_unit.rs"); 34 | t.compile_fail("tests/derive_tests/float_eq/float_eq_enum.rs"); 35 | t.compile_fail("tests/derive_tests/float_eq/float_eq_generic.rs"); 36 | 37 | // FloatEqAll 38 | t.pass("tests/derive_tests/float_eq_all/float_eq_all_struct.rs"); 39 | t.pass("tests/derive_tests/float_eq_all/float_eq_all_struct_no_fields.rs"); 40 | t.pass("tests/derive_tests/float_eq_all/float_eq_all_tuple_struct.rs"); 41 | t.pass("tests/derive_tests/float_eq_all/float_eq_all_unit.rs"); 42 | t.compile_fail("tests/derive_tests/float_eq_all/float_eq_all_enum.rs"); 43 | t.compile_fail("tests/derive_tests/float_eq_all/float_eq_all_generic.rs"); 44 | t.compile_fail("tests/derive_tests/float_eq_all/float_eq_all_missing_tol.rs"); 45 | t.compile_fail("tests/derive_tests/float_eq_all/float_eq_all_duplicate_tol.rs"); 46 | 47 | // AssertFloatEq 48 | t.pass("tests/derive_tests/assert_float_eq/assert_float_eq_struct.rs"); 49 | t.pass("tests/derive_tests/assert_float_eq/assert_float_eq_struct_no_fields.rs"); 50 | t.pass("tests/derive_tests/assert_float_eq/assert_float_eq_tuple_struct.rs"); 51 | t.pass("tests/derive_tests/assert_float_eq/assert_float_eq_unit.rs"); 52 | t.compile_fail("tests/derive_tests/assert_float_eq/assert_float_eq_enum.rs"); 53 | t.compile_fail("tests/derive_tests/assert_float_eq/assert_float_eq_generic.rs"); 54 | 55 | // AssertFloatEqAll 56 | t.pass("tests/derive_tests/assert_float_eq_all/assert_float_eq_all_struct.rs"); 57 | t.pass("tests/derive_tests/assert_float_eq_all/assert_float_eq_all_struct_no_fields.rs"); 58 | t.pass("tests/derive_tests/assert_float_eq_all/assert_float_eq_all_tuple_struct.rs"); 59 | t.pass("tests/derive_tests/assert_float_eq_all/assert_float_eq_all_unit.rs"); 60 | t.compile_fail("tests/derive_tests/assert_float_eq_all/assert_float_eq_all_enum.rs"); 61 | t.compile_fail("tests/derive_tests/assert_float_eq_all/assert_float_eq_all_generic.rs"); 62 | 63 | // #[float_eq(...)] 64 | t.compile_fail("tests/derive_tests/float_eq_attribute/float_eq_no_params_list.rs"); 65 | t.compile_fail("tests/derive_tests/float_eq_attribute/float_eq_malformed_param.rs"); 66 | t.compile_fail("tests/derive_tests/float_eq_attribute/float_eq_malformed_value.rs"); 67 | t.compile_fail("tests/derive_tests/float_eq_attribute/float_eq_unknown_param.rs"); 68 | 69 | // #[derive_float_eq(...)] 70 | t.pass("tests/derive_tests/derive_float_eq/derive_float_eq.rs"); 71 | t.pass("tests/derive_tests/derive_float_eq/derive_float_eq_tuple_struct.rs"); 72 | t.pass("tests/derive_tests/derive_float_eq/derive_float_eq_all.rs"); 73 | t.pass("tests/derive_tests/derive_float_eq/derive_float_eq_all_custom_debug.rs"); 74 | t.compile_fail("tests/derive_tests/derive_float_eq/derive_float_eq_missing_ulps_tol.rs"); 75 | t.compile_fail("tests/derive_tests/derive_float_eq/derive_float_eq_missing_debug_ulps_diff.rs"); 76 | } 77 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq/assert_float_eq_enum.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{AssertFloatEq, FloatEq, FloatEqDebugUlpsDiff, FloatEqUlpsTol}; 2 | 3 | #[derive( 4 | Clone, 5 | Copy, 6 | Debug, 7 | PartialEq, 8 | PartialOrd, 9 | FloatEqUlpsTol, 10 | FloatEq, 11 | FloatEqDebugUlpsDiff, 12 | AssertFloatEq, 13 | )] 14 | enum SomeEnum { 15 | Float(f32), 16 | Double(f64), 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq/assert_float_eq_enum.stderr: -------------------------------------------------------------------------------- 1 | error: FloatEqUlpsTol may only be derived for structs. 2 | --> $DIR/assert_float_eq_enum.rs:14:6 3 | | 4 | 14 | enum SomeEnum { 5 | | ^^^^^^^^ 6 | 7 | error: FloatEq may only be derived for structs. 8 | --> $DIR/assert_float_eq_enum.rs:14:6 9 | | 10 | 14 | enum SomeEnum { 11 | | ^^^^^^^^ 12 | 13 | error: FloatEqDebugUlpsDiff may only be derived for structs. 14 | --> $DIR/assert_float_eq_enum.rs:14:6 15 | | 16 | 14 | enum SomeEnum { 17 | | ^^^^^^^^ 18 | 19 | error: AssertFloatEq may only be derived for structs. 20 | --> $DIR/assert_float_eq_enum.rs:14:6 21 | | 22 | 14 | enum SomeEnum { 23 | | ^^^^^^^^ 24 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq/assert_float_eq_generic.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{AssertFloatEq, FloatEq, FloatEqDebugUlpsDiff, FloatEqUlpsTol}; 2 | 3 | #[derive( 4 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 5 | )] 6 | struct MyComplexUlps { 7 | re: T, 8 | im: T, 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq/assert_float_eq_generic.stderr: -------------------------------------------------------------------------------- 1 | error: This trait does not yet support derive for generic types. 2 | --> $DIR/assert_float_eq_generic.rs:4:36 3 | | 4 | 4 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 5 | | ^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FloatEqUlpsTol` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error: This trait does not yet support derive for generic types. 10 | --> $DIR/assert_float_eq_generic.rs:4:52 11 | | 12 | 4 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 13 | | ^^^^^^^ 14 | | 15 | = note: this error originates in the derive macro `FloatEq` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error: This trait does not yet support derive for generic types. 18 | --> $DIR/assert_float_eq_generic.rs:4:61 19 | | 20 | 4 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 21 | | ^^^^^^^^^^^^^^^^^^^^ 22 | | 23 | = note: this error originates in the derive macro `FloatEqDebugUlpsDiff` (in Nightly builds, run with -Z macro-backtrace for more info) 24 | 25 | error: This trait does not yet support derive for generic types. 26 | --> $DIR/assert_float_eq_generic.rs:4:83 27 | | 28 | 4 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 29 | | ^^^^^^^^^^^^^ 30 | | 31 | = note: this error originates in the derive macro `AssertFloatEq` (in Nightly builds, run with -Z macro-backtrace for more info) 32 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq/assert_float_eq_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{ 2 | AssertFloatEq, DebugUlpsDiff, FloatEq, FloatEqDebugUlpsDiff, FloatEqUlpsTol, UlpsTol, 3 | }; 4 | 5 | #[derive( 6 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 7 | )] 8 | #[float_eq( 9 | ulps_tol = "MyComplex32Ulps", 10 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 11 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 12 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 13 | )] 14 | struct MyComplex32 { 15 | re: f32, 16 | im: f32, 17 | } 18 | 19 | impl MyComplex32 { 20 | fn new(re: f32, im: f32) -> MyComplex32 { 21 | MyComplex32 { re, im } 22 | } 23 | } 24 | 25 | impl MyComplex32Ulps { 26 | fn new(re: UlpsTol, im: UlpsTol) -> MyComplex32Ulps { 27 | MyComplex32Ulps { re, im } 28 | } 29 | } 30 | 31 | fn debug_diff() { 32 | let a = MyComplex32::new(1.0, 2.000_003_6); 33 | assert_eq!(a.debug_abs_diff(&a), MyComplex32::new(0.0, 0.0)); 34 | assert_eq!( 35 | a.debug_ulps_diff(&a), 36 | DebugUlpsDiff:: { 37 | re: Some(0), 38 | im: Some(0) 39 | } 40 | ); 41 | 42 | let b = MyComplex32::new(1.000_000_1, 2.0); 43 | assert_eq!( 44 | a.debug_abs_diff(&b), 45 | MyComplex32::new(0.000_000_119_209_29, 0.000_003_576_278_7) 46 | ); 47 | assert_eq!( 48 | a.debug_ulps_diff(&b), 49 | DebugUlpsDiff:: { 50 | re: Some(1), 51 | im: Some(15) 52 | } 53 | ); 54 | 55 | let c = MyComplex32::new(1.000_000_2, -2.0); 56 | assert_eq!( 57 | a.debug_ulps_diff(&c), 58 | DebugUlpsDiff:: { 59 | re: Some(2), 60 | im: None 61 | } 62 | ); 63 | } 64 | 65 | fn debug_tol() { 66 | let a = MyComplex32::new(1.0, 200.0); 67 | let b = MyComplex32::new(50.0, 1.0); 68 | 69 | assert_eq!( 70 | a.debug_abs_tol(&b, &MyComplex32::new(0.1, 0.2)), 71 | MyComplex32::new(0.1, 0.2) 72 | ); 73 | assert_eq!( 74 | a.debug_rel_tol(&b, &MyComplex32::new(0.1, 0.2)), 75 | MyComplex32::new(5.0, 40.0) 76 | ); 77 | assert_eq!( 78 | a.debug_ulps_tol(&b, &UlpsTol:: { re: 1, im: 2 }), 79 | UlpsTol:: { re: 1, im: 2 } 80 | ); 81 | } 82 | 83 | fn main() { 84 | debug_diff(); 85 | debug_tol(); 86 | } 87 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq/assert_float_eq_struct_no_fields.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{AssertFloatEq, FloatEq, FloatEqDebugUlpsDiff, FloatEqUlpsTol}; 2 | 3 | #[derive( 4 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 5 | )] 6 | #[float_eq( 7 | ulps_tol = "MyNoFieldsTypeUlps", 8 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 9 | debug_ulps_diff = "MyNoFieldsTypeDebugUlpsDiff", 10 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 11 | )] 12 | struct MyNoFieldsType; 13 | 14 | fn debug_diff() { 15 | let a = MyNoFieldsType {}; 16 | assert_eq!(a.debug_abs_diff(&a), MyNoFieldsType {}); 17 | assert_eq!(a.debug_ulps_diff(&a), MyNoFieldsTypeDebugUlpsDiff {}); 18 | 19 | let b = MyNoFieldsType {}; 20 | assert_eq!(a.debug_abs_diff(&b), MyNoFieldsType {}); 21 | assert_eq!(a.debug_ulps_diff(&b), MyNoFieldsTypeDebugUlpsDiff {}); 22 | } 23 | 24 | fn debug_tol() { 25 | let a = MyNoFieldsType {}; 26 | let b = MyNoFieldsType {}; 27 | 28 | assert_eq!(a.debug_abs_tol(&b, &MyNoFieldsType {}), MyNoFieldsType {}); 29 | assert_eq!(a.debug_rel_tol(&b, &MyNoFieldsType {}), MyNoFieldsType {}); 30 | assert_eq!( 31 | a.debug_ulps_tol(&b, &MyNoFieldsTypeUlps {}), 32 | MyNoFieldsTypeUlps {} 33 | ); 34 | } 35 | 36 | fn main() { 37 | debug_diff(); 38 | debug_tol(); 39 | } 40 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq/assert_float_eq_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{AssertFloatEq, FloatEq, FloatEqDebugUlpsDiff, FloatEqUlpsTol}; 2 | 3 | #[derive( 4 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 5 | )] 6 | #[float_eq( 7 | ulps_tol = "MyTupleTypeUlps", 8 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 9 | debug_ulps_diff = "MyTupleTypeDebugUlpsDiff", 10 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 11 | )] 12 | struct MyTupleType(f32, f64); 13 | 14 | fn debug_diff() { 15 | let a = MyTupleType(1.0, 2.000_000_000_000_006_7); 16 | assert_eq!(a.debug_abs_diff(&a), MyTupleType(0.0, 0.0)); 17 | assert_eq!( 18 | a.debug_ulps_diff(&a), 19 | MyTupleTypeDebugUlpsDiff(Some(0), Some(0)) 20 | ); 21 | 22 | let b = MyTupleType(1.000_000_1, 2.0); 23 | assert_eq!( 24 | a.debug_abs_diff(&b), 25 | MyTupleType( 26 | 0.000_000_119_209_29, 27 | 0.000_000_000_000_006_661_338_147_750_939 28 | ) 29 | ); 30 | assert_eq!( 31 | a.debug_ulps_diff(&b), 32 | MyTupleTypeDebugUlpsDiff(Some(1), Some(15)) 33 | ); 34 | 35 | let c = MyTupleType(1.000_000_1, -2.0); 36 | assert_eq!( 37 | a.debug_ulps_diff(&c), 38 | MyTupleTypeDebugUlpsDiff(Some(1), None) 39 | ); 40 | } 41 | 42 | fn debug_tol() { 43 | let a = MyTupleType(1.0, 200.0); 44 | let b = MyTupleType(50.0, 1.0); 45 | 46 | assert_eq!( 47 | a.debug_abs_tol(&b, &MyTupleType(0.1, 0.2)), 48 | MyTupleType(0.1, 0.2) 49 | ); 50 | assert_eq!( 51 | a.debug_rel_tol(&b, &MyTupleType(0.1, 0.2)), 52 | MyTupleType(5.0, 40.0) 53 | ); 54 | assert_eq!( 55 | a.debug_ulps_tol(&b, &MyTupleTypeUlps(1, 2)), 56 | MyTupleTypeUlps(1, 2) 57 | ); 58 | } 59 | 60 | fn main() { 61 | debug_diff(); 62 | debug_tol(); 63 | } 64 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq/assert_float_eq_unit.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{AssertFloatEq, FloatEq, FloatEqDebugUlpsDiff, FloatEqUlpsTol}; 2 | 3 | #[derive( 4 | Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqDebugUlpsDiff, AssertFloatEq, 5 | )] 6 | #[float_eq( 7 | ulps_tol = "MyUnitTypeUlps", 8 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 9 | debug_ulps_diff = "MyUnitTypeDebugUlpsDiff", 10 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 11 | )] 12 | struct MyUnitType(); 13 | 14 | fn debug_diff() { 15 | let a = MyUnitType {}; 16 | assert_eq!(a.debug_abs_diff(&a), MyUnitType {}); 17 | assert_eq!(a.debug_ulps_diff(&a), MyUnitTypeDebugUlpsDiff {}); 18 | 19 | let b = MyUnitType {}; 20 | assert_eq!(a.debug_abs_diff(&b), MyUnitType {}); 21 | assert_eq!(a.debug_ulps_diff(&b), MyUnitTypeDebugUlpsDiff {}); 22 | } 23 | 24 | fn debug_tol() { 25 | let a = MyUnitType {}; 26 | let b = MyUnitType {}; 27 | 28 | assert_eq!(a.debug_abs_tol(&b, &MyUnitType {}), MyUnitType {}); 29 | assert_eq!(a.debug_rel_tol(&b, &MyUnitType {}), MyUnitType {}); 30 | assert_eq!(a.debug_ulps_tol(&b, &MyUnitTypeUlps {}), MyUnitTypeUlps {}); 31 | } 32 | 33 | fn main() { 34 | debug_diff(); 35 | debug_tol(); 36 | } 37 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq_all/assert_float_eq_all_enum.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{ 2 | AssertFloatEq, AssertFloatEqAll, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, FloatEqUlpsTol, 3 | }; 4 | 5 | #[derive( 6 | Clone, 7 | Copy, 8 | Debug, 9 | PartialEq, 10 | PartialOrd, 11 | FloatEqUlpsTol, 12 | FloatEq, 13 | FloatEqDebugUlpsDiff, 14 | AssertFloatEq, 15 | FloatEqAll, 16 | AssertFloatEqAll, 17 | )] 18 | enum SomeEnum { 19 | Float(f32), 20 | Double(f64), 21 | } 22 | 23 | fn main() {} 24 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq_all/assert_float_eq_all_enum.stderr: -------------------------------------------------------------------------------- 1 | error: FloatEqUlpsTol may only be derived for structs. 2 | --> $DIR/assert_float_eq_all_enum.rs:18:6 3 | | 4 | 18 | enum SomeEnum { 5 | | ^^^^^^^^ 6 | 7 | error: FloatEq may only be derived for structs. 8 | --> $DIR/assert_float_eq_all_enum.rs:18:6 9 | | 10 | 18 | enum SomeEnum { 11 | | ^^^^^^^^ 12 | 13 | error: FloatEqDebugUlpsDiff may only be derived for structs. 14 | --> $DIR/assert_float_eq_all_enum.rs:18:6 15 | | 16 | 18 | enum SomeEnum { 17 | | ^^^^^^^^ 18 | 19 | error: AssertFloatEq may only be derived for structs. 20 | --> $DIR/assert_float_eq_all_enum.rs:18:6 21 | | 22 | 18 | enum SomeEnum { 23 | | ^^^^^^^^ 24 | 25 | error: FloatEqAll may only be derived for structs. 26 | --> $DIR/assert_float_eq_all_enum.rs:18:6 27 | | 28 | 18 | enum SomeEnum { 29 | | ^^^^^^^^ 30 | 31 | error: AssertFloatEqAll may only be derived for structs. 32 | --> $DIR/assert_float_eq_all_enum.rs:18:6 33 | | 34 | 18 | enum SomeEnum { 35 | | ^^^^^^^^ 36 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq_all/assert_float_eq_all_generic.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{ 2 | AssertFloatEq, AssertFloatEqAll, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, FloatEqUlpsTol, 3 | }; 4 | 5 | #[derive( 6 | Debug, 7 | Clone, 8 | Copy, 9 | PartialEq, 10 | FloatEqUlpsTol, 11 | FloatEq, 12 | FloatEqDebugUlpsDiff, 13 | AssertFloatEq, 14 | FloatEqAll, 15 | AssertFloatEqAll, 16 | )] 17 | struct MyComplexUlps { 18 | re: T, 19 | im: T, 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq_all/assert_float_eq_all_generic.stderr: -------------------------------------------------------------------------------- 1 | error: This trait does not yet support derive for generic types. 2 | --> $DIR/assert_float_eq_all_generic.rs:10:5 3 | | 4 | 10 | FloatEqUlpsTol, 5 | | ^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FloatEqUlpsTol` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error: This trait does not yet support derive for generic types. 10 | --> $DIR/assert_float_eq_all_generic.rs:11:5 11 | | 12 | 11 | FloatEq, 13 | | ^^^^^^^ 14 | | 15 | = note: this error originates in the derive macro `FloatEq` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error: This trait does not yet support derive for generic types. 18 | --> $DIR/assert_float_eq_all_generic.rs:12:5 19 | | 20 | 12 | FloatEqDebugUlpsDiff, 21 | | ^^^^^^^^^^^^^^^^^^^^ 22 | | 23 | = note: this error originates in the derive macro `FloatEqDebugUlpsDiff` (in Nightly builds, run with -Z macro-backtrace for more info) 24 | 25 | error: This trait does not yet support derive for generic types. 26 | --> $DIR/assert_float_eq_all_generic.rs:13:5 27 | | 28 | 13 | AssertFloatEq, 29 | | ^^^^^^^^^^^^^ 30 | | 31 | = note: this error originates in the derive macro `AssertFloatEq` (in Nightly builds, run with -Z macro-backtrace for more info) 32 | 33 | error: This trait does not yet support derive for generic types. 34 | --> $DIR/assert_float_eq_all_generic.rs:14:5 35 | | 36 | 14 | FloatEqAll, 37 | | ^^^^^^^^^^ 38 | | 39 | = note: this error originates in the derive macro `FloatEqAll` (in Nightly builds, run with -Z macro-backtrace for more info) 40 | 41 | error: This trait does not yet support derive for generic types. 42 | --> $DIR/assert_float_eq_all_generic.rs:15:5 43 | | 44 | 15 | AssertFloatEqAll, 45 | | ^^^^^^^^^^^^^^^^ 46 | | 47 | = note: this error originates in the derive macro `AssertFloatEqAll` (in Nightly builds, run with -Z macro-backtrace for more info) 48 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq_all/assert_float_eq_all_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{ 2 | AssertFloatEq, AssertFloatEqAll, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, FloatEqUlpsTol, 3 | UlpsTol, 4 | }; 5 | 6 | #[derive( 7 | Debug, 8 | Clone, 9 | Copy, 10 | PartialEq, 11 | FloatEqUlpsTol, 12 | FloatEq, 13 | FloatEqDebugUlpsDiff, 14 | AssertFloatEq, 15 | FloatEqAll, 16 | AssertFloatEqAll, 17 | )] 18 | #[float_eq( 19 | ulps_tol = "MyComplex32Ulps", 20 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 21 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 22 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq", 23 | all_tol = "f32" 24 | )] 25 | struct MyComplex32 { 26 | re: f32, 27 | im: f32, 28 | } 29 | 30 | impl MyComplex32 { 31 | fn new(re: f32, im: f32) -> MyComplex32 { 32 | MyComplex32 { re, im } 33 | } 34 | } 35 | 36 | impl MyComplex32Ulps { 37 | fn new(re: UlpsTol, im: UlpsTol) -> MyComplex32Ulps { 38 | MyComplex32Ulps { re, im } 39 | } 40 | } 41 | 42 | fn main() { 43 | let a = MyComplex32 { re: 1.0, im: 200.0 }; 44 | let b = MyComplex32 { re: 50.0, im: 1.0 }; 45 | 46 | assert_eq!(a.debug_abs_all_tol(&b, &0.2), MyComplex32::new(0.2, 0.2)); 47 | assert_eq!(a.debug_rel_all_tol(&b, &0.2), MyComplex32::new(10.0, 40.0)); 48 | assert_eq!(a.debug_ulps_all_tol(&b, &2), MyComplex32Ulps::new(2, 2)); 49 | } 50 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq_all/assert_float_eq_all_struct_no_fields.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{ 2 | AssertFloatEq, AssertFloatEqAll, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, FloatEqUlpsTol, 3 | }; 4 | 5 | #[derive( 6 | Debug, 7 | Clone, 8 | Copy, 9 | PartialEq, 10 | FloatEqUlpsTol, 11 | FloatEq, 12 | FloatEqDebugUlpsDiff, 13 | AssertFloatEq, 14 | FloatEqAll, 15 | AssertFloatEqAll, 16 | )] 17 | #[float_eq( 18 | ulps_tol = "MyNoFieldsTypeUlps", 19 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 20 | debug_ulps_diff = "MyNoFieldsTypeDebugUlpsDiff", 21 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq", 22 | all_tol = "f32" 23 | )] 24 | struct MyNoFieldsType; 25 | 26 | fn main() { 27 | let a = MyNoFieldsType {}; 28 | let b = MyNoFieldsType {}; 29 | 30 | assert_eq!(a.debug_abs_all_tol(&b, &0.0), MyNoFieldsType {}); 31 | assert_eq!(a.debug_rel_all_tol(&b, &0.0), MyNoFieldsType {}); 32 | assert_eq!(a.debug_ulps_all_tol(&b, &0), MyNoFieldsTypeUlps {}); 33 | } 34 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq_all/assert_float_eq_all_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{ 2 | AssertFloatEq, AssertFloatEqAll, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, FloatEqUlpsTol, 3 | }; 4 | 5 | #[derive( 6 | Debug, 7 | Clone, 8 | Copy, 9 | PartialEq, 10 | FloatEqUlpsTol, 11 | FloatEq, 12 | FloatEqDebugUlpsDiff, 13 | AssertFloatEq, 14 | FloatEqAll, 15 | AssertFloatEqAll, 16 | )] 17 | #[float_eq( 18 | ulps_tol = "MyComplex32Ulps", 19 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 20 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 21 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq", 22 | all_tol = "f32" 23 | )] 24 | struct MyComplex32(f32, f32); 25 | 26 | fn main() { 27 | let a = MyComplex32(1.0, 200.0); 28 | let b = MyComplex32(50.0, 1.0); 29 | 30 | assert_eq!(a.debug_abs_all_tol(&b, &0.2), MyComplex32(0.2, 0.2)); 31 | assert_eq!(a.debug_rel_all_tol(&b, &0.2), MyComplex32(10.0, 40.0)); 32 | assert_eq!(a.debug_ulps_all_tol(&b, &2), MyComplex32Ulps(2, 2)); 33 | } 34 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/assert_float_eq_all/assert_float_eq_all_unit.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{ 2 | AssertFloatEq, AssertFloatEqAll, FloatEq, FloatEqAll, FloatEqDebugUlpsDiff, FloatEqUlpsTol, 3 | }; 4 | 5 | #[derive( 6 | Debug, 7 | Clone, 8 | Copy, 9 | PartialEq, 10 | FloatEqUlpsTol, 11 | FloatEq, 12 | FloatEqDebugUlpsDiff, 13 | AssertFloatEq, 14 | FloatEqAll, 15 | AssertFloatEqAll, 16 | )] 17 | #[float_eq( 18 | ulps_tol = "MyUnitTypeUlps", 19 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 20 | debug_ulps_diff = "MyUnitTypeDebugUlpsDiff", 21 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq", 22 | all_tol = "f32" 23 | )] 24 | struct MyUnitType(); 25 | 26 | fn main() { 27 | let a = MyUnitType {}; 28 | let b = MyUnitType {}; 29 | 30 | assert_eq!(a.debug_abs_all_tol(&b, &0.0), MyUnitType {}); 31 | assert_eq!(a.debug_rel_all_tol(&b, &0.0), MyUnitType {}); 32 | assert_eq!(a.debug_ulps_all_tol(&b, &0), MyUnitTypeUlps {}); 33 | } 34 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_duplicate_type_name.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqDebugUlpsDiff; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 4 | #[float_eq(debug_ulps_diff = "Name1", debug_ulps_diff = "Name2")] 5 | struct MyComplex32 { 6 | re: f32, 7 | im: f32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_duplicate_type_name.stderr: -------------------------------------------------------------------------------- 1 | error: Duplicate `debug_ulps_diff` argument 2 | --> $DIR/debug_ulps_diff_duplicate_type_name.rs:4:39 3 | | 4 | 4 | #[float_eq(debug_ulps_diff = "Name1", debug_ulps_diff = "Name2")] 5 | | ^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_enum.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqDebugUlpsDiff; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, FloatEqDebugUlpsDiff)] 4 | enum SomeEnum { 5 | Float(f32), 6 | Double(f64), 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_enum.stderr: -------------------------------------------------------------------------------- 1 | error: FloatEqDebugUlpsDiff may only be derived for structs. 2 | --> $DIR/debug_ulps_diff_enum.rs:4:6 3 | | 4 | 4 | enum SomeEnum { 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_generic.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqDebugUlpsDiff; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 4 | struct MyComplexUlps { 5 | re: T, 6 | im: T, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_generic.stderr: -------------------------------------------------------------------------------- 1 | error: This trait does not yet support derive for generic types. 2 | --> $DIR/debug_ulps_diff_generic.rs:3:41 3 | | 4 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 5 | | ^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FloatEqDebugUlpsDiff` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_missing_type_name.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqDebugUlpsDiff; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 4 | struct MyComplex32 { 5 | re: f32, 6 | im: f32, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_missing_type_name.stderr: -------------------------------------------------------------------------------- 1 | error: Missing debug ULPs diff type name required to derive trait. 2 | 3 | help: try adding `#[float_eq(debug_ulps_diff = "MyComplex32DebugUlpsDiff")]` to your type. 4 | --> $DIR/debug_ulps_diff_missing_type_name.rs:3:41 5 | | 6 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 7 | | ^^^^^^^^^^^^^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `FloatEqDebugUlpsDiff` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{DebugUlpsDiff, FloatEqDebugUlpsDiff}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 4 | #[float_eq( 5 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 6 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 7 | )] 8 | struct MyComplex32 { 9 | re: f32, 10 | im: f32, 11 | } 12 | 13 | fn main() { 14 | let a = DebugUlpsDiff:: { 15 | re: Some(1), 16 | im: Some(2), 17 | }; 18 | let b = a; // Clone, Copy 19 | 20 | // Debug, PartialEq 21 | assert_eq!(a, b); 22 | assert_ne!( 23 | a, 24 | MyComplex32DebugUlpsDiff { 25 | re: None, 26 | im: Some(2) 27 | } 28 | ); 29 | assert_ne!( 30 | a, 31 | MyComplex32DebugUlpsDiff { 32 | re: Some(1), 33 | im: Some(3) 34 | } 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_struct_custom_debug.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use float_eq::{DebugUlpsDiff, FloatEqDebugUlpsDiff}; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 5 | #[float_eq( 6 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 7 | debug_ulps_diff_derive = "Clone, Copy, PartialEq" 8 | )] 9 | struct MyComplex32 { 10 | re: f32, 11 | im: f32, 12 | } 13 | 14 | impl fmt::Debug for MyComplex32DebugUlpsDiff { 15 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 16 | write!( 17 | f, 18 | "CustomDebug_MyComplex32DebugUlpsDiff({:?}, {:?})", 19 | self.re, self.im 20 | ) 21 | } 22 | } 23 | 24 | fn main() { 25 | let a = DebugUlpsDiff:: { 26 | re: Some(1), 27 | im: Some(2), 28 | }; 29 | let b = a; // Clone, Copy 30 | 31 | // Debug, PartialEq 32 | assert_eq!(a, b); 33 | assert_ne!( 34 | a, 35 | MyComplex32DebugUlpsDiff { 36 | re: None, 37 | im: Some(2) 38 | } 39 | ); 40 | assert_ne!( 41 | a, 42 | MyComplex32DebugUlpsDiff { 43 | re: Some(1), 44 | im: Some(3) 45 | } 46 | ); 47 | assert_eq!( 48 | format!( 49 | "{:?}", 50 | MyComplex32DebugUlpsDiff { 51 | re: Some(1), 52 | im: None 53 | } 54 | ), 55 | "CustomDebug_MyComplex32DebugUlpsDiff(Some(1), None)" 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_struct_no_fields.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{DebugUlpsDiff, FloatEqDebugUlpsDiff}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 4 | #[float_eq( 5 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 6 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 7 | )] 8 | struct MyComplex32; 9 | 10 | fn main() { 11 | let a = DebugUlpsDiff:: {}; 12 | let b = a; // Clone, Copy 13 | 14 | // Debug, PartialEq 15 | assert_eq!(a, b); 16 | } 17 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqDebugUlpsDiff; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 4 | #[float_eq( 5 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 6 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 7 | )] 8 | struct MyComplex32(f32, f32); 9 | 10 | fn main() { 11 | let a = MyComplex32DebugUlpsDiff(Some(1), Some(2)); 12 | let b = a; // Clone, Copy 13 | 14 | // Debug, PartialEq 15 | assert_eq!(a, b); 16 | assert_ne!(a, MyComplex32DebugUlpsDiff(None, Some(2))); 17 | assert_ne!(a, MyComplex32DebugUlpsDiff(Some(1), Some(3))); 18 | } 19 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/debug_ulps_diff/debug_ulps_diff_unit.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqDebugUlpsDiff; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqDebugUlpsDiff)] 4 | #[float_eq( 5 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 6 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 7 | )] 8 | struct MyComplex32(); 9 | 10 | fn main() { 11 | let a = MyComplex32DebugUlpsDiff(); 12 | let b = a; // Clone, Copy 13 | 14 | // Debug, PartialEq 15 | assert_eq!(a, b); 16 | } 17 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/derive_float_eq/derive_float_eq.rs: -------------------------------------------------------------------------------- 1 | mod my_module { 2 | use float_eq::{derive_float_eq, UlpsTol}; 3 | 4 | #[derive_float_eq( 5 | ulps_tol = "MyComplex32Ulps", 6 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 7 | debug_ulps_diff = "MyComplex32UlpsDiff", 8 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 9 | )] 10 | #[derive(Debug, Clone, Copy, PartialEq)] 11 | pub struct MyComplex32 { 12 | pub re: f32, 13 | pub im: f32, 14 | } 15 | 16 | impl MyComplex32 { 17 | pub fn new(re: f32, im: f32) -> MyComplex32 { 18 | MyComplex32 { re, im } 19 | } 20 | } 21 | 22 | impl MyComplex32Ulps { 23 | pub fn new(re: UlpsTol, im: UlpsTol) -> MyComplex32Ulps { 24 | MyComplex32Ulps { re, im } 25 | } 26 | } 27 | } 28 | 29 | fn main() { 30 | use float_eq::{assert_float_eq, assert_float_ne}; 31 | use my_module::{MyComplex32, MyComplex32Ulps}; 32 | 33 | let a = MyComplex32 { re: 1.0, im: -2.0 }; 34 | assert_float_eq!(a, a, abs <= MyComplex32 { re: 0.0, im: 0.0 }); 35 | assert_float_eq!(a, a, rel <= MyComplex32 { re: 0.0, im: 0.0 }); 36 | assert_float_eq!(a, a, ulps <= MyComplex32Ulps { re: 0, im: 0 }); 37 | 38 | let b = MyComplex32 { 39 | re: 1.000_000_1, 40 | im: -2.000_000_5, 41 | }; 42 | 43 | assert_float_eq!(a, b, abs <= MyComplex32::new(0.000_000_15, 0.000_000_55)); 44 | assert_float_ne!(a, b, abs <= MyComplex32::new(0.000_000_05, 0.000_000_55)); 45 | assert_float_ne!(a, b, abs <= MyComplex32::new(0.000_000_15, 0.000_000_45)); 46 | 47 | assert_float_eq!(a, b, rel <= MyComplex32::new(0.000_000_15, 0.000_000_25)); 48 | assert_float_ne!(a, b, rel <= MyComplex32::new(0.000_000_05, 0.000_000_25)); 49 | assert_float_ne!(a, b, rel <= MyComplex32::new(0.000_000_15, 0.000_000_15)); 50 | 51 | assert_float_eq!(a, b, ulps <= MyComplex32Ulps::new(1, 2)); 52 | assert_float_ne!(a, b, ulps <= MyComplex32Ulps::new(0, 2)); 53 | assert_float_ne!(a, b, ulps <= MyComplex32Ulps::new(1, 1)); 54 | } 55 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/derive_float_eq/derive_float_eq_all.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{assert_float_eq, assert_float_ne, derive_float_eq}; 2 | 3 | #[derive_float_eq( 4 | ulps_tol = "MyComplex32Ulps", 5 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 6 | debug_ulps_diff = "MyComplex32UlpsDiff", 7 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq", 8 | all_tol = "f32" 9 | )] 10 | #[derive(Debug, Clone, Copy, PartialEq)] 11 | struct MyComplex32 { 12 | re: f32, 13 | im: f32, 14 | } 15 | 16 | impl MyComplex32 { 17 | fn new(re: f32, im: f32) -> MyComplex32 { 18 | MyComplex32 { re, im } 19 | } 20 | } 21 | 22 | fn main() { 23 | let a = MyComplex32 { re: 1.0, im: -2.0 }; 24 | assert_float_eq!(a, a, abs_all <= 0.0); 25 | assert_float_eq!(a, a, rel_all <= 0.0); 26 | assert_float_eq!(a, a, ulps_all <= 0); 27 | 28 | let b = MyComplex32 { 29 | re: 1.000_000_1, 30 | im: -2.000_000_5, 31 | }; 32 | 33 | assert_float_eq!(a, b, abs_all <= 0.000_000_55); 34 | assert_float_ne!(a, b, abs_all <= 0.000_000_45); 35 | 36 | assert_float_eq!(a, b, rel_all <= 0.000_000_25); 37 | assert_float_ne!(a, b, rel_all <= 0.000_000_15); 38 | 39 | assert_float_eq!(a, b, ulps_all <= 2); 40 | assert_float_ne!(a, b, ulps_all <= 1); 41 | } 42 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/derive_float_eq/derive_float_eq_all_custom_debug.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use float_eq::{assert_float_eq, assert_float_ne, derive_float_eq}; 3 | 4 | #[derive_float_eq( 5 | ulps_tol = "MyComplex32Ulps", 6 | ulps_tol_derive = "Clone, Copy, PartialEq", 7 | debug_ulps_diff = "MyComplex32DebugUlpsDiff", 8 | debug_ulps_diff_derive = "Clone, Copy, PartialEq", 9 | all_tol = "f32" 10 | )] 11 | #[derive(Debug, Clone, Copy, PartialEq)] 12 | struct MyComplex32 { 13 | re: f32, 14 | im: f32, 15 | } 16 | 17 | impl fmt::Debug for MyComplex32Ulps { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | write!(f, "CustomDebug_MyComplex32Ulps({}, {})", self.re, self.im) 20 | } 21 | } 22 | 23 | impl fmt::Debug for MyComplex32DebugUlpsDiff { 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | write!( 26 | f, 27 | "CustomDebug_MyComplex32DebugUlpsDiff({:?}, {:?})", 28 | self.re, self.im 29 | ) 30 | } 31 | } 32 | 33 | impl MyComplex32 { 34 | fn new(re: f32, im: f32) -> MyComplex32 { 35 | MyComplex32 { re, im } 36 | } 37 | } 38 | 39 | fn main() { 40 | let a = MyComplex32 { re: 1.0, im: -2.0 }; 41 | assert_float_eq!(a, a, abs_all <= 0.0); 42 | assert_float_eq!(a, a, rel_all <= 0.0); 43 | assert_float_eq!(a, a, ulps_all <= 0); 44 | 45 | let b = MyComplex32 { 46 | re: 1.000_000_1, 47 | im: -2.000_000_5, 48 | }; 49 | 50 | assert_float_eq!(a, b, abs_all <= 0.000_000_55); 51 | assert_float_ne!(a, b, abs_all <= 0.000_000_45); 52 | 53 | assert_float_eq!(a, b, rel_all <= 0.000_000_25); 54 | assert_float_ne!(a, b, rel_all <= 0.000_000_15); 55 | 56 | assert_float_eq!(a, b, ulps_all <= 2); 57 | assert_float_ne!(a, b, ulps_all <= 1); 58 | 59 | assert_eq!( 60 | format!("{:?}", MyComplex32Ulps { re: 1, im: 2 }), 61 | "CustomDebug_MyComplex32Ulps(1, 2)" 62 | ); 63 | assert_eq!( 64 | format!( 65 | "{:?}", 66 | MyComplex32DebugUlpsDiff { 67 | re: Some(1), 68 | im: None 69 | } 70 | ), 71 | "CustomDebug_MyComplex32DebugUlpsDiff(Some(1), None)" 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/derive_float_eq/derive_float_eq_missing_debug_ulps_diff.rs: -------------------------------------------------------------------------------- 1 | use float_eq::derive_float_eq; 2 | 3 | #[derive_float_eq( 4 | ulps_tol = "MyComplex32Ulps", 5 | //debug_ulps_diff = "MyComplex32UlpsDiff" 6 | )] 7 | #[derive(Debug, Clone, Copy, PartialEq)] 8 | struct MyComplex32 { 9 | re: f32, 10 | im: f32, 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/derive_float_eq/derive_float_eq_missing_debug_ulps_diff.stderr: -------------------------------------------------------------------------------- 1 | error: Missing debug ULPs diff type name required to derive trait. 2 | 3 | help: try specifying `debug_ulps_diff = "MyComplex32DebugUlpsDiff"` in `derive_float_eq`. 4 | --> $DIR/derive_float_eq_missing_debug_ulps_diff.rs:3:1 5 | | 6 | 3 | / #[derive_float_eq( 7 | 4 | | ulps_tol = "MyComplex32Ulps", 8 | 5 | | //debug_ulps_diff = "MyComplex32UlpsDiff" 9 | 6 | | )] 10 | | |__^ 11 | | 12 | = note: this error originates in the attribute macro `derive_float_eq` (in Nightly builds, run with -Z macro-backtrace for more info) 13 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/derive_float_eq/derive_float_eq_missing_ulps_tol.rs: -------------------------------------------------------------------------------- 1 | use float_eq::derive_float_eq; 2 | 3 | #[derive_float_eq( 4 | //ulps_tol = "MyComplex32Ulps", 5 | debug_ulps_diff = "MyComplex32UlpsDiff" 6 | )] 7 | #[derive(Debug, Clone, Copy, PartialEq)] 8 | struct MyComplex32 { 9 | re: f32, 10 | im: f32, 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/derive_float_eq/derive_float_eq_missing_ulps_tol.stderr: -------------------------------------------------------------------------------- 1 | error: Missing ULPs tolerance type name required to derive trait. 2 | 3 | help: try specifying `ulps_tol = "MyComplex32Ulps"` in `derive_float_eq`. 4 | --> $DIR/derive_float_eq_missing_ulps_tol.rs:3:1 5 | | 6 | 3 | / #[derive_float_eq( 7 | 4 | | //ulps_tol = "MyComplex32Ulps", 8 | 5 | | debug_ulps_diff = "MyComplex32UlpsDiff" 9 | 6 | | )] 10 | | |__^ 11 | | 12 | = note: this error originates in the attribute macro `derive_float_eq` (in Nightly builds, run with -Z macro-backtrace for more info) 13 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/derive_float_eq/derive_float_eq_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | mod my_module { 2 | use float_eq::derive_float_eq; 3 | 4 | #[derive_float_eq( 5 | ulps_tol = "MyComplex32Ulps", 6 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 7 | debug_ulps_diff = "MyComplex32UlpsDiff", 8 | debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq" 9 | )] 10 | #[derive(Debug, Clone, Copy, PartialEq)] 11 | pub struct MyComplex32(pub f32, pub f32); 12 | } 13 | 14 | fn main() { 15 | use float_eq::{assert_float_eq, assert_float_ne}; 16 | use my_module::{MyComplex32, MyComplex32Ulps}; 17 | 18 | let a = MyComplex32(1.0, -2.0); 19 | assert_float_eq!(a, a, abs <= MyComplex32(0.0, 0.0)); 20 | assert_float_eq!(a, a, rel <= MyComplex32(0.0, 0.0)); 21 | assert_float_eq!(a, a, ulps <= MyComplex32Ulps(0, 0)); 22 | 23 | let b = MyComplex32(1.000_000_1, -2.000_000_5); 24 | 25 | assert_float_eq!(a, b, abs <= MyComplex32(0.000_000_15, 0.000_000_55)); 26 | assert_float_ne!(a, b, abs <= MyComplex32(0.000_000_05, 0.000_000_55)); 27 | assert_float_ne!(a, b, abs <= MyComplex32(0.000_000_15, 0.000_000_45)); 28 | 29 | assert_float_eq!(a, b, rel <= MyComplex32(0.000_000_15, 0.000_000_25)); 30 | assert_float_ne!(a, b, rel <= MyComplex32(0.000_000_05, 0.000_000_25)); 31 | assert_float_ne!(a, b, rel <= MyComplex32(0.000_000_15, 0.000_000_15)); 32 | 33 | assert_float_eq!(a, b, ulps <= MyComplex32Ulps(1, 2)); 34 | assert_float_ne!(a, b, ulps <= MyComplex32Ulps(0, 2)); 35 | assert_float_ne!(a, b, ulps <= MyComplex32Ulps(1, 1)); 36 | } 37 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq/float_eq_enum.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqUlpsTol}; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, FloatEqUlpsTol, FloatEq)] 4 | enum SomeEnum { 5 | Float(f32), 6 | Double(f64), 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq/float_eq_enum.stderr: -------------------------------------------------------------------------------- 1 | error: FloatEqUlpsTol may only be derived for structs. 2 | --> $DIR/float_eq_enum.rs:4:6 3 | | 4 | 4 | enum SomeEnum { 5 | | ^^^^^^^^ 6 | 7 | error: FloatEq may only be derived for structs. 8 | --> $DIR/float_eq_enum.rs:4:6 9 | | 10 | 4 | enum SomeEnum { 11 | | ^^^^^^^^ 12 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq/float_eq_generic.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq)] 4 | struct MyComplexUlps { 5 | re: T, 6 | im: T, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq/float_eq_generic.stderr: -------------------------------------------------------------------------------- 1 | error: This trait does not yet support derive for generic types. 2 | --> $DIR/float_eq_generic.rs:3:41 3 | | 4 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq)] 5 | | ^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FloatEqUlpsTol` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error: This trait does not yet support derive for generic types. 10 | --> $DIR/float_eq_generic.rs:3:57 11 | | 12 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq)] 13 | | ^^^^^^^ 14 | | 15 | = note: this error originates in the derive macro `FloatEq` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq/float_eq_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq)] 4 | #[float_eq(ulps_tol = "MyComplex32Ulps")] 5 | struct MyComplex32 { 6 | re: f32, 7 | im: f32, 8 | } 9 | 10 | impl MyComplex32 { 11 | fn new(re: f32, im: f32) -> MyComplex32 { 12 | MyComplex32 { re, im } 13 | } 14 | } 15 | 16 | fn main() { 17 | let a = MyComplex32::new(2.0, -1_000_000.0); 18 | let b = MyComplex32::new(2.000_000_5, -1_000_000.06); 19 | 20 | assert!(a.eq_abs(&b, &MyComplex32::new(0.000_000_5, 0.07))); 21 | assert!(a.ne_abs(&b, &MyComplex32::new(0.000_000_4, 0.07))); 22 | assert!(a.ne_abs(&b, &MyComplex32::new(0.000_000_5, 0.06))); 23 | 24 | assert!(a.eq_rel(&b, &MyComplex32::new(0.000_000_25, 0.000_000_1))); 25 | assert!(a.ne_rel(&b, &MyComplex32::new(0.000_000_15, 0.000_000_1))); 26 | assert!(a.ne_rel(&b, &MyComplex32::new(0.000_000_25, 0.000_000_05))); 27 | 28 | assert!(a.eq_ulps(&b, &MyComplex32Ulps { re: 2, im: 1 })); 29 | assert!(a.ne_ulps(&b, &MyComplex32Ulps { re: 1, im: 1 })); 30 | assert!(a.ne_ulps(&b, &MyComplex32Ulps { re: 2, im: 0 })); 31 | } 32 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq/float_eq_struct_no_fields.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq)] 4 | #[float_eq(ulps_tol = "MyNoFieldsTypeUlps")] 5 | struct MyNoFieldsType; 6 | 7 | fn main() { 8 | let a = MyNoFieldsType {}; 9 | let b = MyNoFieldsType {}; 10 | 11 | assert!(a.eq_abs(&b, &MyNoFieldsType {})); 12 | assert!(a.eq_rel(&b, &MyNoFieldsType {})); 13 | assert!(a.eq_ulps(&b, &MyNoFieldsTypeUlps {})); 14 | } 15 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq/float_eq_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq)] 4 | #[float_eq(ulps_tol = "MyTupleTypeUlps")] 5 | struct MyTupleType(f32, f64); 6 | 7 | fn main() { 8 | let a = MyTupleType(2.0, -1_000_000.0); 9 | let b = MyTupleType(2.000_000_5, -1_000_000.000_000_000_1); 10 | 11 | assert!(a.eq_abs(&b, &MyTupleType(0.000_000_5, 0.000_000_000_2))); 12 | assert!(a.ne_abs(&b, &MyTupleType(0.000_000_4, 0.000_000_000_2))); 13 | assert!(a.ne_abs(&b, &MyTupleType(0.000_000_5, 0.000_000_000_05))); 14 | 15 | assert!(a.eq_rel(&b, &MyTupleType(0.000_000_25, 0.000_000_000_000_000_2))); 16 | assert!(a.ne_rel(&b, &MyTupleType(0.000_000_15, 0.000_000_000_000_000_2))); 17 | assert!(a.ne_rel(&b, &MyTupleType(0.000_000_25, 0.000_000_000_000_000_1))); 18 | 19 | assert!(a.eq_ulps(&b, &MyTupleTypeUlps(2, 1))); 20 | assert!(a.ne_ulps(&b, &MyTupleTypeUlps(1, 1))); 21 | assert!(a.ne_ulps(&b, &MyTupleTypeUlps(2, 0))); 22 | } 23 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq/float_eq_unit.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq)] 4 | #[float_eq(ulps_tol = "MyUnitTypeUlps")] 5 | struct MyUnitType(); 6 | 7 | fn main() { 8 | let a = MyUnitType {}; 9 | let b = MyUnitType {}; 10 | 11 | assert!(a.eq_abs(&b, &MyUnitType {})); 12 | assert!(a.eq_rel(&b, &MyUnitType {})); 13 | assert!(a.eq_ulps(&b, &MyUnitTypeUlps {})); 14 | } 15 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_duplicate_tol.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqAll, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 4 | #[float_eq( 5 | ulps_tol = "MyComplex32Ulps", 6 | all_tol = "Tol1", 7 | all_tol = "Tol2" 8 | )] 9 | struct MyComplex32 { 10 | re: f32, 11 | im: f32, 12 | } 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_duplicate_tol.stderr: -------------------------------------------------------------------------------- 1 | error: Duplicate `all_tol` argument 2 | --> $DIR/float_eq_all_duplicate_tol.rs:7:5 3 | | 4 | 7 | all_tol = "Tol2" 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_enum.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqAll, FloatEqUlpsTol}; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, FloatEqUlpsTol, FloatEq, FloatEqAll)] 4 | enum SomeEnum { 5 | Float(f32), 6 | Double(f64), 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_enum.stderr: -------------------------------------------------------------------------------- 1 | error: FloatEqUlpsTol may only be derived for structs. 2 | --> $DIR/float_eq_all_enum.rs:4:6 3 | | 4 | 4 | enum SomeEnum { 5 | | ^^^^^^^^ 6 | 7 | error: FloatEq may only be derived for structs. 8 | --> $DIR/float_eq_all_enum.rs:4:6 9 | | 10 | 4 | enum SomeEnum { 11 | | ^^^^^^^^ 12 | 13 | error: FloatEqAll may only be derived for structs. 14 | --> $DIR/float_eq_all_enum.rs:4:6 15 | | 16 | 4 | enum SomeEnum { 17 | | ^^^^^^^^ 18 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_generic.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqAll, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 4 | struct MyComplexUlps { 5 | re: T, 6 | im: T, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_generic.stderr: -------------------------------------------------------------------------------- 1 | error: This trait does not yet support derive for generic types. 2 | --> $DIR/float_eq_all_generic.rs:3:41 3 | | 4 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 5 | | ^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FloatEqUlpsTol` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error: This trait does not yet support derive for generic types. 10 | --> $DIR/float_eq_all_generic.rs:3:57 11 | | 12 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 13 | | ^^^^^^^ 14 | | 15 | = note: this error originates in the derive macro `FloatEq` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error: This trait does not yet support derive for generic types. 18 | --> $DIR/float_eq_all_generic.rs:3:66 19 | | 20 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 21 | | ^^^^^^^^^^ 22 | | 23 | = note: this error originates in the derive macro `FloatEqAll` (in Nightly builds, run with -Z macro-backtrace for more info) 24 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_missing_tol.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqAll, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 4 | #[float_eq(ulps_tol = "MyComplex32Ulps")] 5 | struct MyComplex32 { 6 | re: f32, 7 | im: f32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_missing_tol.stderr: -------------------------------------------------------------------------------- 1 | error: Missing Tol type name required to derive trait. 2 | 3 | help: try adding `#[float_eq(all_tol = "T")]` to your type, where T is commonly `f32` or `f64`. 4 | --> $DIR/float_eq_all_missing_tol.rs:3:66 5 | | 6 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 7 | | ^^^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `FloatEqAll` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqAll, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 4 | #[float_eq(ulps_tol = "MyComplex32Ulps", all_tol = "f32")] 5 | struct MyComplex32 { 6 | re: f32, 7 | im: f32, 8 | } 9 | 10 | impl MyComplex32 { 11 | fn new(re: f32, im: f32) -> MyComplex32 { 12 | MyComplex32 { re, im } 13 | } 14 | } 15 | 16 | fn main() { 17 | let a = MyComplex32::new(2.0, -1_000_000.0); 18 | let b = MyComplex32::new(2.000_000_5, -1_000_000.06); 19 | 20 | assert!(a.eq_abs_all(&b, &0.07)); 21 | assert!(a.ne_abs_all(&b, &0.06)); 22 | 23 | assert!(a.eq_rel_all(&b, &0.000_000_25)); 24 | assert!(a.ne_rel_all(&b, &0.000_000_15)); 25 | 26 | assert!(a.eq_ulps_all(&b, &2)); 27 | assert!(a.ne_ulps_all(&b, &1)); 28 | } 29 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_struct_no_fields.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqAll, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 4 | #[float_eq(ulps_tol = "MyNoFieldsTypeUlps", all_tol = "f64")] 5 | struct MyNoFieldsType; 6 | 7 | fn main() { 8 | let a = MyNoFieldsType {}; 9 | let b = MyNoFieldsType {}; 10 | 11 | assert!(a.eq_abs_all(&b, &0.0)); 12 | assert!(a.eq_rel_all(&b, &0.0)); 13 | assert!(a.eq_ulps_all(&b, &0)); 14 | } 15 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqAll, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 4 | #[float_eq(ulps_tol = "MyTupleTypeUlps", all_tol = "f32")] 5 | struct MyComplex32(f32, f32); 6 | 7 | fn main() { 8 | let a = MyComplex32(2.0, -1_000_000.0); 9 | let b = MyComplex32(2.000_000_5, -1_000_000.06); 10 | 11 | assert!(a.eq_abs_all(&b, &0.07)); 12 | assert!(a.ne_abs_all(&b, &0.06)); 13 | 14 | assert!(a.eq_rel_all(&b, &0.000_000_25)); 15 | assert!(a.ne_rel_all(&b, &0.000_000_15)); 16 | 17 | assert!(a.eq_ulps_all(&b, &2)); 18 | assert!(a.ne_ulps_all(&b, &1)); 19 | } 20 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_all/float_eq_all_unit.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEq, FloatEqAll, FloatEqUlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol, FloatEq, FloatEqAll)] 4 | #[float_eq(ulps_tol = "MyUnitTypeUlps", all_tol = "f32")] 5 | struct MyUnitType(); 6 | 7 | fn main() { 8 | let a = MyUnitType {}; 9 | let b = MyUnitType {}; 10 | 11 | assert!(a.eq_abs_all(&b, &0.0)); 12 | assert!(a.eq_rel_all(&b, &0.0)); 13 | assert!(a.eq_ulps_all(&b, &0)); 14 | } 15 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_attribute/float_eq_malformed_param.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq(ulps = "MyComplex32Ulps", cheese)] 5 | struct MyComplex32 { 6 | re: f32, 7 | im: f32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_attribute/float_eq_malformed_param.stderr: -------------------------------------------------------------------------------- 1 | error: Expected a `name = "value"` pair. 2 | --> $DIR/float_eq_malformed_param.rs:4:38 3 | | 4 | 4 | #[float_eq(ulps = "MyComplex32Ulps", cheese)] 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_attribute/float_eq_malformed_value.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq(ulps_tol = "{}")] 5 | struct MyComplex32 { 6 | re: f32, 7 | im: f32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_attribute/float_eq_malformed_value.stderr: -------------------------------------------------------------------------------- 1 | error: Invalid value `{}` for attribute `ulps_tol`. 2 | --> $DIR/float_eq_malformed_value.rs:4:23 3 | | 4 | 4 | #[float_eq(ulps_tol = "{}")] 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_attribute/float_eq_no_params_list.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq] 5 | struct MyComplex32 { 6 | re: f32, 7 | im: f32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_attribute/float_eq_no_params_list.stderr: -------------------------------------------------------------------------------- 1 | error: float_eq attribute must be a list of options, for example `#[float_eq(ulps_tol = "MyComplex32Ulps")]` 2 | --> $DIR/float_eq_no_params_list.rs:4:3 3 | | 4 | 4 | #[float_eq] 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_attribute/float_eq_unknown_param.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq(ulps_tol = "MyComplex32Ulps", cheese = "Hello")] 5 | struct MyComplex32 { 6 | re: f32, 7 | im: f32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/float_eq_attribute/float_eq_unknown_param.stderr: -------------------------------------------------------------------------------- 1 | error: 'cheese' is not a valid float_eq derive option. 2 | --> $DIR/float_eq_unknown_param.rs:4:42 3 | | 4 | 4 | #[float_eq(ulps_tol = "MyComplex32Ulps", cheese = "Hello")] 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_duplicate_type_name.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq( 5 | ulps_tol = "Name1", 6 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq", 7 | ulps_tol = "Name2" 8 | )] 9 | struct MyComplex32 { 10 | re: f32, 11 | im: f32, 12 | } 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_duplicate_type_name.stderr: -------------------------------------------------------------------------------- 1 | error: Duplicate `ulps_tol` argument 2 | --> $DIR/ulps_tol_duplicate_type_name.rs:7:5 3 | | 4 | 7 | ulps_tol = "Name2" 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_enum.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, FloatEqUlpsTol)] 4 | enum SomeEnum { 5 | Float(f32), 6 | Double(f64), 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_enum.stderr: -------------------------------------------------------------------------------- 1 | error: FloatEqUlpsTol may only be derived for structs. 2 | --> $DIR/ulps_tol_enum.rs:4:6 3 | | 4 | 4 | enum SomeEnum { 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_generic.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 4 | struct MyComplexUlps { 5 | re: T, 6 | im: T, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_generic.stderr: -------------------------------------------------------------------------------- 1 | error: This trait does not yet support derive for generic types. 2 | --> $DIR/ulps_tol_generic.rs:3:41 3 | | 4 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 5 | | ^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FloatEqUlpsTol` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_missing_type_name.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 4 | struct MyComplex32 { 5 | re: f32, 6 | im: f32, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_missing_type_name.stderr: -------------------------------------------------------------------------------- 1 | error: Missing ULPs tolerance type name required to derive trait. 2 | 3 | help: try adding `#[float_eq(ulps_tol = "MyComplex32Ulps")]` to your type. 4 | --> $DIR/ulps_tol_missing_type_name.rs:3:41 5 | | 6 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 7 | | ^^^^^^^^^^^^^^ 8 | | 9 | = note: this error originates in the derive macro `FloatEqUlpsTol` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEqUlpsTol, UlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq( 5 | ulps_tol = "MyComplex32Ulps", 6 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq" 7 | )] 8 | struct MyComplex32 { 9 | re: f32, 10 | im: f32, 11 | } 12 | 13 | fn main() { 14 | let a = UlpsTol:: { re: 1, im: 2 }; 15 | let b = a; // Clone, Copy 16 | 17 | // Debug, PartialEq 18 | assert_eq!(a, b); 19 | assert_ne!(a, MyComplex32Ulps { re: 3, im: 2 }); 20 | assert_ne!(a, MyComplex32Ulps { re: 1, im: 3 }); 21 | } 22 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_struct_custom_debug.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use float_eq::{FloatEqUlpsTol, UlpsTol}; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 5 | #[float_eq( 6 | ulps_tol = "MyComplex32Ulps", 7 | ulps_tol_derive = "Clone, Copy, PartialEq" 8 | )] 9 | struct MyComplex32 { 10 | re: f32, 11 | im: f32, 12 | } 13 | 14 | impl fmt::Debug for MyComplex32Ulps { 15 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 16 | write!(f, "CustomDebug_MyComplex32Ulps({}, {})", self.re, self.im) 17 | } 18 | } 19 | 20 | fn main() { 21 | let a = UlpsTol:: { re: 1, im: 2 }; 22 | let b = a; // Clone, Copy 23 | 24 | // Debug, PartialEq 25 | assert_eq!(a, b); 26 | assert_ne!(a, MyComplex32Ulps { re: 3, im: 2 }); 27 | assert_ne!(a, MyComplex32Ulps { re: 1, im: 3 }); 28 | assert_eq!( 29 | format!("{:?}", MyComplex32Ulps { re: 1, im: 2 }), 30 | "CustomDebug_MyComplex32Ulps(1, 2)" 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_struct_no_fields.rs: -------------------------------------------------------------------------------- 1 | use float_eq::{FloatEqUlpsTol, UlpsTol}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq( 5 | ulps_tol = "MyComplex32Ulps", 6 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq" 7 | )] 8 | struct MyComplex32; 9 | 10 | fn main() { 11 | let a = UlpsTol:: {}; 12 | let b = a; // Clone, Copy 13 | 14 | // Debug, PartialEq 15 | assert_eq!(a, b); 16 | } 17 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq( 5 | ulps_tol = "MyComplex32Ulps", 6 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq" 7 | )] 8 | struct MyComplex32(f32, f32); 9 | 10 | fn main() { 11 | let a = MyComplex32Ulps(1, 2); 12 | let b = a; // Clone, Copy 13 | 14 | // Debug, PartialEq 15 | assert_eq!(a, b); 16 | assert_ne!(a, MyComplex32Ulps(3, 2)); 17 | assert_ne!(a, MyComplex32Ulps(1, 3)); 18 | } 19 | -------------------------------------------------------------------------------- /float_eq/tests/derive_tests/ulps_tol/ulps_tol_unit.rs: -------------------------------------------------------------------------------- 1 | use float_eq::FloatEqUlpsTol; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, FloatEqUlpsTol)] 4 | #[float_eq( 5 | ulps_tol = "MyComplex32Ulps", 6 | ulps_tol_derive = "Clone, Copy, Debug, PartialEq" 7 | )] 8 | struct MyComplex32(); 9 | 10 | fn main() { 11 | let a = MyComplex32Ulps(); 12 | let b = a; // Clone, Copy 13 | 14 | // Debug, PartialEq 15 | assert_eq!(a, b); 16 | } 17 | -------------------------------------------------------------------------------- /float_eq/tests/unit_tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp, clippy::unit_cmp)] 2 | 3 | // A selection of NaN values from the edges of the ranges of negative and 4 | // positive NaN values and their payloads. Testing every single NaN value 5 | // is viable in reasonable time for f32, but there are just too many f64 6 | // values so this is the compromise. 7 | #[derive(Clone, Copy)] 8 | struct NaNBits { 9 | pos_min: T, 10 | pos_max: T, 11 | neg_min: T, 12 | neg_max: T, 13 | } 14 | 15 | const F32_NAN_BITS: NaNBits = NaNBits { 16 | pos_min: 0x7F_80_00_01, 17 | pos_max: 0x7F_FF_FF_FF, 18 | neg_min: 0xFF_80_00_01, 19 | neg_max: 0xFF_FF_FF_FF, 20 | }; 21 | 22 | const F64_NAN_BITS: NaNBits = NaNBits { 23 | pos_min: 0x7F_F0_00_00_00_00_00_01, 24 | pos_max: 0x7F_FF_FF_FF_FF_FF_FF_FF, 25 | neg_min: 0xFF_F0_00_00_00_00_00_01, 26 | neg_max: 0xFF_FF_FF_FF_FF_FF_FF_FF, 27 | }; 28 | 29 | macro_rules! impl_test_helpers { 30 | ($float:ident, $uint:ident, $nan_bits:path) => { 31 | mod $float { 32 | pub(crate) const EPSILON: $float = $float::EPSILON; 33 | pub(crate) const INFINITY: $float = $float::INFINITY; 34 | pub(crate) const MIN_NORMAL: $float = $float::MIN_POSITIVE; 35 | pub(crate) const MAX_NORMAL: $float = $float::MAX; 36 | pub(crate) const MAX_ULPS: $uint = $uint::MAX; 37 | 38 | pub(crate) fn nan_test_values() -> [$float; 4] { 39 | [ 40 | $float::from_bits($nan_bits.pos_min), 41 | $float::from_bits($nan_bits.pos_max), 42 | $float::from_bits($nan_bits.neg_min), 43 | $float::from_bits($nan_bits.neg_max), 44 | ] 45 | } 46 | 47 | // next representable float 48 | pub(crate) fn next(f: $float) -> $float { 49 | next_n(f, 1) 50 | } 51 | 52 | // previous representable float 53 | pub(crate) fn prev(f: $float) -> $float { 54 | prev_n(f, 1) 55 | } 56 | 57 | pub(crate) fn next_n(f: $float, n: $uint) -> $float { 58 | $float::from_bits(f.to_bits() + n) 59 | } 60 | 61 | pub(crate) fn prev_n(f: $float, n: $uint) -> $float { 62 | $float::from_bits(f.to_bits() - n) 63 | } 64 | } 65 | }; 66 | } 67 | 68 | impl_test_helpers!(f32, u32, crate::F32_NAN_BITS); 69 | impl_test_helpers!(f64, u64, crate::F64_NAN_BITS); 70 | 71 | macro_rules! wrapper_tests { 72 | ($t:ident) => { 73 | #[test] 74 | fn float_eq() { 75 | let a = $t::new([0.999_999_9f32, 4.0]); 76 | let b = $t::new([1.0f32, 3.999_999_5]); 77 | let eps = f32::EPSILON; 78 | 79 | assert_float_eq!(a, b, abs <= [1.0 * eps, 4.0 * eps]); 80 | assert_float_ne!(a, b, abs <= [0.5 * eps, 4.0 * eps]); 81 | assert_float_ne!(a, b, abs <= [1.0 * eps, 2.0 * eps]); 82 | 83 | assert_float_eq!(a, b, rel <= [1.0 * eps, 1.0 * eps]); 84 | assert_float_ne!(a, b, rel <= [0.5 * eps, 1.0 * eps]); 85 | assert_float_ne!(a, b, rel <= [1.0 * eps, 0.5 * eps]); 86 | 87 | assert_float_eq!(a, b, rmax <= [1.0 * eps, 1.0 * eps]); 88 | assert_float_ne!(a, b, rmax <= [0.5 * eps, 1.0 * eps]); 89 | assert_float_ne!(a, b, rmax <= [1.0 * eps, 0.5 * eps]); 90 | 91 | assert_float_eq!(a, b, rmin <= [2.0 * eps, 2.0 * eps]); 92 | assert_float_ne!(a, b, rmin <= [1.0 * eps, 2.0 * eps]); 93 | assert_float_ne!(a, b, rmin <= [2.0 * eps, 1.0 * eps]); 94 | 95 | assert_float_eq!(a, b, r1st <= [2.0 * eps, 1.0 * eps]); 96 | assert_float_ne!(a, b, r1st <= [1.0 * eps, 1.0 * eps]); 97 | assert_float_ne!(a, b, r1st <= [2.0 * eps, 0.5 * eps]); 98 | 99 | assert_float_eq!(a, b, r2nd <= [1.0 * eps, 2.0 * eps]); 100 | assert_float_ne!(a, b, r2nd <= [0.5 * eps, 2.0 * eps]); 101 | assert_float_ne!(a, b, r2nd <= [1.0 * eps, 1.0 * eps]); 102 | 103 | assert_float_eq!(a, b, ulps <= [2, 2]); 104 | assert_float_ne!(a, b, ulps <= [1, 2]); 105 | assert_float_ne!(a, b, ulps <= [2, 1]); 106 | } 107 | 108 | #[test] 109 | fn float_eq_all() { 110 | let a = $t::new([0.999_999_9f32, 4.0]); 111 | let b = $t::new([1.0f32, 3.999_999_5]); 112 | let eps = f32::EPSILON; 113 | 114 | assert_float_eq!(a, b, abs_all <= 4.0 * eps); 115 | assert_float_ne!(a, b, abs_all <= 2.0 * eps); 116 | 117 | assert_float_eq!(a, b, rel_all <= 1.0 * eps); 118 | assert_float_ne!(a, b, rel_all <= 0.5 * eps); 119 | 120 | assert_float_eq!(a, b, rmax_all <= 1.0 * eps); 121 | assert_float_ne!(a, b, rmax_all <= 0.5 * eps); 122 | 123 | assert_float_eq!(a, b, rmin_all <= 2.0 * eps); 124 | assert_float_ne!(a, b, rmin_all <= 1.0 * eps); 125 | 126 | assert_float_eq!(a, b, r1st_all <= 2.0 * eps); 127 | assert_float_ne!(a, b, r1st_all <= 1.0 * eps); 128 | 129 | assert_float_eq!(a, b, r2nd_all <= 2.0 * eps); 130 | assert_float_ne!(a, b, r2nd_all <= 1.0 * eps); 131 | 132 | assert_float_eq!(a, b, ulps_all <= 2); 133 | assert_float_ne!(a, b, ulps_all <= 1); 134 | } 135 | 136 | #[test] 137 | fn debug_diff() { 138 | let a = $t::new([1.0f32, 2.0]); 139 | let b = $t::new([1.5f32, 2.25]); 140 | let ulps = [Some(4_194_304), Some(1_048_576)]; 141 | 142 | assert_eq!(a.debug_abs_diff(&a), [0.0; 2]); 143 | assert_eq!(a.debug_ulps_diff(&a), [Some(0); 2]); 144 | 145 | assert_eq!(a.debug_abs_diff(&b), [0.5, 0.25]); 146 | assert_eq!(b.debug_abs_diff(&a), [0.5, 0.25]); 147 | 148 | assert_eq!(a.debug_ulps_diff(&b), ulps); 149 | assert_eq!(b.debug_ulps_diff(&a), ulps); 150 | } 151 | 152 | #[test] 153 | fn debug_tol() { 154 | let a = $t::new([2.0f32, 4.25]); 155 | let b = $t::new([2.5f32, 4.0]); 156 | let eps = [0.1, 0.2]; 157 | 158 | assert_eq!(a.debug_abs_tol(&b, &eps), [0.1, 0.2]); 159 | assert_eq!(a.debug_rel_tol(&b, &eps), [0.25, 0.85]); 160 | assert_eq!(a.debug_rmax_tol(&b, &eps), [0.25, 0.85]); 161 | assert_eq!(a.debug_rmin_tol(&b, &eps), [0.2, 0.8]); 162 | assert_eq!(a.debug_r1st_tol(&b, &eps), [0.2, 0.85]); 163 | assert_eq!(a.debug_r2nd_tol(&b, &eps), [0.25, 0.8]); 164 | assert_eq!(a.debug_ulps_tol(&b, &[1, 2]), [1, 2]); 165 | } 166 | 167 | #[test] 168 | fn debug_all_tol() { 169 | let a = $t::new([2.0f32, 4.25]); 170 | let b = $t::new([2.5f32, 4.0]); 171 | 172 | assert_eq!(a.debug_abs_all_tol(&b, &0.2), [0.2, 0.2]); 173 | assert_eq!(a.debug_rel_all_tol(&b, &0.2), [0.5, 0.85]); 174 | assert_eq!(a.debug_rmax_all_tol(&b, &0.2), [0.5, 0.85]); 175 | assert_eq!(a.debug_rmin_all_tol(&b, &0.2), [0.4, 0.8]); 176 | assert_eq!(a.debug_r1st_all_tol(&b, &0.2), [0.4, 0.85]); 177 | assert_eq!(a.debug_r2nd_all_tol(&b, &0.2), [0.5, 0.8]); 178 | assert_eq!(a.debug_ulps_all_tol(&b, &2), [2, 2]); 179 | } 180 | }; 181 | } 182 | 183 | mod unit_tests { 184 | mod arrays; 185 | mod core_types; 186 | mod macros; 187 | mod primitives; 188 | mod tuples; 189 | 190 | #[cfg(feature = "std")] 191 | mod std_types; 192 | 193 | #[cfg(feature = "num")] 194 | mod num_complex; 195 | } 196 | 197 | struct Foo(f32, f64); 198 | 199 | #[test] 200 | fn check() { 201 | let f = Foo { 0: 0.0, 1: 1.0 }; 202 | assert_eq!(f.0, 0.0); 203 | assert_eq!(f.1, 1.0); 204 | } 205 | -------------------------------------------------------------------------------- /float_eq/tests/unit_tests/num_complex.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | use core::f32; 4 | use float_eq::{ 5 | assert_float_eq, assert_float_ne, AssertFloatEq, AssertFloatEqAll, ComplexUlps32, DebugUlpsDiff, 6 | }; 7 | use num_complex::Complex32; 8 | 9 | #[test] 10 | fn complex_ulps() { 11 | let a = ComplexUlps32::new(1, 2); 12 | assert_eq!(a, a); 13 | let mut b = a.clone(); 14 | b.im = 3; 15 | assert_ne!(a, b); 16 | } 17 | 18 | #[test] 19 | fn float_eq() { 20 | let a = Complex32::new(0.999_999_9f32, 4.0); 21 | let b = Complex32::new(1.0f32, 3.999_999_5); 22 | let eps = f32::EPSILON; 23 | 24 | assert_float_eq!(a, b, abs <= Complex32::new(1.0 * eps, 4.0 * eps)); 25 | assert_float_ne!(a, b, abs <= Complex32::new(0.5 * eps, 4.0 * eps)); 26 | assert_float_ne!(a, b, abs <= Complex32::new(1.0 * eps, 2.0 * eps)); 27 | 28 | assert_float_eq!(a, b, rel <= Complex32::new(1.0 * eps, 1.0 * eps)); 29 | assert_float_ne!(a, b, rel <= Complex32::new(0.5 * eps, 1.0 * eps)); 30 | assert_float_ne!(a, b, rel <= Complex32::new(1.0 * eps, 0.5 * eps)); 31 | 32 | assert_float_eq!(a, b, rmax <= Complex32::new(1.0 * eps, 1.0 * eps)); 33 | assert_float_ne!(a, b, rmax <= Complex32::new(0.5 * eps, 1.0 * eps)); 34 | assert_float_ne!(a, b, rmax <= Complex32::new(1.0 * eps, 0.5 * eps)); 35 | 36 | assert_float_eq!(a, b, rmin <= Complex32::new(2.0 * eps, 2.0 * eps)); 37 | assert_float_ne!(a, b, rmin <= Complex32::new(1.0 * eps, 2.0 * eps)); 38 | assert_float_ne!(a, b, rmin <= Complex32::new(2.0 * eps, 1.0 * eps)); 39 | 40 | assert_float_eq!(a, b, r1st <= Complex32::new(2.0 * eps, 1.0 * eps)); 41 | assert_float_ne!(a, b, r1st <= Complex32::new(1.0 * eps, 1.0 * eps)); 42 | assert_float_ne!(a, b, r1st <= Complex32::new(2.0 * eps, 0.5 * eps)); 43 | 44 | assert_float_eq!(a, b, r2nd <= Complex32::new(1.0 * eps, 2.0 * eps)); 45 | assert_float_ne!(a, b, r2nd <= Complex32::new(0.5 * eps, 2.0 * eps)); 46 | assert_float_ne!(a, b, r2nd <= Complex32::new(1.0 * eps, 1.0 * eps)); 47 | 48 | assert_float_eq!(a, b, ulps <= ComplexUlps32::new(2, 2)); 49 | assert_float_ne!(a, b, ulps <= ComplexUlps32::new(1, 2)); 50 | assert_float_ne!(a, b, ulps <= ComplexUlps32::new(2, 1)); 51 | } 52 | 53 | #[test] 54 | fn float_eq_all() { 55 | let a = Complex32::new(0.999_999_9f32, 4.0); 56 | let b = Complex32::new(1.0f32, 3.999_999_5); 57 | let eps = f32::EPSILON; 58 | 59 | assert_float_eq!(a, b, abs_all <= (4.0 * eps)); 60 | assert_float_ne!(a, b, abs_all <= (2.0 * eps)); 61 | 62 | assert_float_eq!(a, b, rel_all <= (1.0 * eps)); 63 | assert_float_ne!(a, b, rel_all <= (0.5 * eps)); 64 | 65 | assert_float_eq!(a, b, rmax_all <= (1.0 * eps)); 66 | assert_float_ne!(a, b, rmax_all <= (0.5 * eps)); 67 | 68 | assert_float_eq!(a, b, rmin_all <= (2.0 * eps)); 69 | assert_float_ne!(a, b, rmin_all <= (1.0 * eps)); 70 | 71 | assert_float_eq!(a, b, r1st_all <= (2.0 * eps)); 72 | assert_float_ne!(a, b, r1st_all <= (1.0 * eps)); 73 | 74 | assert_float_eq!(a, b, r2nd_all <= (2.0 * eps)); 75 | assert_float_ne!(a, b, r2nd_all <= (1.0 * eps)); 76 | 77 | assert_float_eq!(a, b, ulps_all <= 2); 78 | assert_float_ne!(a, b, ulps_all <= 1); 79 | } 80 | 81 | #[test] 82 | fn debug_diff() { 83 | let a = Complex32::new(1.0f32, 2.0); 84 | let b = Complex32::new(1.5f32, 2.25); 85 | let ulps = DebugUlpsDiff::::new(Some(4_194_304), Some(1_048_576)); 86 | 87 | assert_eq!(a.debug_abs_diff(&a), Complex32::new(0.0, 0.0)); 88 | assert_eq!( 89 | a.debug_ulps_diff(&a), 90 | DebugUlpsDiff::::new(Some(0), Some(0)) 91 | ); 92 | 93 | assert_eq!(a.debug_abs_diff(&b), Complex32::new(0.5, 0.25)); 94 | assert_eq!(b.debug_abs_diff(&a), Complex32::new(0.5, 0.25)); 95 | 96 | assert_eq!(a.debug_ulps_diff(&b), ulps); 97 | assert_eq!(b.debug_ulps_diff(&a), ulps); 98 | } 99 | 100 | #[test] 101 | fn debug_tol() { 102 | let a = Complex32::new(2.0f32, 4.25); 103 | let b = Complex32::new(2.5f32, 4.0); 104 | let eps = Complex32::new(0.1, 0.2); 105 | 106 | assert_eq!(a.debug_abs_tol(&b, &eps), Complex32::new(0.1, 0.2)); 107 | assert_eq!(a.debug_rel_tol(&b, &eps), Complex32::new(0.25, 0.85)); 108 | assert_eq!(a.debug_rmax_tol(&b, &eps), Complex32::new(0.25, 0.85)); 109 | assert_eq!(a.debug_rmin_tol(&b, &eps), Complex32::new(0.2, 0.8)); 110 | assert_eq!(a.debug_r1st_tol(&b, &eps), Complex32::new(0.2, 0.85)); 111 | assert_eq!(a.debug_r2nd_tol(&b, &eps), Complex32::new(0.25, 0.8)); 112 | assert_eq!( 113 | a.debug_ulps_tol(&b, &ComplexUlps32::new(1, 2)), 114 | ComplexUlps32::new(1, 2) 115 | ); 116 | } 117 | 118 | #[test] 119 | fn debug_all_tol() { 120 | let a = Complex32::new(2.0f32, 4.25); 121 | let b = Complex32::new(2.5f32, 4.0); 122 | 123 | assert_eq!(a.debug_abs_all_tol(&b, &0.2), Complex32::new(0.2, 0.2)); 124 | assert_eq!(a.debug_rel_all_tol(&b, &0.2), Complex32::new(0.5, 0.85)); 125 | assert_eq!(a.debug_rmax_all_tol(&b, &0.2), Complex32::new(0.5, 0.85)); 126 | assert_eq!(a.debug_rmin_all_tol(&b, &0.2), Complex32::new(0.4, 0.8)); 127 | assert_eq!(a.debug_r1st_all_tol(&b, &0.2), Complex32::new(0.4, 0.85)); 128 | assert_eq!(a.debug_r2nd_all_tol(&b, &0.2), Complex32::new(0.5, 0.8)); 129 | assert_eq!(a.debug_ulps_all_tol(&b, &2), ComplexUlps32::new(2, 2)); 130 | } 131 | 132 | #[test] 133 | #[should_panic(expected = r#"`float_eq!(left, right, abs <= t, rel <= t, ulps <= t)` 134 | left: `Complex { re: 1.0, im: 2.0 }`, 135 | right: `Complex { re: 3.0, im: -5.0 }`, 136 | abs_diff: `Complex { re: 2.0, im: 7.0 }`, 137 | ulps_diff: `ComplexUlps { re: Some(12582912), im: None }`, 138 | [abs] t: `Complex { re: 0.1, im: 0.25 }`, 139 | [rel] t: `Complex { re: 0.3, im: 1.25 }`, 140 | [ulps] t: `ComplexUlps { re: 1, im: 2 }`"#)] 141 | fn assert_fail_message() { 142 | assert_float_eq!( 143 | Complex32::new(1., 2.), 144 | Complex32::new(3., -5.), 145 | abs <= Complex32::new(0.1, 0.25), 146 | rel <= Complex32::new(0.1, 0.25), 147 | ulps <= ComplexUlps32::new(1, 2) 148 | ); 149 | } 150 | 151 | #[test] 152 | #[should_panic( 153 | expected = r#"`float_eq!(left, right, abs_all <= t, rel_all <= t, ulps_all <= t)` 154 | left: `Complex { re: 1.0, im: 2.0 }`, 155 | right: `Complex { re: 3.0, im: -5.0 }`, 156 | abs_diff: `Complex { re: 2.0, im: 7.0 }`, 157 | ulps_diff: `ComplexUlps { re: Some(12582912), im: None }`, 158 | [abs_all] t: `Complex { re: 0.25, im: 0.25 }`, 159 | [rel_all] t: `Complex { re: 0.75, im: 1.25 }`, 160 | [ulps_all] t: `ComplexUlps { re: 3, im: 3 }`"# 161 | )] 162 | fn assert_fail_all_message() { 163 | assert_float_eq!( 164 | Complex32::new(1., 2.), 165 | Complex32::new(3., -5.), 166 | abs_all <= 0.25, 167 | rel_all <= 0.25, 168 | ulps_all <= 3 169 | ); 170 | } 171 | -------------------------------------------------------------------------------- /float_eq/tests/unit_tests/primitives.rs: -------------------------------------------------------------------------------- 1 | mod assert_float_eq; 2 | mod eq_abs; 3 | mod eq_rmax; 4 | mod eq_rmin; 5 | mod eq_ulps; 6 | -------------------------------------------------------------------------------- /float_eq/tests/unit_tests/primitives/assert_float_eq.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_tests { 2 | ($float:ident, $uint:ident) => { 3 | mod $float { 4 | use crate::$float::*; 5 | use float_eq::{AssertFloatEq, AssertFloatEqAll}; 6 | 7 | #[test] 8 | fn debug_abs_diff() { 9 | let check = 10 | |a: $float, b, expected| assert!(a.debug_abs_diff(&b) - expected <= EPSILON); 11 | 12 | // zeroes 13 | check(0.0, 0.0, 0.0); 14 | check(0.0, -0.0, 0.0); 15 | check(-0.0, 0.0, 0.0); 16 | check(-0.0, -0.0, 0.0); 17 | 18 | // self 19 | check(1.0, 1.0, 0.0); 20 | check(-1.0, -1.0, 0.0); 21 | 22 | // finite numbers 23 | check(1.0, 2.0, 1.0); 24 | check(1.0, -2.0, 3.0); 25 | check(-1.0, 2.0, 3.0); 26 | check(-1.0, -2.0, 1.0); 27 | 28 | // infinities 29 | assert!(INFINITY.debug_abs_diff(&INFINITY).is_nan()); 30 | assert_eq!(INFINITY.debug_abs_diff(&(-INFINITY)), INFINITY); 31 | assert_eq!((-INFINITY).debug_abs_diff(&(INFINITY)), INFINITY); 32 | assert!((-INFINITY).debug_abs_diff(&(-INFINITY)).is_nan()); 33 | 34 | // nans 35 | let nans = nan_test_values(); 36 | for a in &nans { 37 | assert!(a.debug_abs_diff(&1.0).is_nan()); 38 | assert!(1.0.debug_abs_diff(a).is_nan()); 39 | for b in &nans { 40 | assert!(a.debug_abs_diff(b).is_nan()); 41 | } 42 | } 43 | } 44 | 45 | #[test] 46 | fn debug_ulps_diff() { 47 | let check = |a: $float, b, expected| assert_eq!(a.debug_ulps_diff(&b), expected); 48 | 49 | // zeroes 50 | check(0.0, 0.0, Some(0)); 51 | check(0.0, -0.0, Some(0)); 52 | check(-0.0, 0.0, Some(0)); 53 | check(-0.0, -0.0, Some(0)); 54 | 55 | // self 56 | check(1.0, 1.0, Some(0)); 57 | check(-1.0, -1.0, Some(0)); 58 | 59 | // denormals 60 | check(next(0.0), next_n(0.0, 10), Some(9)); 61 | check(next(-0.0), next_n(-0.0, 10), Some(9)); 62 | 63 | check(next(0.0), next(-0.0), None); 64 | check(next(-0.0), next(0.0), None); 65 | 66 | // normals 67 | check(1.0, next_n(1.0, 10), Some(10)); 68 | check(-1.0, next_n(-1.0, 10), Some(10)); 69 | 70 | check(1.0, -1.0, None); 71 | check(-1.0, 1.0, None); 72 | 73 | // infinities 74 | check(INFINITY, INFINITY, Some(0)); 75 | check(INFINITY, -INFINITY, None); 76 | check(-INFINITY, INFINITY, None); 77 | check(-INFINITY, -INFINITY, Some(0)); 78 | 79 | // nans 80 | let nans = nan_test_values(); 81 | for a in &nans { 82 | assert!(a.debug_ulps_diff(&1.0).is_none()); 83 | assert!(1.0.debug_ulps_diff(a).is_none()); 84 | for b in &nans { 85 | assert!(a.debug_ulps_diff(b).is_none()); 86 | } 87 | } 88 | } 89 | 90 | #[test] 91 | fn debug_tol() { 92 | let a: $float = 10.0; 93 | let b: $float = 25.0; 94 | 95 | assert_eq!(a.debug_abs_tol(&b, &3.0), 3.0); 96 | assert_eq!(b.debug_abs_tol(&a, &3.0), 3.0); 97 | 98 | assert_eq!(a.debug_rel_tol(&b, &0.5), 12.5); 99 | assert_eq!(b.debug_rel_tol(&a, &0.5), 12.5); 100 | 101 | assert_eq!(a.debug_rmax_tol(&b, &0.5), 12.5); 102 | assert_eq!(b.debug_rmax_tol(&a, &0.5), 12.5); 103 | 104 | assert_eq!(a.debug_rmin_tol(&b, &0.5), 5.0); 105 | assert_eq!(b.debug_rmin_tol(&a, &0.5), 5.0); 106 | 107 | assert_eq!(a.debug_r1st_tol(&b, &0.5), 5.0); 108 | assert_eq!(b.debug_r1st_tol(&a, &0.5), 12.5); 109 | 110 | assert_eq!(a.debug_r2nd_tol(&b, &0.5), 12.5); 111 | assert_eq!(b.debug_r2nd_tol(&a, &0.5), 5.0); 112 | 113 | assert_eq!(a.debug_ulps_tol(&b, &5), 5); 114 | assert_eq!(b.debug_ulps_tol(&a, &5), 5); 115 | } 116 | 117 | #[test] 118 | fn debug_all_tol() { 119 | let a: $float = 10.0; 120 | let b: $float = 25.0; 121 | 122 | assert_eq!(a.debug_abs_all_tol(&b, &3.0), 3.0); 123 | assert_eq!(b.debug_abs_all_tol(&a, &3.0), 3.0); 124 | 125 | assert_eq!(a.debug_rel_all_tol(&b, &0.5), 12.5); 126 | assert_eq!(b.debug_rel_all_tol(&a, &0.5), 12.5); 127 | 128 | assert_eq!(a.debug_rmax_all_tol(&b, &0.5), 12.5); 129 | assert_eq!(b.debug_rmax_all_tol(&a, &0.5), 12.5); 130 | 131 | assert_eq!(a.debug_rmin_all_tol(&b, &0.5), 5.0); 132 | assert_eq!(b.debug_rmin_all_tol(&a, &0.5), 5.0); 133 | 134 | assert_eq!(a.debug_r1st_all_tol(&b, &0.5), 5.0); 135 | assert_eq!(b.debug_r1st_all_tol(&a, &0.5), 12.5); 136 | 137 | assert_eq!(a.debug_r2nd_all_tol(&b, &0.5), 12.5); 138 | assert_eq!(b.debug_r2nd_all_tol(&a, &0.5), 5.0); 139 | 140 | assert_eq!(a.debug_ulps_all_tol(&b, &5), 5); 141 | assert_eq!(b.debug_ulps_all_tol(&a, &5), 5); 142 | } 143 | } 144 | }; 145 | } 146 | 147 | impl_tests!(f32, u32); 148 | impl_tests!(f64, u64); 149 | -------------------------------------------------------------------------------- /float_eq/tests/unit_tests/primitives/eq_abs.rs: -------------------------------------------------------------------------------- 1 | /// Systematic tests of eq_abs/eq_abs_all behaviour over primitives. 2 | 3 | macro_rules! impl_tests { 4 | ($float:ident) => { 5 | mod $float { 6 | use crate::$float::*; 7 | use float_eq::{ 8 | assert_float_eq, assert_float_ne, float_eq, float_ne, FloatEq, FloatEqAll, 9 | }; 10 | 11 | fn check_eq_abs(a: $float, b: $float, tol: $float) { 12 | assert_float_eq!(a, b, abs <= tol); 13 | assert_float_eq!(a, b, abs_all <= tol); 14 | 15 | assert!(float_eq!(a, b, abs <= tol)); 16 | assert!(float_eq!(a, b, abs_all <= tol)); 17 | assert!(!float_ne!(a, b, abs <= tol)); 18 | assert!(!float_ne!(a, b, abs_all <= tol)); 19 | 20 | assert!(a.eq_abs(&b, &tol)); 21 | assert!(a.eq_abs_all(&b, &tol)); 22 | assert!(!a.ne_abs(&b, &tol)); 23 | assert!(!a.ne_abs_all(&b, &tol)); 24 | } 25 | 26 | fn check_ne_abs(a: $float, b: $float, tol: $float) { 27 | assert_float_ne!(a, b, abs <= tol); 28 | assert_float_ne!(a, b, abs_all <= tol); 29 | 30 | assert!(!float_eq!(a, b, abs <= tol)); 31 | assert!(!float_eq!(a, b, abs_all <= tol)); 32 | assert!(float_ne!(a, b, abs <= tol)); 33 | assert!(float_ne!(a, b, abs_all <= tol)); 34 | 35 | assert!(!a.eq_abs(&b, &tol)); 36 | assert!(!a.eq_abs_all(&b, &tol)); 37 | assert!(a.ne_abs(&b, &tol)); 38 | assert!(a.ne_abs_all(&b, &tol)); 39 | } 40 | 41 | // also covers commutativity and negative values 42 | fn check_eq(a: $float, b: $float, tol: $float) { 43 | check_eq_abs(a, b, tol); 44 | check_eq_abs(b, a, tol); 45 | check_eq_abs(-a, -b, tol); 46 | check_eq_abs(-b, -a, tol); 47 | } 48 | 49 | // also covers commutativity and negative values 50 | fn check_ne(a: $float, b: $float, tol: $float) { 51 | check_ne_abs(a, b, tol); 52 | check_ne_abs(b, a, tol); 53 | check_ne_abs(-a, -b, tol); 54 | check_ne_abs(-b, -a, tol); 55 | } 56 | 57 | // also covers tests over -f 58 | fn check_eq_self(f: $float) { 59 | check_eq(f, f, 0.0); 60 | check_eq(f, f, next(0.0)); 61 | check_eq(f, f, prev(MIN_NORMAL)); 62 | check_eq(f, f, MIN_NORMAL); 63 | check_eq(f, f, MAX_NORMAL); 64 | check_eq(f, f, INFINITY); 65 | } 66 | 67 | // also covers tests over -f 68 | fn check_eq_zero(f: $float) { 69 | check_ne(f, 0.0, prev(f)); 70 | check_eq(f, 0.0, f); 71 | } 72 | 73 | #[test] 74 | fn zero() { 75 | check_eq_self(0.0); 76 | 77 | // negative zero is a different representation 78 | check_eq(0.0, -0.0, next(0.0)); 79 | check_eq(0.0, -0.0, prev(MIN_NORMAL)); 80 | check_eq(0.0, -0.0, MIN_NORMAL); 81 | check_eq(0.0, -0.0, MAX_NORMAL); 82 | check_eq(0.0, -0.0, INFINITY); 83 | } 84 | 85 | #[test] 86 | fn subnormals() { 87 | let min_subnormal: $float = next(0.0); 88 | let max_subnormal: $float = prev(MIN_NORMAL); 89 | 90 | check_eq_self(min_subnormal); 91 | check_eq_self(max_subnormal); 92 | 93 | check_eq_zero(min_subnormal); 94 | check_eq_zero(max_subnormal); 95 | 96 | check_ne(max_subnormal, MIN_NORMAL, 0.0); 97 | check_eq(max_subnormal, MIN_NORMAL, min_subnormal); // due to linear spacing 98 | 99 | // ranges of -f to f 100 | check_ne(min_subnormal, -min_subnormal, prev(2.0 * min_subnormal)); 101 | check_eq(min_subnormal, -min_subnormal, 2.0 * min_subnormal); 102 | check_ne(max_subnormal, -max_subnormal, prev(2.0 * max_subnormal)); 103 | check_eq(max_subnormal, -max_subnormal, 2.0 * max_subnormal); 104 | } 105 | 106 | #[test] 107 | fn normals() { 108 | check_eq_self(MIN_NORMAL); 109 | check_eq_self(MAX_NORMAL); 110 | 111 | check_eq_zero(MIN_NORMAL); 112 | check_eq_zero(MAX_NORMAL); 113 | 114 | // below MIN_NORMAL is in subnormal tests 115 | check_ne(MIN_NORMAL, next(MIN_NORMAL), 0.0); 116 | check_eq(MIN_NORMAL, next(MIN_NORMAL), next(0.0)); 117 | 118 | check_ne(MIN_NORMAL, next_n(MIN_NORMAL, 2), next(0.0)); 119 | check_eq(MIN_NORMAL, next_n(MIN_NORMAL, 2), next_n(0.0, 2)); 120 | 121 | check_ne(MIN_NORMAL, MAX_NORMAL, prev(MAX_NORMAL)); // due to of loss of precision 122 | check_eq(MIN_NORMAL, MAX_NORMAL, MAX_NORMAL); 123 | // above MAX_NORMAL is in infinities tests 124 | 125 | // ranges of -f to f 126 | check_ne(MIN_NORMAL, -MIN_NORMAL, prev(2.0 * MIN_NORMAL)); 127 | check_eq(MIN_NORMAL, -MIN_NORMAL, 2.0 * MIN_NORMAL); 128 | check_ne(MAX_NORMAL, -MAX_NORMAL, MAX_NORMAL); 129 | check_eq(MAX_NORMAL, -MAX_NORMAL, INFINITY); 130 | } 131 | 132 | #[test] 133 | fn one() { 134 | check_eq_zero(1.0); 135 | 136 | // range of -2 to +2 ULPs 137 | check_eq(1.0, prev_n(1.0, 2), 1.0 * EPSILON); 138 | check_ne(1.0, prev_n(1.0, 2), 0.5 * EPSILON); 139 | 140 | check_eq(1.0, prev(1.0), 0.5 * EPSILON); 141 | check_ne(1.0, prev(1.0), 0.25 * EPSILON); 142 | 143 | check_eq_self(1.0); 144 | 145 | check_ne(1.0, next(1.0), 0.5 * EPSILON); 146 | check_eq(1.0, next(1.0), 1.0 * EPSILON); 147 | 148 | check_ne(1.0, next_n(1.0, 2), 1.0 * EPSILON); 149 | check_eq(1.0, next_n(1.0, 2), 2.0 * EPSILON); 150 | 151 | // ranges of -f to f 152 | check_ne(1.0, -1.0, prev(2.0)); 153 | check_eq(1.0, -1.0, 2.0); 154 | } 155 | 156 | #[test] 157 | fn two() { 158 | check_eq_zero(2.0); 159 | 160 | // range of -2 to +2 ULPs 161 | check_eq(2.0, prev_n(2.0, 2), 2.0 * EPSILON); 162 | check_ne(2.0, prev_n(2.0, 2), 1.0 * EPSILON); 163 | 164 | check_eq(2.0, prev(2.0), 1.0 * EPSILON); 165 | check_ne(2.0, prev(2.0), 0.5 * EPSILON); 166 | 167 | check_eq_self(2.0); 168 | 169 | check_ne(2.0, next(2.0), 1.0 * EPSILON); 170 | check_eq(2.0, next(2.0), 2.0 * EPSILON); 171 | 172 | check_ne(2.0, next_n(2.0, 2), 2.0 * EPSILON); 173 | check_eq(2.0, next_n(2.0, 2), 4.0 * EPSILON); 174 | 175 | // ranges of -f to f 176 | check_ne(2.0, -2.0, prev(4.0)); 177 | check_eq(2.0, -2.0, 4.0); 178 | } 179 | #[test] 180 | fn infinities() { 181 | check_eq_self(INFINITY); 182 | check_eq_zero(INFINITY); 183 | 184 | check_ne(INFINITY, MAX_NORMAL, MAX_NORMAL); 185 | check_eq(INFINITY, MAX_NORMAL, INFINITY); 186 | 187 | // ranges of -f to f 188 | check_ne(INFINITY, -INFINITY, MAX_NORMAL); 189 | check_eq(INFINITY, -INFINITY, INFINITY); 190 | } 191 | 192 | #[test] 193 | fn nans() { 194 | let nans = nan_test_values(); 195 | for &a in &nans { 196 | check_ne_abs(a, a, 0.0); 197 | 198 | check_ne_abs(1.0, a, 1.0); 199 | check_ne_abs(a, 1.0, 1.0); 200 | 201 | for &b in &nans { 202 | check_ne_abs(a, b, EPSILON); 203 | } 204 | } 205 | } 206 | } 207 | }; 208 | } 209 | 210 | impl_tests!(f32); 211 | impl_tests!(f64); 212 | -------------------------------------------------------------------------------- /float_eq/tests/unit_tests/primitives/eq_ulps.rs: -------------------------------------------------------------------------------- 1 | /// Systematic tests of eq_ulps/eq_ulps_all behaviour over primitives. 2 | 3 | macro_rules! impl_tests { 4 | ($float:ident, $uint:ident) => { 5 | mod $float { 6 | use crate::$float::*; 7 | use float_eq::{ 8 | assert_float_eq, assert_float_ne, float_eq, float_ne, FloatEq, FloatEqAll, 9 | }; 10 | 11 | fn check_eq_ulps(a: $float, b: $float, tol: $uint) { 12 | assert_float_eq!(a, b, ulps <= tol); 13 | assert_float_eq!(a, b, ulps_all <= tol); 14 | 15 | assert!(float_eq!(a, b, ulps <= tol)); 16 | assert!(float_eq!(a, b, ulps_all <= tol)); 17 | assert!(!float_ne!(a, b, ulps <= tol)); 18 | assert!(!float_ne!(a, b, ulps_all <= tol)); 19 | 20 | assert!(a.eq_ulps(&b, &tol)); 21 | assert!(a.eq_ulps_all(&b, &tol)); 22 | assert!(!a.ne_ulps(&b, &tol)); 23 | assert!(!a.ne_ulps_all(&b, &tol)); 24 | } 25 | 26 | fn check_ne_ulps(a: $float, b: $float, tol: $uint) { 27 | assert_float_ne!(a, b, ulps <= tol); 28 | assert_float_ne!(a, b, ulps_all <= tol); 29 | 30 | assert!(!float_eq!(a, b, ulps <= tol)); 31 | assert!(!float_eq!(a, b, ulps_all <= tol)); 32 | assert!(float_ne!(a, b, ulps <= tol)); 33 | assert!(float_ne!(a, b, ulps_all <= tol)); 34 | 35 | assert!(!a.eq_ulps(&b, &tol)); 36 | assert!(!a.eq_ulps_all(&b, &tol)); 37 | assert!(a.ne_ulps(&b, &tol)); 38 | assert!(a.ne_ulps_all(&b, &tol)); 39 | } 40 | 41 | // also covers commutativity and negative values 42 | fn check_eq(a: $float, b: $float, tol: $uint) { 43 | check_eq_ulps(a, b, tol); 44 | check_eq_ulps(b, a, tol); 45 | check_eq_ulps(-a, -b, tol); 46 | check_eq_ulps(-b, -a, tol); 47 | } 48 | 49 | // also covers commutativity and negative values 50 | fn check_ne(a: $float, b: $float, tol: $uint) { 51 | check_ne_ulps(a, b, tol); 52 | check_ne_ulps(b, a, tol); 53 | check_ne_ulps(-a, -b, tol); 54 | check_ne_ulps(-b, -a, tol); 55 | } 56 | 57 | // also covers tests over -f 58 | fn check_eq_self(f: $float) { 59 | check_eq(f, f, 0); 60 | check_eq(f, f, 1); 61 | check_eq(f, f, MAX_ULPS); 62 | } 63 | 64 | // also covers tests over -f 65 | fn check_eq_zero(f: $float) { 66 | check_ne(f, 0.0, prev(f).abs().to_bits()); 67 | check_eq(f, 0.0, f.abs().to_bits()); 68 | } 69 | 70 | #[test] 71 | fn zero() { 72 | check_eq_self(0.0); 73 | 74 | // negative zero is a different representation 75 | check_eq(0.0, -0.0, 0); 76 | check_eq(0.0, -0.0, 1); 77 | check_eq(0.0, -0.0, MAX_ULPS); 78 | } 79 | 80 | #[test] 81 | fn subnormals() { 82 | let min_subnormal: $float = next(0.0); 83 | let max_subnormal: $float = prev(MIN_NORMAL); 84 | 85 | check_eq_self(min_subnormal); 86 | check_eq_self(max_subnormal); 87 | 88 | check_eq_zero(min_subnormal); 89 | check_eq_zero(max_subnormal); 90 | 91 | check_ne(max_subnormal, MIN_NORMAL, 0); 92 | check_eq(max_subnormal, MIN_NORMAL, 1); 93 | 94 | // ranges of -f to f 95 | check_ne(min_subnormal, -min_subnormal, MAX_ULPS); // numbers with different signs aren't ever equal 96 | check_ne(max_subnormal, -max_subnormal, MAX_ULPS); 97 | } 98 | 99 | #[test] 100 | fn normals() { 101 | check_eq_self(MIN_NORMAL); 102 | check_eq_self(MAX_NORMAL); 103 | 104 | check_eq_zero(MIN_NORMAL); 105 | check_eq_zero(MAX_NORMAL); 106 | 107 | // below MIN_NORMAL is in subnormal tests 108 | check_ne(MIN_NORMAL, next(MIN_NORMAL), 0); 109 | check_eq(MIN_NORMAL, next(MIN_NORMAL), 1); 110 | 111 | check_ne(MIN_NORMAL, next_n(MIN_NORMAL, 2), 1); 112 | check_eq(MIN_NORMAL, next_n(MIN_NORMAL, 2), 2); 113 | 114 | check_ne( 115 | MIN_NORMAL, 116 | MAX_NORMAL, 117 | MAX_NORMAL.to_bits() - MIN_NORMAL.to_bits() - 1, 118 | ); 119 | check_eq( 120 | MIN_NORMAL, 121 | MAX_NORMAL, 122 | MAX_NORMAL.to_bits() - MIN_NORMAL.to_bits(), 123 | ); 124 | // above MAX_NORMAL is in infinities tests 125 | 126 | // ranges of -f to f 127 | check_ne(MIN_NORMAL, -MIN_NORMAL, MAX_ULPS); // numbers with different signs aren't ever equal 128 | check_ne(MAX_NORMAL, -MAX_NORMAL, MAX_ULPS); 129 | } 130 | 131 | #[test] 132 | fn one() { 133 | check_eq_self(1.0); 134 | check_eq_zero(1.0); 135 | 136 | check_ne(1.0, prev_n(1.0, 2), 1); 137 | check_eq(1.0, prev_n(1.0, 2), 2); 138 | 139 | check_ne(1.0, prev(1.0), 0); 140 | check_eq(1.0, prev(1.0), 1); 141 | 142 | check_ne(1.0, next(1.0), 0); 143 | check_eq(1.0, next(1.0), 1); 144 | 145 | check_ne(1.0, next_n(1.0, 2), 1); 146 | check_eq(1.0, next_n(1.0, 2), 2); 147 | 148 | // ranges of -f to f 149 | check_ne(1.0, -1.0, MAX_ULPS); // numbers with different signs aren't ever equal 150 | } 151 | 152 | #[test] 153 | fn two() { 154 | check_eq_self(2.0); 155 | check_eq_zero(2.0); 156 | 157 | check_ne(2.0, prev_n(2.0, 2), 1); 158 | check_eq(2.0, prev_n(2.0, 2), 2); 159 | 160 | check_ne(2.0, prev(2.0), 0); 161 | check_eq(2.0, prev(2.0), 1); 162 | 163 | check_ne(2.0, next(2.0), 0); 164 | check_eq(2.0, next(2.0), 1); 165 | 166 | check_ne(2.0, next_n(2.0, 2), 1); 167 | check_eq(2.0, next_n(2.0, 2), 2); 168 | 169 | // ranges of -f to f 170 | check_ne(2.0, -2.0, MAX_ULPS); // numbers with different signs aren't ever equal 171 | } 172 | 173 | #[test] 174 | fn infinities() { 175 | check_eq_self(INFINITY); 176 | check_eq_zero(INFINITY); 177 | 178 | check_ne(INFINITY, MAX_NORMAL, 0); 179 | check_eq(INFINITY, MAX_NORMAL, 1); 180 | 181 | // ranges of -f to f 182 | check_ne(INFINITY, -INFINITY, MAX_ULPS); // numbers with different signs aren't ever equal 183 | } 184 | 185 | #[test] 186 | fn nans() { 187 | let nans = nan_test_values(); 188 | for &a in &nans { 189 | check_ne_ulps(a, a, MAX_ULPS); 190 | 191 | check_ne_ulps(1.0, a, MAX_ULPS); 192 | check_ne_ulps(a, 1.0, MAX_ULPS); 193 | 194 | for &b in &nans { 195 | check_ne_ulps(a, b, MAX_ULPS); 196 | } 197 | } 198 | } 199 | } 200 | }; 201 | } 202 | 203 | impl_tests!(f32, u32); 204 | impl_tests!(f64, u64); 205 | -------------------------------------------------------------------------------- /float_eq_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "float_eq_derive" 3 | version = "1.0.2-pre" 4 | authors = ["jtempest"] 5 | license = "MIT OR Apache-2.0" 6 | description = "Derive macro support for float_eq." 7 | homepage = "https://jtempest.github.io/float_eq-rs/" 8 | repository = "https://github.com/jtempest/float_eq-rs" 9 | documentation = "https://jtempest.github.io/float_eq-rs/book/" 10 | keywords = ["approximate", "assert", "comparison", "equality", "float"] 11 | categories = ["algorithms", "development-tools::debugging", "no-std"] 12 | readme = "crates-io.md" 13 | include = ["Cargo.toml", "src/**/*.rs", "crates-io.md", "LICENSE-APACHE", "LICENSE-MIT"] 14 | edition = "2018" 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [dependencies] 20 | proc-macro2 = "1" 21 | quote = "1" 22 | syn = "1" -------------------------------------------------------------------------------- /float_eq_derive/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 jtempest 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /float_eq_derive/crates-io.md: -------------------------------------------------------------------------------- 1 | # float_eq_derive 2 | 3 | Derive macros for the traits provided by the [float_eq] crate. 4 | 5 | [float_eq]: https://crates.io/crates/float_eq -------------------------------------------------------------------------------- /float_eq_derive/src/read.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span, TokenStream}; 2 | use quote::ToTokens; 3 | use syn::{ 4 | spanned::Spanned, Attribute, Data, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Lit, 5 | LitInt, LitStr, Meta, NestedMeta, Type, Visibility, 6 | }; 7 | 8 | pub enum FieldName<'a> { 9 | Ident(&'a Ident), 10 | Num(Lit), 11 | } 12 | 13 | impl ToTokens for FieldName<'_> { 14 | fn to_tokens(&self, tokens: &mut TokenStream) { 15 | match self { 16 | FieldName::Ident(ident) => ident.to_tokens(tokens), 17 | FieldName::Num(num) => num.to_tokens(tokens), 18 | } 19 | } 20 | } 21 | 22 | pub struct FieldInfo<'a> { 23 | pub name: FieldName<'a>, 24 | pub ty: &'a Type, 25 | pub vis: &'a Visibility, 26 | } 27 | 28 | pub enum FieldListType { 29 | Named, 30 | Tuple, 31 | Unit, 32 | } 33 | 34 | pub struct FieldInfoList<'a> { 35 | pub ty: FieldListType, 36 | fields: Vec>, 37 | } 38 | 39 | impl FieldInfoList<'_> { 40 | pub fn expand TokenStream>(&self, func: F) -> Vec { 41 | self.fields.iter().map(func).collect() 42 | } 43 | } 44 | 45 | pub fn all_fields_info<'a>( 46 | trait_name: &str, 47 | input: &'a DeriveInput, 48 | ) -> Result, syn::Error> { 49 | if !input.generics.params.is_empty() { 50 | return Err(syn::Error::new( 51 | Span::call_site(), 52 | "This trait does not yet support derive for generic types.", 53 | )); 54 | } 55 | 56 | match &input.data { 57 | Data::Struct(data) => match &data.fields { 58 | Fields::Named(FieldsNamed { named, .. }) => Ok(FieldInfoList { 59 | ty: FieldListType::Named, 60 | fields: named.iter().map(named_field_info).collect(), 61 | }), 62 | Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => Ok(FieldInfoList { 63 | ty: FieldListType::Tuple, 64 | fields: unnamed.iter().enumerate().map(unnamed_field_info).collect(), 65 | }), 66 | Fields::Unit => Ok(FieldInfoList { 67 | ty: FieldListType::Unit, 68 | fields: Vec::new(), 69 | }), 70 | }, 71 | _ => Err(syn::Error::new( 72 | input.ident.span(), 73 | format!("{} may only be derived for structs.", trait_name), 74 | )), 75 | } 76 | } 77 | 78 | fn named_field_info(field: &syn::Field) -> FieldInfo { 79 | FieldInfo { 80 | name: FieldName::Ident(field.ident.as_ref().expect("Expected named field")), 81 | ty: &field.ty, 82 | vis: &field.vis, 83 | } 84 | } 85 | 86 | fn unnamed_field_info((n, field): (usize, &syn::Field)) -> FieldInfo { 87 | FieldInfo { 88 | name: FieldName::Num(Lit::Int(LitInt::new(&format!("{}", n), Span::call_site()))), 89 | ty: &field.ty, 90 | vis: &field.vis, 91 | } 92 | } 93 | 94 | #[derive(Default)] 95 | pub struct FloatEqAttr { 96 | struct_name: String, 97 | ulps_tol_type_name: Option, 98 | ulps_tol_derive_types: Option>, 99 | debug_ulps_diff_type_name: Option, 100 | debug_ulps_diff_derive_types: Option>, 101 | all_tol_type_name: Option, 102 | } 103 | 104 | impl FloatEqAttr { 105 | pub fn ulps_tol_type(&self) -> Result<&Ident, syn::Error> { 106 | self.ulps_tol_type_name.as_ref().ok_or({ 107 | let msg = format!( 108 | r#"Missing ULPs tolerance type name required to derive trait. 109 | 110 | help: try adding `#[float_eq(ulps_tol = "{}Ulps")]` to your type."#, 111 | self.struct_name 112 | ); 113 | syn::Error::new(Span::call_site(), msg) 114 | }) 115 | } 116 | 117 | pub fn ulps_tol_derive_types(&self) -> Vec { 118 | self.ulps_tol_derive_types 119 | .as_ref() 120 | .map_or_else(Vec::new, |v| v.clone()) 121 | } 122 | 123 | pub fn debug_ulps_diff_derive_types(&self) -> Vec { 124 | self.debug_ulps_diff_derive_types 125 | .as_ref() 126 | .map_or_else(Vec::new, |v| v.clone()) 127 | } 128 | 129 | pub fn debug_ulps_diff(&self) -> Result<&Ident, syn::Error> { 130 | self.debug_ulps_diff_type_name.as_ref().ok_or({ 131 | let msg = format!( 132 | r#"Missing debug ULPs diff type name required to derive trait. 133 | 134 | help: try adding `#[float_eq(debug_ulps_diff = "{}DebugUlpsDiff")]` to your type."#, 135 | self.struct_name 136 | ); 137 | syn::Error::new(Span::call_site(), msg) 138 | }) 139 | } 140 | 141 | pub fn all_tol_type(&self) -> Result<&Ident, syn::Error> { 142 | self.all_tol_type_name.as_ref().ok_or({ 143 | let msg = r#"Missing Tol type name required to derive trait. 144 | 145 | help: try adding `#[float_eq(all_tol = "T")]` to your type, where T is commonly `f32` or `f64`."#; 146 | syn::Error::new(Span::call_site(), msg) 147 | }) 148 | } 149 | } 150 | 151 | pub fn float_eq_attr(input: &DeriveInput) -> Result { 152 | let nv_pair_lists: Vec> = input 153 | .attrs 154 | .iter() 155 | .filter(|a| a.path.is_ident("float_eq")) 156 | .map(|a| name_value_pair_list(&input.ident, a)) 157 | .collect::>()?; 158 | 159 | let mut attr_values = FloatEqAttr { 160 | struct_name: input.ident.to_string(), 161 | ..Default::default() 162 | }; 163 | 164 | for nv in nv_pair_lists.into_iter().flatten() { 165 | let name = nv.name.to_string(); 166 | if name == "ulps_tol" { 167 | set_float_eq_attr(&mut attr_values.ulps_tol_type_name, &nv, &parse_ident)?; 168 | } else if name == "debug_ulps_diff" { 169 | set_float_eq_attr( 170 | &mut attr_values.debug_ulps_diff_type_name, 171 | &nv, 172 | &parse_ident, 173 | )?; 174 | } else if name == "all_tol" { 175 | set_float_eq_attr(&mut attr_values.all_tol_type_name, &nv, &parse_ident)?; 176 | } else if name == "ulps_tol_derive" { 177 | set_float_eq_attr( 178 | &mut attr_values.ulps_tol_derive_types, 179 | &nv, 180 | &parse_ident_list, 181 | )?; 182 | } else if name == "debug_ulps_diff_derive" { 183 | set_float_eq_attr( 184 | &mut attr_values.debug_ulps_diff_derive_types, 185 | &nv, 186 | &parse_ident_list, 187 | )?; 188 | } else { 189 | let msg = format!(r"'{}' is not a valid float_eq derive option.", name); 190 | return Err(syn::Error::new(nv.name.span(), msg)); 191 | } 192 | } 193 | 194 | Ok(attr_values) 195 | } 196 | 197 | fn set_float_eq_attr( 198 | attr_value: &mut Option, 199 | name_value_pair: &NameValuePair, 200 | parse_fn: &dyn Fn(&LitStr) -> Result, 201 | ) -> Result<(), syn::Error> { 202 | if attr_value.is_none() { 203 | if let Ok(value) = parse_fn(&name_value_pair.value) { 204 | *attr_value = Some(value); 205 | Ok(()) 206 | } else { 207 | let msg = format!( 208 | "Invalid value `{}` for attribute `{}`.", 209 | name_value_pair.value.value(), 210 | name_value_pair.name 211 | ); 212 | Err(syn::Error::new(name_value_pair.value.span(), msg)) 213 | } 214 | } else { 215 | let msg = format!("Duplicate `{}` argument", name_value_pair.name); 216 | Err(syn::Error::new(name_value_pair.name.span(), msg)) 217 | } 218 | } 219 | 220 | fn parse_ident(value: &LitStr) -> Result { 221 | value.parse::() 222 | } 223 | 224 | fn parse_ident_list(value: &LitStr) -> Result, syn::Error> { 225 | Ok(value 226 | .value() 227 | .split(',') 228 | .map(str::trim) 229 | .map(|trait_name| Ident::new(trait_name, Span::call_site())) 230 | .collect()) 231 | } 232 | 233 | fn name_value_pair_list( 234 | struct_name: &Ident, 235 | attr: &Attribute, 236 | ) -> Result, syn::Error> { 237 | if let Meta::List(list) = attr.parse_meta()? { 238 | list.nested.iter().map(name_value_pair).collect() 239 | } else { 240 | let msg = format!( 241 | r#"float_eq attribute must be a list of options, for example `#[float_eq(ulps_tol = "{}Ulps")]`"#, 242 | struct_name 243 | ); 244 | Err(syn::Error::new(attr.path.span(), msg)) 245 | } 246 | } 247 | 248 | pub struct NameValuePair { 249 | pub name: Ident, 250 | pub value: LitStr, 251 | } 252 | 253 | pub fn name_value_pair(meta: &NestedMeta) -> Result { 254 | if let NestedMeta::Meta(Meta::NameValue(nv)) = meta { 255 | if let Some(name) = nv.path.get_ident() { 256 | if let Lit::Str(value) = &nv.lit { 257 | return Ok(NameValuePair { 258 | name: name.clone(), 259 | value: value.clone(), 260 | }); 261 | } 262 | } 263 | } 264 | Err(syn::Error::new( 265 | meta.span(), 266 | "Expected a `name = \"value\"` pair.", 267 | )) 268 | } 269 | -------------------------------------------------------------------------------- /generate-readme.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo ^ README.md 4 | echo THIS FILE IS GENERATED FROM crates-io.md, badges.md AND LICENSE.md.>> README.md 5 | echo DO NOT EDIT IT DIRECTLY.>> README.md 6 | echo --^>>> README.md 7 | echo.>> README.md 8 | echo # float_eq >> README.md 9 | echo.>> README.md 10 | type badges.md >> README.md 11 | echo.>> README.md 12 | more +2 "float_eq\crates-io.md" >> README.md 13 | echo.>> README.md 14 | echo ^>> README.md 15 | echo.>> README.md 16 | type LICENSE.md >> README.md 17 | --------------------------------------------------------------------------------