├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches ├── cmp.rs └── iter.rs ├── build.rs ├── ci └── script.sh ├── examples ├── all.rs ├── all_ints.rs ├── all_ints_par.rs ├── all_par.rs └── all_rev.rs └── src ├── impls.rs ├── iter ├── mod.rs └── rayon.rs ├── lib.rs └── traits.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | perf.data* 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | matrix: 3 | fast_finish: true 4 | 5 | include: 6 | - rust: nightly 7 | env: COVERAGE=1 8 | sudo: required 9 | 10 | - rust: stable 11 | services: docker 12 | env: TARGET=i686-unknown-linux-gnu 13 | sudo: required 14 | addons: 15 | apt: 16 | packages: 17 | - gcc-multilib 18 | 19 | - rust: stable 20 | services: docker 21 | env: TARGET=i686-unknown-linux-gnu FEATURES=rayon 22 | sudo: required 23 | addons: 24 | apt: 25 | packages: 26 | - gcc-multilib 27 | 28 | - rust: beta 29 | 30 | - rust: 1.23.0 31 | 32 | - rust: stable 33 | services: docker 34 | env: TARGET=mips64-unknown-linux-gnuabi64 35 | sudo: required 36 | 37 | - rust: stable 38 | services: docker 39 | env: TARGET=mips64-unknown-linux-gnuabi64 FEATURES=rayon 40 | sudo: required 41 | 42 | - rust: stable 43 | env: COVERAGE=1 44 | sudo: required 45 | 46 | - rust: stable 47 | env: COVERAGE=1 FEATURES=rayon 48 | sudo: required 49 | 50 | cache: cargo 51 | addons: 52 | apt: 53 | packages: 54 | - libcurl4-openssl-dev 55 | - libelf-dev 56 | - libdw-dev 57 | 58 | before_script: 59 | - export PATH=$HOME/.cargo/bin:$PATH 60 | 61 | script: 62 | - ./ci/script.sh 63 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ieee754" 3 | version = "0.2.6" 4 | authors = ["Huon Wilson "] 5 | 6 | homepage = "https://github.com/huonw/ieee754" 7 | repository = "https://github.com/huonw/ieee754" 8 | documentation = "http://docs.rs/ieee754" 9 | license = "MIT/Apache-2.0" 10 | keywords = ["floating-point", "mathematics", "numerics", "float", "no_std"] 11 | categories = ["no-std", "science"] 12 | 13 | description = """ 14 | Low-level manipulations of IEEE754 floating-point numbers. 15 | """ 16 | 17 | readme = "README.md" 18 | 19 | [build-dependencies] 20 | rustc_version = "0.2" 21 | 22 | [dependencies] 23 | rayon = { version = "1.0", optional = true } 24 | 25 | [dev-dependencies] 26 | criterion = "0.2" 27 | 28 | [[bench]] 29 | name = "iter" 30 | harness = false 31 | 32 | [[bench]] 33 | name = "cmp" 34 | harness = false 35 | 36 | [features] 37 | unstable = [] 38 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Huon Wilson 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `ieee754` 2 | 3 | [![Build Status](https://travis-ci.org/huonw/ieee754.svg?branch=master)](https://travis-ci.org/huonw/ieee754) [![codecov](https://codecov.io/gh/huonw/ieee754/branch/master/graph/badge.svg)](https://codecov.io/gh/huonw/ieee754) 4 | 5 | Low-level manipulations of IEEE754 floating-point numbers. 6 | 7 | This library includes: 8 | 9 | - `no_std` support by default, 10 | - ulp computation (units in the last place, representing the resolution of a 11 | float), 12 | - miscellaneous functions like `nextafter` (`next` and `prev`), 13 | `copysign` (`copy_sign`), `abs`, `sign`, 14 | - the IEEE-754 `totalOrder` predicate for doing `Ord::cmp`-like 15 | comparisons on floats, 16 | - an iterator over every floating point value in a range, 17 | - a parallel iterator over every floating point value in a range 18 | (optional: activate with the `rayon` feature), 19 | - relative error computation. 20 | 21 | [Documentation](http://docs.rs/ieee754), 22 | [crates.io](https://crates.io/crates/ieee754). 23 | -------------------------------------------------------------------------------- /benches/cmp.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate criterion; 2 | extern crate ieee754; 3 | 4 | use criterion::{Criterion, Fun, black_box}; 5 | 6 | use std::{f32, f64}; 7 | use ieee754::Ieee754; 8 | use std::cmp::Ordering; 9 | 10 | fn f32_cmp(c: &mut Criterion) { 11 | let std = Fun::new("std", |b, &data: &&[f32]| { 12 | b.iter(|| { 13 | for x in data { 14 | for y in data { 15 | if black_box(x).partial_cmp(y).unwrap_or(Ordering::Less) == Ordering::Less { 16 | black_box(*x); 17 | } 18 | } 19 | } 20 | }) 21 | }); 22 | let total = Fun::new("total", |b, &data: &&[f32]| { 23 | b.iter(|| { 24 | for x in data { 25 | for y in data { 26 | if black_box(x).total_cmp(y) == Ordering::Less { 27 | black_box(*x); 28 | } 29 | } 30 | } 31 | }) 32 | }); 33 | 34 | const DATA: &[f32] = &[ 35 | -f32::INFINITY,-0.97,0.66,-0.00,-0.49,1.68,f32::NAN,0.13,-0.41,0.59, 36 | -0.47,0.00,1.03,0.89,1.97,f32::INFINITY,0.17,-0.30,-0.16,-f32::NAN, 37 | ]; 38 | c.bench_functions("f32_cmp", vec![std, total], DATA); 39 | } 40 | 41 | fn f64_cmp(c: &mut Criterion) { 42 | let std = Fun::new("std", |b, &data: &&[f64]| { 43 | b.iter(|| { 44 | for x in data { 45 | for y in data { 46 | if black_box(x).partial_cmp(y).unwrap_or(Ordering::Less) == Ordering::Less { 47 | black_box(*x); 48 | } 49 | } 50 | } 51 | }) 52 | }); 53 | let total = Fun::new("total", |b, &data: &&[f64]| { 54 | b.iter(|| { 55 | for x in data { 56 | for y in data { 57 | if black_box(x).total_cmp(y) == Ordering::Less { 58 | black_box(*x); 59 | } 60 | } 61 | } 62 | }) 63 | }); 64 | 65 | const DATA: &[f64] = &[ 66 | -f64::INFINITY,-0.97,0.66,-0.00,-0.49,1.68,f64::NAN,0.13,-0.41,0.59, 67 | -0.47,0.00,1.03,0.89,1.97,f64::INFINITY,0.17,-0.30,-0.16,-f64::NAN, 68 | ]; 69 | c.bench_functions("f64_cmp", vec![std, total], DATA); 70 | } 71 | 72 | fn f32_sort(c: &mut Criterion) { 73 | let baseline = Fun::new("baseline", |b, &data: &&[f32]| { 74 | let v = data.iter().cloned().cycle().take(1000).collect::>(); 75 | 76 | b.iter(|| v.clone()); 77 | }); 78 | let std = Fun::new("std", |b, &data: &&[f32]| { 79 | let v = data.iter().cloned().cycle().take(1000).collect::>(); 80 | 81 | b.iter(|| { 82 | v.clone().sort_by( 83 | |a, b| a.partial_cmp(b).unwrap_or_else(|| { 84 | if a.is_nan() { Ordering::Less } else { Ordering::Greater } 85 | })) 86 | }) 87 | }); 88 | let total = Fun::new("total", |b, &data: &&[f32]| { 89 | let v = data.iter().cloned().cycle().take(1000).collect::>(); 90 | 91 | b.iter(|| { 92 | v.clone().sort_by(|a, b| a.total_cmp(b)) 93 | }) 94 | }); 95 | 96 | const DATA: &[f32] = &[ 97 | -f32::INFINITY,-0.97,0.66,-0.00,-0.49,1.68,f32::NAN,0.13,-0.41,0.59, 98 | -0.47,0.00,1.03,0.89,1.97,f32::INFINITY,0.17,-0.30,-0.16,-f32::NAN, 99 | ]; 100 | c.bench_functions("f32_sort", vec![baseline, std, total], DATA); 101 | } 102 | 103 | fn f64_sort(c: &mut Criterion) { 104 | let baseline = Fun::new("baseline", |b, &data: &&[f64]| { 105 | let v = data.iter().cloned().cycle().take(1000).collect::>(); 106 | 107 | b.iter(|| v.clone()); 108 | }); 109 | let std = Fun::new("std", |b, &data: &&[f64]| { 110 | let v = data.iter().cloned().cycle().take(1000).collect::>(); 111 | 112 | b.iter(|| { 113 | v.clone().sort_by( 114 | |a, b| a.partial_cmp(b).unwrap_or_else(|| { 115 | if a.is_nan() { Ordering::Less } else { Ordering::Greater } 116 | })) 117 | }) 118 | }); 119 | let total = Fun::new("total", |b, &data: &&[f64]| { 120 | let v = data.iter().cloned().cycle().take(1000).collect::>(); 121 | 122 | b.iter(|| { 123 | v.clone().sort_by(|a, b| a.total_cmp(b)) 124 | }) 125 | }); 126 | 127 | const DATA: &[f64] = &[ 128 | -f64::INFINITY,-0.97,0.66,-0.00,-0.49,1.68,f64::NAN,0.13,-0.41,0.59, 129 | -0.47,0.00,1.03,0.89,1.97,f64::INFINITY,0.17,-0.30,-0.16,-f64::NAN, 130 | ]; 131 | c.bench_functions("f64_sort", vec![baseline, std, total], DATA); 132 | } 133 | 134 | criterion_group!(benches, f32_cmp, f64_cmp, f32_sort, f64_sort); 135 | criterion_main!(benches); 136 | -------------------------------------------------------------------------------- /benches/iter.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate criterion; 2 | extern crate ieee754; 3 | 4 | use criterion::{Criterion, Bencher, ParameterizedBenchmark, black_box}; 5 | 6 | use ieee754::Ieee754; 7 | 8 | const SIZES: &[usize] = &[10, 100, 1_000, 1_000_000]; 9 | 10 | fn f32_iter(c: &mut Criterion) { 11 | type B = ::Bits; 12 | type S = ::Significand; 13 | 14 | let positive = |b: &mut Bencher, &i: &usize| { 15 | let end = f32::recompose(false, 0, i as S); 16 | b.iter(|| { 17 | assert_eq!(black_box(1_f32).upto(black_box(end)) 18 | .map(black_box).count(), 19 | i + 1); 20 | }) 21 | }; 22 | let positive_ext = |b: &mut Bencher, &i: &usize| { 23 | let end = f32::recompose(false, 0, i as S); 24 | b.iter(|| { 25 | let mut count = 0; 26 | for val in black_box(1_f32).upto(black_box(end)) { 27 | black_box(val); 28 | count += 1; 29 | } 30 | assert_eq!(count, i + 1); 31 | }) 32 | }; 33 | let positive_find = |b: &mut Bencher, &i: &usize| { 34 | let end = f32::recompose(false, 0, i as S); 35 | let limit = black_box(1e9); 36 | b.iter(|| { 37 | assert_eq!(black_box(1_f32).upto(black_box(end)) 38 | .map(black_box).find(|a| *a > limit), 39 | None); 40 | }) 41 | }; 42 | let over_zero = |b: &mut Bencher, &i: &usize| { 43 | let x = f32::recompose(false, -f32::exponent_bias(), (i / 2) as S); 44 | b.iter(|| { 45 | assert_eq!(black_box(-x).upto(black_box(x)) 46 | .map(black_box).count(), 47 | i + 1); 48 | }) 49 | }; 50 | let over_zero_ext = |b: &mut Bencher, &i: &usize| { 51 | let x = f32::recompose(false, -f32::exponent_bias(), (i / 2) as S); 52 | b.iter(|| { 53 | let mut count = 0; 54 | for val in black_box(-x).upto(black_box(x)) { 55 | black_box(val); 56 | count += 1; 57 | } 58 | assert_eq!(count, i + 1); 59 | }) 60 | }; 61 | let over_zero_find = |b: &mut Bencher, &i: &usize| { 62 | let x = f32::recompose(false, -f32::exponent_bias(), (i / 2) as S); 63 | let limit = black_box(1e9); 64 | b.iter(|| { 65 | assert_eq!(black_box(-x).upto(black_box(x)) 66 | .map(black_box).find(|a| *a > limit), 67 | None); 68 | }) 69 | }; 70 | let baseline = |b: &mut Bencher, &i: &usize| { 71 | b.iter(|| { 72 | assert_eq!((black_box(0 as B)..=black_box(i as B)) 73 | .map(black_box).count(), 74 | i + 1); 75 | }) 76 | }; 77 | let baseline_ext = |b: &mut Bencher, &i: &usize| { 78 | b.iter(|| { 79 | let mut count = 0; 80 | 81 | for val in black_box(0 as B)..=black_box(i as B) { 82 | black_box(val); 83 | count += 1; 84 | } 85 | assert_eq!(count, i + 1); 86 | }) 87 | }; 88 | let baseline_find = |b: &mut Bencher, &i: &usize| { 89 | let limit = black_box(1_000_000_000); 90 | b.iter(|| { 91 | assert_eq!((black_box(0 as B)..=black_box(i as B)) 92 | .map(black_box).find(|a| *a > limit), 93 | None); 94 | }) 95 | }; 96 | 97 | let internal = ParameterizedBenchmark::new("baseline", baseline, SIZES.to_owned()) 98 | .with_function("positive", positive) 99 | .with_function("over_zero", over_zero); 100 | let external = ParameterizedBenchmark::new("baseline", baseline_ext, SIZES.to_owned()) 101 | .with_function("positive", positive_ext) 102 | .with_function("over_zero", over_zero_ext); 103 | let find = ParameterizedBenchmark::new("baseline", baseline_find, SIZES.to_owned()) 104 | .with_function("positive", positive_find) 105 | .with_function("over_zero", over_zero_find); 106 | 107 | c.bench("iter_f32_internal", internal); 108 | c.bench("iter_f32_external", external); 109 | c.bench("iter_f32_find", find); 110 | } 111 | 112 | fn f64_iter(c: &mut Criterion) { 113 | type B = ::Bits; 114 | type S = ::Significand; 115 | 116 | let positive = |b: &mut Bencher, &i: &usize| { 117 | let end = f64::recompose(false, 0, i as S); 118 | b.iter(|| { 119 | assert_eq!(black_box(1_f64).upto(black_box(end)) 120 | .map(black_box).count(), 121 | i + 1); 122 | }) 123 | }; 124 | let positive_ext = |b: &mut Bencher, &i: &usize| { 125 | let end = f64::recompose(false, 0, i as S); 126 | b.iter(|| { 127 | let mut count = 0; 128 | for val in black_box(1_f64).upto(black_box(end)) { 129 | black_box(val); 130 | count += 1; 131 | } 132 | assert_eq!(count, i + 1); 133 | }) 134 | }; 135 | let positive_find = |b: &mut Bencher, &i: &usize| { 136 | let end = f64::recompose(false, 0, i as S); 137 | let limit = black_box(1e9); 138 | b.iter(|| { 139 | assert_eq!(black_box(1_f64).upto(black_box(end)) 140 | .find(|a| *a > limit), 141 | None); 142 | }) 143 | }; 144 | let over_zero = |b: &mut Bencher, &i: &usize| { 145 | let x = f64::recompose(false, -f64::exponent_bias(), (i / 2) as S); 146 | b.iter(|| { 147 | assert_eq!(black_box(-x).upto(black_box(x)) 148 | .map(black_box).count(), 149 | i + 1); 150 | }) 151 | }; 152 | let over_zero_ext = |b: &mut Bencher, &i: &usize| { 153 | let x = f64::recompose(false, -f64::exponent_bias(), (i / 2) as S); 154 | b.iter(|| { 155 | let mut count = 0; 156 | for val in black_box(-x).upto(black_box(x)) { 157 | black_box(val); 158 | count += 1; 159 | } 160 | assert_eq!(count, i + 1); 161 | }) 162 | }; 163 | let over_zero_find = |b: &mut Bencher, &i: &usize| { 164 | let x = f64::recompose(false, -f64::exponent_bias(), (i / 2) as S); 165 | let limit = black_box(1e9); 166 | b.iter(|| { 167 | assert_eq!(black_box(-x).upto(black_box(x)) 168 | .find(|a| *a > limit), 169 | None); 170 | }) 171 | }; 172 | let baseline = |b: &mut Bencher, &i: &usize| { 173 | b.iter(|| { 174 | assert_eq!((black_box(0 as B)..=black_box(i as B)) 175 | .map(black_box).count(), 176 | i + 1); 177 | }) 178 | }; 179 | let baseline_ext = |b: &mut Bencher, &i: &usize| { 180 | b.iter(|| { 181 | let mut count = 0; 182 | 183 | for val in black_box(0 as B)..=black_box(i as B) { 184 | black_box(val); 185 | count += 1; 186 | } 187 | assert_eq!(count, i + 1); 188 | }) 189 | }; 190 | let baseline_find = |b: &mut Bencher, &i: &usize| { 191 | let limit = black_box(1_000_000_000); 192 | b.iter(|| { 193 | assert_eq!((black_box(0 as B)..=black_box(i as B)) 194 | .find(|a| *a > limit), 195 | None); 196 | }) 197 | }; 198 | 199 | let internal = ParameterizedBenchmark::new("baseline", baseline, SIZES.to_owned()) 200 | .with_function("positive", positive) 201 | .with_function("over_zero", over_zero); 202 | let external = ParameterizedBenchmark::new("baseline", baseline_ext, SIZES.to_owned()) 203 | .with_function("positive", positive_ext) 204 | .with_function("over_zero", over_zero_ext); 205 | let find = ParameterizedBenchmark::new("baseline", baseline_find, SIZES.to_owned()) 206 | .with_function("positive", positive_find) 207 | .with_function("over_zero", over_zero_find); 208 | 209 | c.bench("iter_f64_internal", internal); 210 | c.bench("iter_f64_external", external); 211 | c.bench("iter_f64_find", find); 212 | } 213 | 214 | criterion_group!(benches, f32_iter, f64_iter); 215 | criterion_main!(benches); 216 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate rustc_version; 2 | 3 | fn main() { 4 | if rustc_version::version_meta().unwrap().channel <= rustc_version::Channel::Nightly { 5 | println!("cargo:rustc-cfg=nightly"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | cargo=cargo 5 | target_param="" 6 | features_param="" 7 | if [ ! -z "$TARGET" ]; then 8 | rustup target add "$TARGET" 9 | cargo install -v cross --force 10 | cargo="cross" 11 | target_param="--target $TARGET" 12 | fi 13 | 14 | if [ -n "$FEATURES" ]; then 15 | features_param="--features=$FEATURES" 16 | fi 17 | 18 | $cargo build -v $target_param $features_param 19 | 20 | if [ "$TRAVIS_RUST_VERSION" = "1.23.0" ]; then 21 | # testing requires building dev-deps, which require a newer Rust. 22 | exit 0 23 | fi 24 | 25 | $cargo test -v $target_param $features_param 26 | 27 | # for now, `cross bench` is broken https://github.com/rust-embedded/cross/issues/239 28 | if [ "$cargo" != "cross" ]; then 29 | $cargo bench -v $target_param $features_param -- --test # don't actually record numbers 30 | fi 31 | 32 | $cargo doc -v $target_param $features_param 33 | 34 | $cargo test -v --release $features_param 35 | 36 | if [ ! -z "$COVERAGE" ]; then 37 | if [ ! -z "$TARGET" ]; then 38 | echo "cannot record coverage while cross compiling" 39 | exit 1 40 | fi 41 | 42 | cargo install -v cargo-travis || echo "cargo-travis already installed" 43 | cargo coverage -v -m coverage-reports $features_param --kcov-build-location "$PWD/target" 44 | bash <(curl -s https://codecov.io/bash) -c -X gcov -X coveragepy -s coverage-reports 45 | fi 46 | -------------------------------------------------------------------------------- /examples/all.rs: -------------------------------------------------------------------------------- 1 | extern crate criterion; 2 | extern crate ieee754; 3 | use ieee754::Ieee754; 4 | use std::f32 as f; 5 | 6 | fn main() { 7 | let count = f::NEG_INFINITY.upto(f::INFINITY).map(criterion::black_box).count(); 8 | let expected = 9 | /* bits */ (1u64 << 32) 10 | - /* NaNs */ (1 << 24) 11 | - /* -0.0 */ 1 12 | + /* infinities */ 2; 13 | 14 | assert_eq!(count, expected as usize); 15 | println!("there are {} non-NaN floats", count); 16 | } 17 | -------------------------------------------------------------------------------- /examples/all_ints.rs: -------------------------------------------------------------------------------- 1 | extern crate criterion; 2 | 3 | // base line comparison for the all example. 4 | fn main() { 5 | let expected = 6 | /* bits */ (1u64 << 32) 7 | - /* NaNs */ (1 << 24) 8 | - /* -0.0 */ 1 9 | + /* infinities */ 2; 10 | 11 | println!("count {}", (0..expected as u32).map(criterion::black_box).count()); 12 | } 13 | -------------------------------------------------------------------------------- /examples/all_ints_par.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "rayon")] 2 | extern crate rayon; 3 | 4 | // base line comparison for the all example. 5 | #[cfg(feature = "rayon")] 6 | fn main() { 7 | extern crate criterion; 8 | use rayon::prelude::*; 9 | 10 | let expected = 11 | /* bits */ (1u64 << 32) 12 | - /* NaNs */ (1 << 24) 13 | - /* -0.0 */ 1 14 | + /* infinities */ 2; 15 | 16 | println!("count {}", 17 | (0..expected as u32).into_par_iter().map(criterion::black_box).count()); 18 | } 19 | 20 | #[cfg(not(feature = "rayon"))] 21 | fn main() { 22 | println!("all_par requires '--feature rayon'"); 23 | } 24 | -------------------------------------------------------------------------------- /examples/all_par.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "rayon")] 2 | extern crate rayon; 3 | #[cfg(feature = "rayon")] 4 | extern crate ieee754; 5 | 6 | #[cfg(feature = "rayon")] 7 | fn main() { 8 | extern crate criterion; 9 | use std::f32 as f; 10 | 11 | use ieee754::Ieee754; 12 | use rayon::prelude::*; 13 | 14 | let count = f::NEG_INFINITY.upto(f::INFINITY).into_par_iter().map(criterion::black_box).count(); 15 | let expected = 16 | /* bits */ (1u64 << 32) 17 | - /* NaNs */ (1 << 24) 18 | - /* -0.0 */ 1 19 | + /* infinities */ 2; 20 | 21 | assert_eq!(count, expected as usize); 22 | println!("there are {} non-NaN floats", count); 23 | } 24 | 25 | #[cfg(not(feature = "rayon"))] 26 | fn main() { 27 | println!("all_par requires '--features rayon'"); 28 | } 29 | -------------------------------------------------------------------------------- /examples/all_rev.rs: -------------------------------------------------------------------------------- 1 | extern crate criterion; 2 | extern crate ieee754; 3 | use ieee754::Ieee754; 4 | use std::f32 as f; 5 | 6 | fn main() { 7 | let count = f::NEG_INFINITY.upto(f::INFINITY).rev().map(criterion::black_box).count(); 8 | let expected = 9 | /* bits */ (1u64 << 32) 10 | - /* NaNs */ (1 << 24) 11 | - /* -0.0 */ 1 12 | + /* infinities */ 2; 13 | 14 | assert_eq!(count, expected as usize); 15 | println!("there are {} non-NaN floats", count); 16 | } 17 | -------------------------------------------------------------------------------- /src/impls.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::cmp::Ordering; 3 | use {Iter, Ieee754}; 4 | 5 | macro_rules! mask{ 6 | ($bits: expr; $current: expr => $($other: expr),*) => { 7 | ($bits >> (0 $(+ $other)*)) & ((1 << $current) - 1) 8 | } 9 | } 10 | macro_rules! unmask { 11 | ($x: expr => $($other: expr),*) => { 12 | $x << (0 $(+ $other)*) 13 | } 14 | } 15 | 16 | macro_rules! mk_impl { 17 | ($f: ident, $bits: ty, $signed_bits: ty, 18 | $expn: ty, $expn_raw: ty, $signif: ty, 19 | $expn_n: expr, $signif_n: expr) => { 20 | impl Ieee754 for $f { 21 | type Bits = $bits; 22 | type Exponent = $expn; 23 | type RawExponent = $expn_raw; 24 | type Significand = $signif; 25 | #[inline] 26 | fn upto(self, lim: Self) -> Iter { 27 | assert!(self <= lim); 28 | // map -0.0 to 0.0, i.e. ensure that any zero is 29 | // stored in a canonical manner. This is necessary to 30 | // use bit-hacks for the comparison in next. 31 | #[inline(always)] 32 | fn canon(x: $f) -> $f { if x == 0.0 { 0.0 } else { x } } 33 | 34 | ::iter::new_iter(canon(self), canon(lim)) 35 | } 36 | #[inline] 37 | fn ulp(self) -> Option { 38 | let (_sign, expn, _signif) = self.decompose_raw(); 39 | 40 | const MAX_EXPN: $expn_raw = ((1u64 << $expn_n) - 1) as $expn_raw; 41 | match expn { 42 | MAX_EXPN => None, 43 | 0 => Some($f::recompose_raw(false, 0, 1)), 44 | _ => { 45 | let ulp_expn = expn.saturating_sub($signif_n); 46 | if ulp_expn == 0 { 47 | Some($f::recompose_raw(false, 0, 1 << (expn - 1))) 48 | } else { 49 | Some($f::recompose_raw(false, ulp_expn, 0)) 50 | } 51 | } 52 | } 53 | } 54 | 55 | #[inline] 56 | fn next(self) -> Self { 57 | let abs_mask = (!(0 as Self::Bits)) >> 1; 58 | let mut bits = self.bits(); 59 | if self == 0.0 { 60 | bits = 1; 61 | } else if self < 0.0 { 62 | bits -= 1; 63 | if bits == !abs_mask { 64 | // normalise -0.0 to +0.0 65 | bits = 0 66 | } 67 | } else { 68 | bits += 1 69 | } 70 | Ieee754::from_bits(bits) 71 | } 72 | #[inline] 73 | fn prev(self) -> Self { 74 | let abs_mask = (!(0 as Self::Bits)) >> 1; 75 | let mut bits = self.bits(); 76 | if self < 0.0 { 77 | bits += 1; 78 | } else if bits & abs_mask == 0 { 79 | bits = 1 | !abs_mask; 80 | } else { 81 | bits -= 1; 82 | } 83 | Ieee754::from_bits(bits) 84 | } 85 | 86 | #[inline] 87 | fn exponent_bias() -> Self::Exponent { 88 | (1 << ($expn_n - 1)) - 1 89 | } 90 | 91 | #[inline] 92 | fn bits(self) -> Self::Bits { 93 | unsafe {mem::transmute(self)} 94 | } 95 | #[inline] 96 | fn from_bits(bits: Self::Bits) -> Self { 97 | unsafe {mem::transmute(bits)} 98 | } 99 | #[inline] 100 | fn decompose_raw(self) -> (bool, Self::RawExponent, Self::Significand) { 101 | let bits = self.bits(); 102 | 103 | (mask!(bits; 1 => $expn_n, $signif_n) != 0, 104 | mask!(bits; $expn_n => $signif_n) as Self::RawExponent, 105 | mask!(bits; $signif_n => ) as Self::Significand) 106 | 107 | } 108 | #[inline] 109 | fn recompose_raw(sign: bool, expn: Self::RawExponent, signif: Self::Significand) -> Self { 110 | Ieee754::from_bits( 111 | unmask!(sign as Self::Bits => $expn_n, $signif_n) | 112 | unmask!(expn as Self::Bits => $signif_n) | 113 | unmask!(signif as Self::Bits => )) 114 | } 115 | 116 | #[inline] 117 | fn decompose(self) -> (bool, Self::Exponent, Self::Significand) { 118 | let (sign, expn, signif) = self.decompose_raw(); 119 | (sign, expn as Self::Exponent - Self::exponent_bias(), 120 | signif) 121 | } 122 | #[inline] 123 | fn recompose(sign: bool, expn: Self::Exponent, signif: Self::Significand) -> Self { 124 | Self::recompose_raw(sign, 125 | (expn + Self::exponent_bias()) as Self::RawExponent, 126 | signif) 127 | } 128 | 129 | #[inline] 130 | fn total_cmp(&self, other: &Self) -> Ordering { 131 | #[inline] 132 | fn cmp_key(x: $f) -> $signed_bits { 133 | let bits = x.bits(); 134 | let sign_bit = bits & (1 << ($expn_n + $signif_n)); 135 | let mask = ((sign_bit as $signed_bits) >> ($expn_n + $signif_n)) as $bits >> 1; 136 | (bits ^ mask) as $signed_bits 137 | } 138 | cmp_key(*self).cmp(&cmp_key(*other)) 139 | } 140 | 141 | #[inline] 142 | fn abs(self) -> Self { 143 | let (_, e, s) = self.decompose_raw(); 144 | Self::recompose_raw(false, e, s) 145 | } 146 | 147 | #[inline] 148 | fn copy_sign(self, sign: Self) -> Self { 149 | let (s, _, _) = sign.decompose_raw(); 150 | let (_, e, m) = self.decompose_raw(); 151 | Self::recompose_raw(s, e, m) 152 | } 153 | 154 | #[inline] 155 | fn sign(self) -> Self { 156 | if self == 0.0 || self != self { // is NaN? (.is_nan() added to core in 1.27) 157 | self 158 | } else { 159 | 1.0.copy_sign(self) 160 | } 161 | } 162 | 163 | #[inline] 164 | fn rel_error(self, exact: Self) -> Self { 165 | use core::$f; 166 | if exact == 0.0 { 167 | if self == 0.0 { 168 | 0.0 169 | } else { 170 | self.sign() * $f::INFINITY 171 | } 172 | } else if exact.abs() == $f::INFINITY { 173 | if self == exact { 174 | 0.0 175 | } else if self != self { // is NaN? (.is_nan() added to core in 1.27) 176 | self 177 | } else { 178 | $f::NEG_INFINITY 179 | } 180 | } else { 181 | // NaNs propagate 182 | (self - exact) / exact 183 | } 184 | } 185 | } 186 | 187 | #[cfg(test)] 188 | mod $f { 189 | use std::prelude::v1::*; 190 | use std::{$f, usize}; 191 | 192 | use {Ieee754, Iter}; 193 | 194 | // test both `next`, and any potential internal-iteration 195 | // optimisations that the iterators support (which will 196 | // almost certainly be exhibited via `fold`) 197 | fn count_nexts(mut it: I) -> usize { 198 | let mut i = 0; 199 | while let Some(_) = it.next() { i += 1 } 200 | i 201 | } 202 | fn count_fold(it: I) -> usize { 203 | it.fold(0, |i, _| i + 1) 204 | } 205 | #[cfg(nightly)] 206 | fn count_try_fold(mut it: I) -> usize { 207 | const AT_A_TIME: usize = 5; 208 | (0..10).map(|_| { 209 | it.try_fold(0, |i, _| { 210 | let count = i + 1; 211 | if count < AT_A_TIME { 212 | Ok(count) 213 | } else { 214 | // make the error value different to the ok one 215 | Err((count,)) 216 | } 217 | }).unwrap_or_else(|x| x.0) 218 | }).take_while(|x| *x > 0).sum() 219 | } 220 | #[cfg(not(nightly))] 221 | fn count_try_fold(it: I) -> usize { 222 | count_fold(it) 223 | } 224 | fn count_try_fold_result(mut it: Iter<$f>) -> usize { 225 | const AT_A_TIME: usize = 5; 226 | (0..10).map(|_| { 227 | it.try_fold_result(0, |i, _| { 228 | let count = i + 1; 229 | if count < AT_A_TIME { 230 | Ok(count) 231 | } else { 232 | // make the error value different to the ok one 233 | Err((count,)) 234 | } 235 | }).unwrap_or_else(|x| x.0) 236 | }).take_while(|x| *x > 0).sum() 237 | } 238 | fn count_try_rfold_result(mut it: Iter<$f>) -> usize { 239 | const AT_A_TIME: usize = 5; 240 | (0..10).map(|_| { 241 | it.try_rfold_result(0, |i, _| { 242 | let count = i + 1; 243 | if count < AT_A_TIME { 244 | Ok(count) 245 | } else { 246 | // make the error value different to the ok one 247 | Err((count,)) 248 | } 249 | }).unwrap_or_else(|x| x.0) 250 | }).take_while(|x| *x > 0).sum() 251 | } 252 | fn count(it: I) -> usize { 253 | let nexts = count_nexts(it.clone()); 254 | let fold = count_fold(it.clone()); 255 | let try_fold = count_try_fold(it.clone()); 256 | let count = it.count(); 257 | // check they all match 258 | assert_eq!(nexts, fold); 259 | assert_eq!(fold, try_fold); 260 | assert_eq!(fold, count); 261 | count 262 | } 263 | 264 | fn collect_nexts(mut it: I) -> Vec { 265 | let mut v = vec![]; 266 | while let Some(x) = it.next() { 267 | v.push(x); 268 | } 269 | v 270 | } 271 | fn collect_fold(it: I) -> Vec { 272 | it.fold(vec![], |mut v, x| { v.push(x); v }) 273 | } 274 | #[cfg(nightly)] 275 | fn collect_try_fold(mut it: I) -> Vec { 276 | const AT_A_TIME: usize = 5; 277 | (0..10).map(|_| { 278 | it.try_fold(vec![], |mut v, x| { 279 | v.push(x); 280 | if v.len() < AT_A_TIME { Ok(v) } else { Err(v) } 281 | }).unwrap_or_else(|x| x) 282 | }).take_while(|x| x.len() > 0).flat_map(|x| x).collect() 283 | } 284 | #[cfg(not(nightly))] 285 | fn collect_try_fold(it: I) -> Vec { 286 | collect_fold(it) 287 | } 288 | fn collect_try_fold_result(mut it: Iter<$f>) -> Vec<$f> { 289 | const AT_A_TIME: usize = 5; 290 | (0..10).map(|_| { 291 | it.try_fold_result(vec![], |mut v, x| { 292 | v.push(x); 293 | if v.len() < AT_A_TIME { Ok(v) } else { Err(v) } 294 | }).unwrap_or_else(|x| x) 295 | }).take_while(|x| x.len() > 0).flat_map(|x| x).collect() 296 | } 297 | fn collect_try_rfold_result(mut it: Iter<$f>) -> Vec<$f> { 298 | const AT_A_TIME: usize = 5; 299 | (0..10).map(|_| { 300 | it.try_rfold_result(vec![], |mut v, x| { 301 | v.push(x); 302 | if v.len() < AT_A_TIME { Ok(v) } else { Err(v) } 303 | }).unwrap_or_else(|x| x) 304 | }).take_while(|x| x.len() > 0).flat_map(|x| x).collect() 305 | } 306 | fn collect + Clone>(it: I) -> Vec<$f> { 307 | let nexts = collect_nexts(it.clone()); 308 | let fold = collect_fold(it.clone()); 309 | let try_fold = collect_try_fold(it.clone()); 310 | let collect = it.collect::>(); 311 | // check they all match 312 | assert_eq!(nexts, fold); 313 | assert_eq!(fold, try_fold); 314 | assert_eq!(fold, collect); 315 | collect 316 | } 317 | 318 | #[test] 319 | fn upto() { 320 | let one = (0.0 as $f).upto(0.0); 321 | assert_eq!(collect(one.clone()), &[0.0]); 322 | assert_eq!(collect_try_fold_result(one), &[0.0]); 323 | 324 | let ten = $f::recompose(false, 1, 1).upto($f::recompose(false, 1, 10)); 325 | assert_eq!(count(ten.clone()), 10); 326 | assert_eq!(count_try_fold_result(ten), 10); 327 | 328 | let twenty_one = $f::recompose(true, -$f::exponent_bias(), 10) 329 | .upto($f::recompose(false, -$f::exponent_bias(), 10)); 330 | assert_eq!(count(twenty_one.clone()), 21); 331 | assert_eq!(count_try_fold_result(twenty_one), 21); 332 | } 333 | #[test] 334 | fn upto_rev() { 335 | let one = (0.0 as $f).upto(0.0); 336 | assert_eq!(collect(one.clone().rev()), &[0.0]); 337 | assert_eq!(collect_try_rfold_result(one), &[0.0]); 338 | 339 | let ten = $f::recompose(false, 1, 1).upto($f::recompose(false, 1, 10)); 340 | assert_eq!(count(ten.clone().rev()), 10); 341 | assert_eq!(count_try_rfold_result(ten), 10); 342 | 343 | let twenty_one = $f::recompose(true, -$f::exponent_bias(), 10) 344 | .upto($f::recompose(false, -$f::exponent_bias(), 10)); 345 | assert_eq!(count(twenty_one.clone().rev()), 21); 346 | assert_eq!(count_try_rfold_result(twenty_one), 21); 347 | } 348 | 349 | #[test] 350 | fn upto_infinities() { 351 | use std::$f as f; 352 | assert_eq!(collect(f::MAX.upto(f::INFINITY)), 353 | &[f::MAX, f::INFINITY]); 354 | assert_eq!(collect(f::NEG_INFINITY.upto(f::MIN)), 355 | &[f::NEG_INFINITY, f::MIN]); 356 | } 357 | #[test] 358 | fn upto_infinities_rev() { 359 | use std::$f as f; 360 | assert_eq!(collect(f::MAX.upto(f::INFINITY).rev()), 361 | &[f::INFINITY, f::MAX]); 362 | assert_eq!(collect(f::NEG_INFINITY.upto(f::MIN).rev()), 363 | &[f::MIN, f::NEG_INFINITY]); 364 | } 365 | 366 | #[test] 367 | fn upto_size_hint_table() { 368 | let signs = 2; 369 | let finite_expns = (1 << $expn_n) - 1; 370 | let subone_expns = (1 << ($expn_n - 1)) - 1; 371 | let per_binade = 1u64 << $signif_n; 372 | let table: &[($f, $f, u64)] = &[ 373 | (0.0, 1.0, subone_expns * per_binade + /* +1: */ 1), 374 | (-1.0, 0.0, subone_expns * per_binade + /* -1: */ 1), 375 | (2.0, 4.0, per_binade + /* +4: */ 1), 376 | (-4.0, -2.0, per_binade + /* -2: */ 1), 377 | (-1.0, 1.0, 378 | signs * subone_expns * per_binade + /* +/-1: */ 2 - /* -0: */1), 379 | ($f::NEG_INFINITY, $f::INFINITY, 380 | signs * finite_expns * per_binade + /* +/-inf: */ 2 - /* -0: */ 1) 381 | ]; 382 | 383 | for &(low, hi, count) in table.iter() { 384 | let hint = low.upto(hi).size_hint(); 385 | if count > usize::MAX as u64 { 386 | assert_eq!(hint, (usize::MAX, None), "{:?}->{:?}", low, hi); 387 | } else { 388 | let count = count as usize; 389 | assert_eq!(hint, (count, Some(count)), "{:?}->{:?}", low, hi); 390 | } 391 | } 392 | } 393 | 394 | #[test] 395 | fn upto_size_hint_iterate() { 396 | let mut iter = 397 | $f::recompose(true, -$f::exponent_bias(), 10) 398 | .upto($f::recompose(false, -$f::exponent_bias(), 10)); 399 | 400 | assert_eq!(iter.size_hint(), (21, Some(21))); 401 | for i in (0..21).rev() { 402 | assert!(iter.next().is_some()); 403 | assert_eq!(iter.size_hint(), (i, Some(i))); 404 | } 405 | assert_eq!(iter.next(), None); 406 | assert_eq!(iter.size_hint(), (0, Some(0))) 407 | } 408 | 409 | #[test] 410 | fn upto_size_hint_rev() { 411 | let mut iter = 412 | $f::recompose(true, -$f::exponent_bias(), 10) 413 | .upto($f::recompose(false, -$f::exponent_bias(), 10)) 414 | .rev(); 415 | 416 | assert_eq!(iter.size_hint(), (21, Some(21))); 417 | for i in (0..21).rev() { 418 | assert!(iter.next().is_some()); 419 | assert_eq!(iter.size_hint(), (i, Some(i))); 420 | } 421 | assert_eq!(iter.next(), None); 422 | assert_eq!(iter.size_hint(), (0, Some(0))) 423 | } 424 | 425 | #[test] 426 | fn upto_fmt() { 427 | fn test(from: $f, to: $f) { 428 | let mut iter = from.upto(to); 429 | assert_eq!(format!("{:?}", iter), 430 | format!("Iter {{ from: {:?}, to: {:?} }}", 431 | from, to)); 432 | 433 | if from.next() < to.prev() { 434 | let _ = iter.next(); 435 | let _ = iter.next_back(); 436 | assert_eq!(format!("{:?}", iter), 437 | format!("Iter {{ from: {:?}, to: {:?} }}", 438 | from.next(), to.prev())); 439 | } 440 | 441 | if iter.size_hint().0 < 1_000_000 { 442 | iter.by_ref().for_each(|_| {}); 443 | assert_eq!(format!("{:?}", iter), 444 | "Iter { done: true }"); 445 | } 446 | } 447 | 448 | test(0.0, 0.0); 449 | test(0.0, 1.0); 450 | test(-1.0, 1.0); 451 | test(0.0, (0.0 as $f).next().next()); 452 | test(1e30, (1e30 as $f).next().next().next()); 453 | test($f::NEG_INFINITY, $f::INFINITY); 454 | } 455 | 456 | #[test] 457 | fn next_prev_order() { 458 | let cases = [0.0 as $f, -0.0, 1.0, 1.0001, 1e30, -1.0, -1.0001, -1e30]; 459 | for &x in &cases { 460 | assert!(x.next() > x); 461 | assert!(x.prev() < x); 462 | } 463 | } 464 | 465 | #[test] 466 | fn ulp_smoke() { 467 | let smallest_subnormal = $f::recompose_raw(false, 0, 1); 468 | let smallest_normal = $f::recompose_raw(false, 1, 0); 469 | assert_eq!((0.0 as $f).ulp(), Some(smallest_subnormal)); 470 | assert_eq!(smallest_subnormal.ulp(), Some(smallest_subnormal)); 471 | assert_eq!($f::recompose_raw(true, 0, 9436).ulp(), 472 | Some(smallest_subnormal)); 473 | assert_eq!(smallest_normal.ulp(), Some(smallest_subnormal)); 474 | 475 | assert_eq!((1.0 as $f).ulp(), 476 | Some($f::recompose(false, -$signif_n, 0))); 477 | 478 | assert_eq!((-123.456e30 as $f).ulp(), 479 | Some($f::recompose(false, 106 - $signif_n, 0))); 480 | 481 | assert_eq!($f::INFINITY.ulp(), None); 482 | assert_eq!($f::NEG_INFINITY.ulp(), None); 483 | assert_eq!($f::NAN.ulp(), None); 484 | } 485 | 486 | #[test] 487 | fn ulp_aggressive() { 488 | fn check_ulp(x: $f, ulp: $f) { 489 | println!(" {:e} {:e}", x, ulp); 490 | assert_eq!(x.ulp(), Some(ulp)); 491 | // with signed-magnitude we need to be moving away 492 | let same_sign_ulp = if x < 0.0 { -ulp } else { ulp }; 493 | 494 | assert_ne!(x + same_sign_ulp, x, "adding ulp should be different"); 495 | 496 | if ulp / 2.0 > 0.0 { 497 | // floats break ties like this by rounding to 498 | // even (in the default mode), so adding half 499 | // a ulp may be a new value depending on the 500 | // significand. 501 | if x.decompose().2 & 1 == 0 { 502 | assert_eq!(x + same_sign_ulp / 2.0, x); 503 | } else { 504 | assert_eq!(x + same_sign_ulp / 2.0, x + same_sign_ulp); 505 | } 506 | } 507 | // no ties to worry about 508 | assert_eq!(x + same_sign_ulp / 4.0, x); 509 | } 510 | 511 | let smallest_subnormal = $f::recompose_raw(false, 0, 1); 512 | let mut ulp = smallest_subnormal; 513 | 514 | check_ulp(0.0, ulp); 515 | 516 | let mut pow2 = smallest_subnormal; 517 | for i in 0..200 { 518 | println!("{}", i); 519 | check_ulp(pow2, ulp); 520 | check_ulp(-pow2, ulp); 521 | 522 | let (_, e, _) = pow2.decompose_raw(); 523 | if e > 0 { 524 | for &signif in &[1, 525 | // random numbers 526 | 9436, 1577069, 527 | // last two for this exponent 528 | (1 << $signif_n) - 2, (1 << $signif_n) - 1] { 529 | check_ulp($f::recompose_raw(false, e, signif), ulp); 530 | check_ulp($f::recompose_raw(true, e, signif), ulp); 531 | } 532 | } 533 | 534 | pow2 *= 2.0; 535 | if i >= $signif_n { 536 | ulp *= 2.0; 537 | } 538 | } 539 | } 540 | 541 | #[test] 542 | fn abs() { 543 | let this_abs = <$f as Ieee754>::abs; 544 | assert!(this_abs($f::NAN).is_nan()); 545 | 546 | let cases = [0.0 as $f, -1.0, 1.0001, 547 | // denormals 548 | $f::recompose_raw(false, 0, 123), $f::recompose(true, 0, 123), 549 | $f::NEG_INFINITY, $f::INFINITY]; 550 | for x in &cases { 551 | assert_eq!(this_abs(*x), x.abs()); 552 | } 553 | } 554 | 555 | #[test] 556 | fn total_cmp() { 557 | let nan_exp = $f::NAN.decompose_raw().1; 558 | let q = 1 << ($signif_n - 1); 559 | 560 | let qnan0 = $f::recompose_raw(false, nan_exp, q); 561 | let qnan1 = $f::recompose_raw(false, nan_exp, q | 1); 562 | let qnanlarge = $f::recompose_raw(false, nan_exp, q | (q - 1)); 563 | 564 | let snan1 = $f::recompose_raw(false, nan_exp, 1); 565 | let snan2 = $f::recompose_raw(false, nan_exp, 2); 566 | let snanlarge = $f::recompose_raw(false, nan_exp, q - 1); 567 | 568 | let subnormal = $f::recompose_raw(false, 0, 1); 569 | 570 | // it's a total order, so we can literally write 571 | // options in order, and compare them all, using their 572 | // indices as ground-truth. NB. the snans seem to 573 | // get canonicalized to qnan on some versions of i686 574 | // Linux (using `cross` on Travis CI), so we can't 575 | // include them. 576 | let include_snan = cfg!(not(target_arch = "x86")); 577 | 578 | fn neg(x: $f) -> $f { x.copy_sign(-1.0) } 579 | // -qNaN 580 | let mut cases = vec![neg(qnanlarge), neg(qnan1), neg(qnan0)]; 581 | // -sNaN 582 | if include_snan { 583 | cases.extend_from_slice(&[-snanlarge, -snan2, -snan1]); 584 | } 585 | // Numbers (note -0, +0) 586 | cases.extend_from_slice(&[ 587 | $f::NEG_INFINITY, 588 | -1e15, -1.001, -1.0, -0.999, -1e-15, -subnormal, 589 | -0.0, 0.0, 590 | subnormal, 1e-15, 0.999, 1.0, 1.001, 1e15, 591 | $f::INFINITY 592 | ]); 593 | // +sNaN 594 | if include_snan { 595 | cases.extend_from_slice(&[snan1, snan2, snanlarge]); 596 | } 597 | // +qNaN 598 | cases.extend_from_slice(&[qnan0, qnan1, qnanlarge]); 599 | 600 | for (ix, &x) in cases.iter().enumerate() { 601 | for (iy, &y) in cases.iter().enumerate() { 602 | let computed = x.total_cmp(&y); 603 | let expected = ix.cmp(&iy); 604 | assert_eq!( 605 | computed, expected, 606 | "{:e} ({}, {:?}) cmp {:e} ({}, {:?}), got: {:?}, expected: {:?}", 607 | x, ix, x.decompose(), 608 | y, iy, y.decompose(), 609 | computed, expected); 610 | } 611 | } 612 | } 613 | 614 | #[test] 615 | fn copy_sign() { 616 | let positives = [$f::NAN, $f::INFINITY, 1e10, 1.0, 0.0]; 617 | 618 | for &pos in &positives { 619 | // CI fails on i686 in debug mode if we do 620 | // arithmetic directly on NaN: the result is 621 | // normalized to qNaN(0). 622 | let neg = if pos.is_nan() { -$f::NAN } else { -pos }; 623 | 624 | assert_eq!((1 as $f).copy_sign(pos), 1.0); 625 | assert_eq!((1 as $f).copy_sign(neg), -1.0); 626 | assert_eq!((-1 as $f).copy_sign(pos), 1.0); 627 | assert_eq!((-1 as $f).copy_sign(neg), -1.0); 628 | 629 | assert_eq!($f::INFINITY.copy_sign(pos), $f::INFINITY); 630 | assert_eq!($f::INFINITY.copy_sign(neg), $f::NEG_INFINITY); 631 | assert_eq!($f::NEG_INFINITY.copy_sign(pos), $f::INFINITY); 632 | assert_eq!($f::NEG_INFINITY.copy_sign(neg), $f::NEG_INFINITY); 633 | 634 | assert!($f::NAN.copy_sign(pos).is_nan()); 635 | assert!($f::NAN.copy_sign(neg).is_nan()); 636 | } 637 | } 638 | 639 | #[test] 640 | fn sign() { 641 | assert!($f::NAN.sign().is_nan()); 642 | 643 | assert_eq!($f::NEG_INFINITY.sign(), -1.0); 644 | assert_eq!((-1e10 as $f).sign(), -1.0); 645 | assert_eq!((-1.0 as $f).sign(), -1.0); 646 | assert_eq!((-0.0 as $f).sign().bits(), (-0.0 as $f).bits()); 647 | assert_eq!((0.0 as $f).sign().bits(), (0.0 as $f).bits()); 648 | assert_eq!((1.0 as $f).sign(), 1.0); 649 | assert_eq!((1e10 as $f).sign(), 1.0); 650 | assert_eq!($f::INFINITY.sign(), 1.0); 651 | } 652 | 653 | #[test] 654 | fn rel_error() { 655 | let zer: $f = 0.0; 656 | let one: $f = 1.0; 657 | let two: $f = 2.0; 658 | 659 | assert_eq!(zer.rel_error(one), -1.0); 660 | assert_eq!(one.rel_error(one), 0.0); 661 | assert_eq!((-one).rel_error(one), -2.0); 662 | assert_eq!(two.rel_error(one), 1.0); 663 | assert_eq!((-two).rel_error(one), -3.0); 664 | 665 | assert_eq!(zer.rel_error(-one), -1.0); 666 | assert_eq!(one.rel_error(-one), -2.0); 667 | assert_eq!((-one).rel_error(-one), 0.0); 668 | assert_eq!(two.rel_error(-one), -3.0); 669 | assert_eq!((-two).rel_error(-one), 1.0); 670 | 671 | assert_eq!(zer.rel_error(two), -1.0); 672 | assert_eq!(one.rel_error(two), -0.5); 673 | assert_eq!((-one).rel_error(two), -1.5); 674 | assert_eq!(two.rel_error(two), 0.0); 675 | assert_eq!((-two).rel_error(two), -2.0); 676 | 677 | assert_eq!(zer.rel_error(-two), -1.0); 678 | assert_eq!(one.rel_error(-two), -1.5); 679 | assert_eq!((-one).rel_error(-two), -0.5); 680 | assert_eq!(two.rel_error(-two), -2.0); 681 | assert_eq!((-two).rel_error(-two), 0.0); 682 | } 683 | #[test] 684 | fn rel_error_edge_cases() { 685 | let nan = $f::NAN; 686 | let inf = $f::INFINITY; 687 | let zer: $f = 0.0; 688 | let one: $f = 1.0; 689 | 690 | assert!(nan.rel_error(nan).is_nan()); 691 | assert!(zer.rel_error(nan).is_nan()); 692 | assert!(nan.rel_error(zer).is_nan()); 693 | 694 | assert_eq!(zer.rel_error(zer), 0.0); 695 | assert_eq!(zer.rel_error(-zer), 0.0); 696 | assert_eq!((-zer).rel_error(zer), 0.0); 697 | assert_eq!((-zer).rel_error(-zer), 0.0); 698 | assert_eq!(one.rel_error(zer), inf); 699 | assert_eq!((-one).rel_error(zer), -inf); 700 | assert_eq!(inf.rel_error(zer), inf); 701 | assert_eq!((-inf).rel_error(zer), -inf); 702 | 703 | 704 | assert_eq!(inf.rel_error(inf), 0.0); 705 | assert_eq!(inf.rel_error(-inf), -inf); 706 | assert_eq!((-inf).rel_error(inf), -inf); 707 | assert_eq!((-inf).rel_error(-inf), 0.0); 708 | assert_eq!(zer.rel_error(inf), -inf); 709 | assert_eq!(zer.rel_error(-inf), -inf); 710 | } 711 | } 712 | } 713 | } 714 | 715 | mk_impl!(f32, u32, i32, i16, u8, u32, 8, 23); 716 | mk_impl!(f64, u64, i64, i16, u16, u64, 11, 52); 717 | -------------------------------------------------------------------------------- /src/iter/mod.rs: -------------------------------------------------------------------------------- 1 | use core::usize; 2 | use core::fmt; 3 | #[cfg(nightly)] 4 | use core::ops::Try; 5 | use {Bits, Ieee754}; 6 | 7 | #[cfg(feature = "rayon")] 8 | pub mod rayon; 9 | 10 | /// An iterator over floating point numbers, created by `Ieee754::upto`. 11 | #[derive(Clone)] 12 | pub struct Iter { 13 | neg: SingleSignIter, 14 | pos: SingleSignIter 15 | } 16 | /// Create an iterator over the floating point values in [from, to] 17 | /// (inclusive!) 18 | pub fn new_iter(from: T, to: T) -> Iter { 19 | // this also NaN, e.g. (NaN <= x) == false for all x. 20 | assert!(from <= to); 21 | 22 | let from_bits = from.bits(); 23 | let to_bits = to.bits(); 24 | let negative = from_bits.high(); 25 | let positive = !to_bits.high(); 26 | 27 | let neg_start = from_bits; 28 | let pos_end = to_bits.next(); 29 | 30 | let (neg_end, pos_start) = match (negative, positive) { 31 | (true, true) => (T::Bits::imin(), T::Bits::zero()), 32 | // self is a range with just one sign, so one side is 33 | // empty (has start == end) 34 | (false, true) => (neg_start, from_bits), 35 | (true, false) => (to_bits.prev(), pos_end), 36 | // impossible to have no negative and no positive values 37 | (false, false) => unreachable!() 38 | }; 39 | 40 | Iter { 41 | neg: SingleSignIter { from: neg_start, to: neg_end, _sign: Negative }, 42 | pos: SingleSignIter { from: pos_start, to: pos_end, _sign: Positive }, 43 | } 44 | } 45 | 46 | fn u64_to_size_hint(x: u64) -> (usize, Option) { 47 | if x <= usize::MAX as u64 { 48 | let d = x as usize; 49 | (d, Some(d)) 50 | } else { 51 | (usize::MAX, None) 52 | } 53 | } 54 | 55 | #[cfg(nightly)] 56 | fn result_to_try(r: Result) -> R { 57 | match r { 58 | Ok(ok) => R::from_ok(ok), 59 | Err(error) => R::from_error(error) 60 | } 61 | } 62 | 63 | impl Iter { 64 | fn len(&self) -> u64 { 65 | self.neg.len() + self.pos.len() 66 | } 67 | 68 | fn done(&self) -> bool { self.neg.done() && self.pos.done() } 69 | 70 | /// A non-nightly only version of `Iterator::try_fold`. 71 | pub fn try_fold_result(&mut self, init: B, mut f: F) -> Result 72 | where F: FnMut(B, T) -> Result { 73 | let next = self.neg.try_fold_result(init, &mut f)?; 74 | self.pos.try_fold_result(next, f) 75 | } 76 | 77 | /// A non-nightly only version of `Iterator::try_fold`. 78 | pub fn try_rfold_result(&mut self, init: B, mut f: F) -> Result 79 | where F: FnMut(B, T) -> Result { 80 | let next = self.pos.try_rfold_result(init, &mut f)?; 81 | self.neg.try_fold_result(next, f) 82 | } 83 | } 84 | 85 | impl Iterator for Iter { 86 | type Item = T; 87 | fn next(&mut self) -> Option { 88 | self.neg.next().or_else(|| self.pos.next()) 89 | } 90 | 91 | fn size_hint(&self) -> (usize, Option) { 92 | u64_to_size_hint(self.len()) 93 | } 94 | 95 | // internal iteration optimisations: 96 | fn fold(self, init: B, mut f: F) -> B 97 | where F: FnMut(B, Self::Item) -> B 98 | { 99 | let next = self.neg.fold(init, &mut f); 100 | self.pos.fold(next, f) 101 | } 102 | 103 | #[cfg(nightly)] 104 | fn try_fold(&mut self, init: B, mut f: F) -> R 105 | where F: FnMut(B, Self::Item) -> R, 106 | R: Try { 107 | result_to_try(self.try_fold_result(init, |b, x| f(b, x).into_result())) 108 | } 109 | } 110 | 111 | impl DoubleEndedIterator for Iter { 112 | fn next_back(&mut self) -> Option { 113 | self.pos.next_back().or_else(|| self.neg.next_back()) 114 | } 115 | } 116 | 117 | // equal if they would iterate the same elements, even if the precise 118 | // stored values are different. 119 | impl PartialEq for Iter { 120 | fn eq(&self, other: &Self) -> bool { 121 | let mut self_ = self.clone(); 122 | let mut other_ = other.clone(); 123 | 124 | self_.next() == other_.next() && self_.next_back() == other_.next_back() 125 | } 126 | } 127 | impl Eq for Iter {} 128 | 129 | impl fmt::Debug for Iter { 130 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 131 | let mut dbg = f.debug_struct("Iter"); 132 | if self.done() { 133 | dbg.field("done", &true); 134 | } else { 135 | let mut iter = self.clone(); 136 | let (from, to) = match (iter.next(), iter.next_back()) { 137 | (Some(f), Some(t)) => (f, t), 138 | (Some(f), None) => (f, f), 139 | _ => unreachable!() 140 | }; 141 | dbg.field("from", &from) 142 | .field("to", &to); 143 | } 144 | dbg.finish() 145 | } 146 | } 147 | 148 | // Infrastructure for working with a side of the range that has a 149 | // single sign, i.e. -x to -0.0 or +0.0 to +y. Loops using these types 150 | // have no branches other than the loop condition, and compile done to 151 | // a plain old C-style `for (x = ...; x != y; x++)` (or 152 | // x--. statically determined). 153 | 154 | trait Sign { 155 | fn to_pos_inf(x: B) -> B; 156 | fn to_neg_inf(x: B) -> B; 157 | 158 | fn dist(from: B, to: B) -> u64; 159 | 160 | } 161 | #[derive(Clone, Eq, PartialEq, Debug)] 162 | struct Positive; 163 | #[derive(Clone, Eq, PartialEq, Debug)] 164 | struct Negative; 165 | 166 | impl Sign for Positive { 167 | fn to_pos_inf(x: B) -> B { x.next() } 168 | fn to_neg_inf(x: B) -> B { x.prev() } 169 | 170 | fn dist(from: B, to: B) -> u64 { 171 | to.as_u64() - from.as_u64() 172 | } 173 | } 174 | impl Sign for Negative { 175 | fn to_pos_inf(x: B) -> B { x.prev() } 176 | fn to_neg_inf(x: B) -> B { x.next() } 177 | 178 | fn dist(from: B, to: B) -> u64 { 179 | // sign-magnitude has the order reversed when negative 180 | from.as_u64() - to.as_u64() 181 | } 182 | } 183 | 184 | #[derive(Clone, Eq, PartialEq, Debug)] 185 | struct SingleSignIter { 186 | from: T::Bits, 187 | to: T::Bits, 188 | _sign: S, 189 | } 190 | 191 | impl SingleSignIter { 192 | fn len(&self) -> u64 { 193 | S::dist(self.from, self.to) 194 | } 195 | 196 | fn done(&self) -> bool { 197 | self.from == self.to 198 | } 199 | 200 | fn try_fold_result(&mut self, mut value: B, mut f: F) -> Result 201 | where F: FnMut(B, T) -> Result { 202 | let SingleSignIter { mut from, to, .. } = *self; 203 | while from != to { 204 | let this = T::from_bits(from); 205 | from = S::to_pos_inf(from); 206 | value = f(value, this).map_err(|e| { 207 | // save the new state before leaving, since we might iterate again 208 | self.from = from; 209 | e 210 | })?; 211 | } 212 | self.from = from; 213 | Ok(value) 214 | } 215 | 216 | fn try_rfold_result(&mut self, mut value: B, mut f: F) -> Result 217 | where F: FnMut(B, T) -> Result { 218 | let SingleSignIter { from, mut to, .. } = *self; 219 | while from != to { 220 | to = S::to_neg_inf(to); 221 | let this = T::from_bits(to); 222 | value = f(value, this).map_err(|e| { 223 | self.to = to; 224 | e 225 | })?; 226 | } 227 | self.to = to; 228 | Ok(value) 229 | } 230 | } 231 | 232 | impl Iterator for SingleSignIter { 233 | type Item = T; 234 | 235 | fn next(&mut self) -> Option { 236 | if self.from != self.to { 237 | let ret = self.from; 238 | self.from = S::to_pos_inf(ret); 239 | Some(T::from_bits(ret)) 240 | } else { 241 | None 242 | } 243 | } 244 | 245 | fn size_hint(&self) -> (usize, Option) { 246 | u64_to_size_hint(self.len()) 247 | } 248 | 249 | fn fold(mut self, init: B, mut f: F) -> B 250 | where 251 | F: FnMut(B, Self::Item) -> B, 252 | { 253 | enum Void {} 254 | 255 | match self.try_fold_result(init, |b, x| Ok::<_, Void>(f(b, x))) { 256 | Ok(result) => result, 257 | Err(_) => unreachable!() 258 | } 259 | } 260 | 261 | #[cfg(nightly)] 262 | fn try_fold(&mut self, init: B, mut f: F) -> R 263 | where F: FnMut(B, Self::Item) -> R, 264 | R: Try { 265 | result_to_try(self.try_fold_result(init, |b, x| f(b, x).into_result())) 266 | } 267 | } 268 | 269 | impl DoubleEndedIterator for SingleSignIter { 270 | fn next_back(&mut self) -> Option { 271 | if self.from != self.to { 272 | self.to = S::to_neg_inf(self.to); 273 | Some(T::from_bits(self.to)) 274 | } else { 275 | None 276 | } 277 | } 278 | 279 | #[cfg(nightly)] 280 | fn try_rfold(&mut self, init: B, mut f: F) -> R 281 | where F: FnMut(B, Self::Item) -> R, 282 | R: Try { 283 | result_to_try(self.try_fold_result(init, |b, x| f(b, x).into_result())) 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/iter/rayon.rs: -------------------------------------------------------------------------------- 1 | extern crate rayon; 2 | 3 | use {Ieee754, Bits}; 4 | use super::{Iter, SingleSignIter}; 5 | use self::rayon::iter::*; 6 | use self::rayon::iter::plumbing::*; 7 | 8 | /// A parallel iterator over floating point numbers, created by 9 | /// `into_par_iter` on `Iter`. 10 | pub struct ParIter { 11 | range: Iter 12 | } 13 | 14 | struct IterProducer { 15 | range: Iter, 16 | } 17 | 18 | impl IntoParallelIterator for Iter { 19 | type Item = T; 20 | type Iter = ParIter; 21 | 22 | fn into_par_iter(self) -> Self::Iter { 23 | ParIter { range: self } 24 | } 25 | } 26 | 27 | impl ParallelIterator for ParIter { 28 | type Item = T; 29 | 30 | fn drive_unindexed(self, consumer: C) -> C::Result 31 | where 32 | C: UnindexedConsumer, 33 | { 34 | bridge_unindexed(IterProducer { range: self.range }, consumer) 35 | } 36 | 37 | fn opt_len(&self) -> Option { 38 | self.range.size_hint().1 39 | } 40 | } 41 | 42 | impl IterProducer { 43 | // it's convenient to think of the split location separately to 44 | // actually doing the split. 45 | fn split_at(self, position: u64) -> (Self, Self) { 46 | // This splits the range in half by count, not by arithmetic 47 | // value. 48 | let len = self.range.len(); 49 | assert!(position <= len); 50 | 51 | let Iter { neg, pos } = self.range; 52 | 53 | let (left, right) = if position < neg.len() { 54 | // the split happens within the negative range 55 | let mid = neg.from.offset(-(position as i64)); 56 | (Iter { 57 | neg: SingleSignIter { to: mid, .. neg.clone() }, 58 | // the positive range is empty 59 | pos: SingleSignIter { to: pos.from, .. pos.clone() }, 60 | }, 61 | Iter { 62 | neg: SingleSignIter { from: mid, .. neg }, 63 | pos: pos 64 | }) 65 | } else { 66 | // the split happens within the positive range (or at the boundary) 67 | let mid = pos.from.offset((position - neg.len()) as i64); 68 | (Iter { 69 | neg: neg.clone(), 70 | pos: SingleSignIter { to: mid, .. pos.clone() }, 71 | }, 72 | Iter { 73 | // the negative range is empty 74 | neg: SingleSignIter { from: neg.to, .. neg }, 75 | pos: SingleSignIter { from: mid, .. pos } 76 | }) 77 | }; 78 | // the ranges should be exactly touching, i.e. last of left is 79 | // immediately before first of right 80 | debug_assert!(left.clone().next_back().map(|x| x.next()) == right.clone().next()); 81 | debug_assert_eq!(left.len(), position); 82 | debug_assert_eq!(right.len(), len - position); 83 | (IterProducer { range: left }, IterProducer { range: right }) 84 | } 85 | } 86 | 87 | impl UnindexedProducer for IterProducer { 88 | type Item = T; 89 | 90 | fn split(self) -> (Self, Option) { 91 | if self.range.len() <= 1 { 92 | (self, None) 93 | } else { 94 | // left-bias by rounding up the position (e.g. if self.len() 95 | // == 5, we want (3, 2), not (2, 3)). 96 | let position = (self.range.len() + 1) / 2; 97 | let (left, right) = self.split_at(position); 98 | (left, Some(right)) 99 | } 100 | } 101 | 102 | fn fold_with(self, neg_folder: F) -> F 103 | where F: Folder { 104 | // consume the two signs separately, to minimise branching 105 | let Iter { neg, pos } = self.range; 106 | 107 | let pos_folder = neg_folder.consume_iter(neg); 108 | if pos_folder.full() { 109 | pos_folder 110 | } else { 111 | pos_folder.consume_iter(pos) 112 | } 113 | } 114 | } 115 | 116 | #[cfg(test)] 117 | mod tests { 118 | use super::*; 119 | use core::{f32, f64}; 120 | use std::vec::Vec; 121 | 122 | fn split_test(from: T, to: T, mid: T) { 123 | let range = from.upto(to); 124 | let len = range.len(); 125 | let (left, right) = IterProducer { range: range }.split(); 126 | let right = right.unwrap(); 127 | assert_eq!(left.range.len(), (len + 1) / 2); 128 | assert_eq!(right.range.len(), len / 2); 129 | assert_eq!(left.range, from.upto(mid)); 130 | assert_eq!(right.range, mid.next().upto(to)); 131 | } 132 | #[test] 133 | fn test_split_zero() { 134 | split_test(0f32, 1.0, 8.131516e-20); 135 | split_test(-1f32, 0.0, -8.131516e-20); 136 | split_test(0f64, 1.0, 1.118751109680031e-154); 137 | split_test(-1f64, 0.0, -1.118751109680031e-154); 138 | 139 | split_test(0f32, f32::INFINITY, 1.5); 140 | split_test(f32::NEG_INFINITY, 0.0, -1.5); 141 | split_test(0f64, f64::INFINITY, 1.5); 142 | split_test(f64::NEG_INFINITY, 0.0, -1.5); 143 | } 144 | #[test] 145 | fn test_split_same_sign() { 146 | let x = f32::consts::PI; 147 | split_test(x, x.next(), x); 148 | split_test(x, x.next().next(), x.next()); 149 | split_test(x, x.next().next().next(), x.next()); 150 | split_test(-x.next(), -x, -x.next()); 151 | split_test(-x.next().next(), -x, -x.next()); 152 | split_test(-x.next().next().next(), -x, -x.next().next()); 153 | 154 | let y = f64::consts::PI; 155 | split_test(y, y.next(), y); 156 | split_test(y, y.next().next(), y.next()); 157 | split_test(y, y.next().next().next(), y.next()); 158 | split_test(-y.next(), -y, -y.next()); 159 | split_test(-y.next().next(), -y, -y.next()); 160 | split_test(-y.next().next().next(), -y, -y.next().next()); 161 | 162 | // one binade 163 | split_test(1f32, 2.0, 1.5); 164 | split_test(-2f32, -1.0, -1.5); 165 | split_test(1f64, 2.0, 1.5); 166 | split_test(-2f64, -1.0, -1.5); 167 | 168 | // cross binade (manually computed by average raw representations) 169 | split_test(1e-10_f32, 1e20, 100703.914); 170 | split_test(-1e20_f32, -1e-10, -100703.92); 171 | split_test(1e-10_f64, 1e20, 100703.91632713746); 172 | split_test(-1e20_f64, -1e-10, -100703.91632713747); 173 | } 174 | 175 | #[test] 176 | fn test_split_awkward_nonsplit() { 177 | fn t(x: f32) { 178 | split_test(x, x.next(), x); 179 | } 180 | t(-94618940000.0); 181 | t(-34652824.0); 182 | t(-34652790.0); 183 | t(-23.812748); 184 | t(-23.81274); 185 | t(-0.0000000000000069391306); 186 | t(-0.00000000000000000027740148); 187 | t(-0.00000000000000000027740024); 188 | t(-0.00000000000000000022259177); 189 | t(-0.00000000000000000022259156); 190 | t(-0.0000000000000000002195783); 191 | t(-0.0000000000000000000000000004100601); 192 | t(-0.00000000000000000000000000040458713); 193 | t(-0.0000000000000000000000000004045599); 194 | } 195 | 196 | #[test] 197 | fn test_split_different_signs_equal() { 198 | split_test(-1f32, 1.0, 0.0); 199 | split_test(-1f64, 1.0, 0.0); 200 | split_test(f32::NEG_INFINITY, f32::INFINITY, 0.0); 201 | split_test(f64::NEG_INFINITY, f64::INFINITY, 0.0); 202 | } 203 | 204 | #[test] 205 | fn test_split_different_signs_nonequal() { 206 | split_test(-2f32, 1.0, -5.877472e-39); 207 | split_test(-1f32, 2.0, 5.877472e-39); 208 | split_test(-2f64, 1.0, -1.1125369292536007e-308); 209 | split_test(-1f64, 2.0, 1.1125369292536007e-308); 210 | 211 | split_test(f32::NEG_INFINITY, 1.0, -1.0842022e-19); 212 | split_test(-1f32, f32::INFINITY, 1.0842022e-19); 213 | split_test(f64::NEG_INFINITY, 1.0, -1.4916681462400413e-154); 214 | split_test(-1.0, f64::INFINITY, 1.4916681462400413e-154); 215 | } 216 | fn split_fail_test(range: Iter) { 217 | let (left, right) = IterProducer { range: range.clone() }.split(); 218 | assert_eq!(left.range, range); 219 | assert!(right.is_none()); 220 | } 221 | #[test] 222 | fn test_split_tiny() { 223 | fn t(x: T) { 224 | split_fail_test(x.upto(x)); 225 | } 226 | t(0_f32); 227 | t(0_f64); 228 | 229 | t(1_f32); 230 | t(1_f64); 231 | 232 | t(-1_f32); 233 | t(-1_f64); 234 | } 235 | 236 | #[test] 237 | fn test_split_done() { 238 | fn t(from: T, to: T) { 239 | let mut range = from.upto(to); 240 | range.by_ref().for_each(|_| {}); 241 | split_fail_test(range) 242 | } 243 | t(1_f32, 1.0001); 244 | t(1_f64, 1.00000000001); 245 | 246 | t(-1.0001_f32, -1.0); 247 | t(-1.00000000001_f64, -1.0); 248 | } 249 | 250 | #[test] 251 | fn test_iterate() { 252 | fn t(from: T, to: T) { 253 | // the elements yielded by the parallel iterate should be 254 | // the same as the non-parallel ones 255 | let mut parallel = from.upto(to).into_par_iter() 256 | .fold_with( 257 | vec![], 258 | |mut v, item| { v.push(item); v } 259 | ) 260 | .reduce_with(|mut x, mut y| { x.append(&mut y); x }) 261 | .unwrap(); 262 | 263 | parallel.sort_by(|x, y| x.partial_cmp(&y).unwrap()); 264 | 265 | let serial = from.upto(to).collect::>(); 266 | assert_eq!(parallel, serial); 267 | } 268 | 269 | t(1_f32, 1.0001); 270 | t(1_f64, 1.00000000001); 271 | t(-1.0001_f32, -1.0); 272 | t(-1.00000000001_f64, -1.0); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Low-level manipulations of IEEE754 floating-point numbers. 2 | //! 3 | //! # Installation 4 | //! 5 | //! Add this to your Cargo.toml: 6 | //! 7 | //! ```toml 8 | //! [dependencies] 9 | //! ieee754 = "0.2" 10 | //! ``` 11 | //! 12 | //! To enable `rayon` parallel iteration, activate the optional 13 | //! `rayon` feature: 14 | //! 15 | //! ```toml 16 | //! [dependencies] 17 | //! ieee754 = { version = "0.2", features = ["rayon"] } 18 | //! ``` 19 | //! 20 | //! # Examples 21 | //! 22 | //! ```rust 23 | //! use ieee754::Ieee754; 24 | //! 25 | //! // there are 840 single-precision floats between 1.0 and 1.0001 26 | //! // (inclusive). 27 | //! assert_eq!(1_f32.upto(1.0001).count(), 840); 28 | //! ``` 29 | //! 30 | //! If `rayon` is enabled, this can be performed in parallel: 31 | //! 32 | //! ```rust 33 | //! extern crate ieee754; 34 | //! # #[cfg(feature = "rayon")] 35 | //! extern crate rayon; 36 | //! 37 | //! # #[cfg(feature = "rayon")] 38 | //! # fn main() { 39 | //! use ieee754::Ieee754; 40 | //! use rayon::prelude::*; 41 | //! 42 | //! // there are 840 single-precision floats between 1.0 and 1.0001 43 | //! // (inclusive). 44 | //! assert_eq!(1_f32.upto(1.0001).into_par_iter().count(), 840); 45 | //! # } 46 | //! # #[cfg(not(feature = "rayon"))] fn main() {} 47 | //! ``` 48 | 49 | #![no_std] 50 | #![cfg_attr(nightly, feature(try_trait))] 51 | #[cfg(test)] #[macro_use] extern crate std; 52 | 53 | mod iter; 54 | mod impls; 55 | mod traits; 56 | 57 | pub use traits::{Bits, Ieee754}; 58 | pub use iter::Iter; 59 | 60 | #[cfg(feature = "rayon")] 61 | pub use iter::rayon::ParIter; 62 | 63 | #[inline] 64 | #[doc(hidden)] 65 | pub fn abs(x: F) -> F { 66 | x.abs() 67 | } 68 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::Ordering; 2 | use core::fmt; 3 | use core::i32; 4 | use Iter; 5 | 6 | pub trait Bits: Eq + PartialEq + PartialOrd + Ord + Copy + Send + Sync { 7 | fn as_u64(self) -> u64; 8 | 9 | fn zero() -> Self; 10 | fn imin() -> Self; 11 | fn high(self) -> bool; 12 | 13 | fn next(self) -> Self; 14 | fn prev(self) -> Self; 15 | 16 | fn offset(self, offset: i64) -> Self; 17 | } 18 | impl Bits for u32 { 19 | #[inline] 20 | fn as_u64(self) -> u64 { self as u64 } 21 | 22 | #[inline] 23 | fn zero() -> Self { 24 | 0 25 | } 26 | #[inline] 27 | fn imin() -> Self { 28 | 1 << 31 29 | } 30 | 31 | #[inline] 32 | fn high(self) -> bool { 33 | self & (1 << 31) != 0 34 | } 35 | 36 | #[inline] 37 | fn next(self) -> Self { self + 1 } 38 | #[inline] 39 | fn prev(self) -> Self { self - 1 } 40 | 41 | #[inline] 42 | fn offset(self, offset: i64) -> Self { 43 | debug_assert!(i32::MIN as i64 <= offset && offset <= i32::MAX as i64); 44 | self.wrapping_add(offset as u32) 45 | } 46 | } 47 | impl Bits for u64 { 48 | #[inline] 49 | fn as_u64(self) -> u64 { self } 50 | 51 | #[inline] 52 | fn zero() -> Self { 53 | 0 54 | } 55 | #[inline] 56 | fn imin() -> Self { 57 | 1 << 63 58 | } 59 | 60 | #[inline] 61 | fn high(self) -> bool { 62 | self & (1 << 63) != 0 63 | } 64 | 65 | #[inline] 66 | fn next(self) -> Self { self + 1 } 67 | #[inline] 68 | fn prev(self) -> Self { self - 1 } 69 | 70 | #[inline] 71 | fn offset(self, offset: i64) -> Self { 72 | self.wrapping_add(offset as u64) 73 | } 74 | } 75 | 76 | /// Types that are IEEE754 floating point numbers. 77 | pub trait Ieee754: Copy + PartialEq + PartialOrd + Send + Sync + fmt::Debug + fmt::Display { 78 | /// Iterate over each value of `Self` in `[self, lim]`. 79 | /// 80 | /// The returned iterator will include subnormal numbers, and will 81 | /// only include one of `-0.0` and `0.0`. 82 | /// 83 | /// # Panics 84 | /// 85 | /// Panics if `self > lim`, or if either are NaN. 86 | /// 87 | /// # Examples 88 | /// 89 | /// ```rust 90 | /// use ieee754::Ieee754; 91 | /// 92 | /// // there are 840 single-precision floats in between 1.0 and 1.0001 93 | /// // (inclusive). 94 | /// assert_eq!(1_f32.upto(1.0001).count(), 840); 95 | /// ``` 96 | fn upto(self, lim: Self) -> Iter; 97 | 98 | /// A type that represents the raw bits of `Self`. 99 | type Bits: Bits; 100 | /// A type large enough to store the true exponent of `Self`. 101 | type Exponent; 102 | /// A type large enough to store the raw exponent (i.e. with the bias). 103 | type RawExponent; 104 | /// A type large enough to store the significand of `Self`. 105 | type Significand; 106 | 107 | /// Return the next value after `self`. 108 | /// 109 | /// Calling this on NaN or positive infinity will yield nonsense. 110 | /// 111 | /// # Examples 112 | /// 113 | /// ```rust 114 | /// use ieee754::Ieee754; 115 | /// let x: f32 = 1.0; 116 | /// assert_eq!(x.next(), 1.000000119209); 117 | /// ``` 118 | fn next(self) -> Self; 119 | 120 | /// Return the previous value before `self`. 121 | /// 122 | /// Calling this on NaN or negative infinity will yield nonsense. 123 | /// 124 | /// # Examples 125 | /// 126 | /// ```rust 127 | /// use ieee754::Ieee754; 128 | /// let x: f32 = 1.0; 129 | /// assert_eq!(x.prev(), 0.99999995); 130 | /// ``` 131 | fn prev(self) -> Self; 132 | 133 | /// Return the unit-in-the-last-place ulp of `self`. That is, 134 | /// `x.abs().next() - x.abs()`, but handling overflow properly. 135 | /// 136 | /// Returns `None` if `self` is not finite. 137 | /// 138 | /// # Examples 139 | /// 140 | /// Single precision: 141 | /// 142 | /// ```rust 143 | /// use std::f32; 144 | /// use ieee754::Ieee754; 145 | /// 146 | /// assert_eq!(0_f32.ulp(), Some(1.4e-45)); 147 | /// 148 | /// assert_eq!(1_f32.ulp(), Some(1.1920928955078125e-07)); 149 | /// assert_eq!((-1_f32).ulp(), Some(1.1920928955078125e-07)); 150 | /// 151 | /// // 2^23 152 | /// assert_eq!(8_388_608_f32.ulp(), Some(1.0)); 153 | /// // 2^24 - 1, the largest f32 with ULP 1 154 | /// assert_eq!(16_777_215_f32.ulp(), Some(1.0)); 155 | /// // 2^24 156 | /// assert_eq!(16_777_216_f32.ulp(), Some(2.0)); 157 | /// 158 | /// // non-finite 159 | /// assert_eq!(f32::INFINITY.ulp(), None); 160 | /// assert_eq!(f32::NAN.ulp(), None); 161 | /// ``` 162 | /// 163 | /// Double precision: 164 | /// 165 | /// ```rust 166 | /// use std::f64; 167 | /// use ieee754::Ieee754; 168 | /// 169 | /// assert_eq!(0_f64.ulp(), Some(4.9e-324)); 170 | /// 171 | /// assert_eq!(1_f64.ulp(), Some(2.220446049250313e-16)); 172 | /// assert_eq!((-1_f64).ulp(), Some(2.220446049250313e-16)); 173 | /// 174 | /// // 2^52 175 | /// assert_eq!(4_503_599_627_370_496_f64.ulp(), Some(1.0)); 176 | /// // 2^53 - 1, the largest f64 with ULP 1 177 | /// assert_eq!(9_007_199_254_740_991_f64.ulp(), Some(1.0)); 178 | /// // 2^53 179 | /// assert_eq!(9_007_199_254_740_992_f64.ulp(), Some(2.0)); 180 | /// 181 | /// // non-finite 182 | /// assert_eq!(f64::INFINITY.ulp(), None); 183 | /// assert_eq!(f64::NAN.ulp(), None); 184 | /// ``` 185 | fn ulp(self) -> Option; 186 | 187 | /// View `self` as a collection of bits. 188 | /// 189 | /// # Examples 190 | /// 191 | /// ```rust 192 | /// use ieee754::Ieee754; 193 | /// let x: f32 = 1.0; 194 | /// assert_eq!(x.bits(), 0x3f80_0000); 195 | /// ``` 196 | fn bits(self) -> Self::Bits; 197 | 198 | /// View a collections of bits as a floating point number. 199 | /// 200 | /// # Examples 201 | /// 202 | /// ```rust 203 | /// use ieee754::Ieee754; 204 | /// let float: f32 = Ieee754::from_bits(0xbf80_0000); 205 | /// assert_eq!(float, -1.0); 206 | /// ``` 207 | fn from_bits(x: Self::Bits) -> Self; 208 | 209 | /// Get the bias of the stored exponent. 210 | /// 211 | /// # Examples 212 | /// 213 | /// ```rust 214 | /// use ieee754::Ieee754; 215 | /// 216 | /// assert_eq!(f32::exponent_bias(), 127); 217 | /// assert_eq!(f64::exponent_bias(), 1023); 218 | /// ``` 219 | fn exponent_bias() -> Self::Exponent; 220 | 221 | /// Break `self` into the three constituent parts of an IEEE754 float. 222 | /// 223 | /// The exponent returned is the raw bits, use `exponent_bias` to 224 | /// compute the offset required or use `decompose` to obtain this 225 | /// in precomputed form. 226 | /// 227 | /// # Examples 228 | /// 229 | /// Single precision: 230 | /// 231 | /// ```rust 232 | /// use ieee754::Ieee754; 233 | /// 234 | /// assert_eq!(1_f32.decompose_raw(), (false, 127, 0)); 235 | /// assert_eq!(1234.567_f32.decompose_raw(), (false, 137, 0x1a5225)); 236 | /// 237 | /// assert_eq!((-0.525_f32).decompose_raw(), (true, 126, 0x66666)); 238 | /// 239 | /// assert_eq!(std::f32::INFINITY.decompose_raw(), (false, 255, 0)); 240 | /// 241 | /// let (sign, expn, signif) = std::f32::NAN.decompose_raw(); 242 | /// assert_eq!((sign, expn), (false, 255)); 243 | /// assert!(signif != 0); 244 | /// ``` 245 | /// 246 | /// Double precision: 247 | /// 248 | /// ```rust 249 | /// use ieee754::Ieee754; 250 | /// 251 | /// assert_eq!(1_f64.decompose_raw(), (false, 1023, 0)); 252 | /// assert_eq!(1234.567_f64.decompose_raw(), (false, 1033, 0x34a449ba5e354)); 253 | /// 254 | /// assert_eq!((-0.525_f64).decompose_raw(), (true, 1022, 0xcccc_cccc_cccd)); 255 | /// 256 | /// assert_eq!(std::f64::INFINITY.decompose_raw(), (false, 2047, 0)); 257 | /// 258 | /// let (sign, expn, signif) = std::f64::NAN.decompose_raw(); 259 | /// assert_eq!((sign, expn), (false, 2047)); 260 | /// assert!(signif != 0); 261 | /// ``` 262 | fn decompose_raw(self) -> (bool, Self::RawExponent, Self::Significand); 263 | 264 | /// Create a `Self` out of the three constituent parts of an IEEE754 float. 265 | /// 266 | /// This returns (-1)sign × 267 | /// 1.signif × 2expn - bias, where 268 | /// 269 | /// - `sign` is treated as if `true` == `1` (meaning `true` is 270 | /// negative), 271 | /// - 1.signif refers to placing the bits of `signif` 272 | /// as the fractional part of a number between 1 and 2, and 273 | /// - bias is the exponent bias for this float (see [`exponent_bias`]). 274 | /// 275 | /// The exponent should be the raw bits: use `exponent_bias` to 276 | /// compute the offset required, or use `recompose` to feed in an 277 | /// unbiased exponent. 278 | /// 279 | /// # Examples 280 | /// 281 | /// Single precision: 282 | /// 283 | /// ```rust 284 | /// use ieee754::Ieee754; 285 | /// 286 | /// assert_eq!(f32::recompose_raw(false, 127, 0), 1.0); 287 | /// assert_eq!(f32::recompose_raw(false, 137, 0x1a5225), 1234.567); 288 | /// assert_eq!(f32::recompose_raw(true, 126, 0x66666), -0.525); 289 | /// 290 | /// assert_eq!(f32::recompose_raw(false, 255, 0), std::f32::INFINITY); 291 | /// 292 | /// assert!(f32::recompose_raw(false, 255, 1).is_nan()); 293 | /// ``` 294 | /// 295 | /// Double precision: 296 | /// 297 | /// ```rust 298 | /// use ieee754::Ieee754; 299 | /// 300 | /// assert_eq!(f64::recompose_raw(false, 1023, 0), 1.0); 301 | /// assert_eq!(f64::recompose_raw(false, 1033, 0x34a449ba5e354), 1234.567); 302 | /// assert_eq!(f64::recompose_raw(true, 1022, 0xcccc_cccc_cccd), -0.525); 303 | /// 304 | /// assert_eq!(f64::recompose_raw(false, 2047, 0), std::f64::INFINITY); 305 | /// 306 | /// assert!(f64::recompose_raw(false, 2047, 1).is_nan()); 307 | /// ``` 308 | fn recompose_raw(sign: bool, expn: Self::RawExponent, signif: Self::Significand) -> Self; 309 | 310 | /// Break `self` into the three constituent parts of an IEEE754 float. 311 | /// 312 | /// The exponent returned is the true exponent, after accounting 313 | /// for the bias it is stored with. The significand does not 314 | /// include the implicit highest bit (if it exists), e.g. the 315 | /// 24-bit for single precision. 316 | /// 317 | /// # Examples 318 | /// 319 | /// Single precision: 320 | /// 321 | /// ```rust 322 | /// use ieee754::Ieee754; 323 | /// 324 | /// assert_eq!(1_f32.decompose(), (false, 0, 0)); 325 | /// assert_eq!(1234.567_f32.decompose(), (false, 10, 0x1a5225)); 326 | /// 327 | /// assert_eq!((-0.525_f32).decompose(), (true, -1, 0x66666)); 328 | /// 329 | /// assert_eq!(std::f32::INFINITY.decompose(), (false, 128, 0)); 330 | /// 331 | /// let (sign, expn, signif) = std::f32::NAN.decompose(); 332 | /// assert_eq!((sign, expn), (false, 128)); 333 | /// assert!(signif != 0); 334 | /// ``` 335 | /// 336 | /// Double precision: 337 | /// 338 | /// ```rust 339 | /// use ieee754::Ieee754; 340 | /// 341 | /// assert_eq!(1_f64.decompose(), (false, 0, 0)); 342 | /// assert_eq!(1234.567_f64.decompose(), (false, 10, 0x34a449ba5e354)); 343 | /// 344 | /// assert_eq!((-0.525_f64).decompose(), (true, -1, 0xcccc_cccc_cccd)); 345 | /// 346 | /// assert_eq!(std::f64::INFINITY.decompose(), (false, 1024, 0)); 347 | /// 348 | /// let (sign, expn, signif) = std::f64::NAN.decompose(); 349 | /// assert_eq!((sign, expn), (false, 1024)); 350 | /// assert!(signif != 0); 351 | /// ``` 352 | fn decompose(self) -> (bool, Self::Exponent, Self::Significand); 353 | 354 | /// Create a `Self` out of the three constituent parts of an IEEE754 float. 355 | /// 356 | /// This returns (-1)sign × 357 | /// 1.signif × 2expn, where 358 | /// 359 | /// - `sign` is treated as if `true` == `1` (meaning `true` is 360 | /// negative), and 361 | /// - 1.signif refers to placing the bits of `signif` 362 | /// as the fractional part of a number between 1 and 2. 363 | /// 364 | /// The exponent should be the true exponent, not accounting for any 365 | /// bias. The significand should not include the implicit highest 366 | /// bit (if it exists), e.g. the 24-th bit for single precision. 367 | /// 368 | /// # Examples 369 | /// 370 | /// Single precision: 371 | /// 372 | /// ```rust 373 | /// use ieee754::Ieee754; 374 | /// 375 | /// // normal numbers 376 | /// assert_eq!(f32::recompose(false, 0, 0), 1.0); 377 | /// assert_eq!(f32::recompose(false, 10, 0x1a5225), 1234.567); 378 | /// assert_eq!(f32::recompose(true, -1, 0x66666), -0.525); 379 | /// 380 | /// // infinity 381 | /// assert_eq!(f32::recompose(false, 128, 0), std::f32::INFINITY); 382 | /// 383 | /// // NaN 384 | /// assert!(f32::recompose(false, 128, 1).is_nan()); 385 | /// ``` 386 | /// 387 | /// Double precision: 388 | /// 389 | /// ```rust 390 | /// use ieee754::Ieee754; 391 | /// 392 | /// // normal numbers 393 | /// assert_eq!(f64::recompose(false, 0, 0), 1.0); 394 | /// assert_eq!(f64::recompose(false, 10, 0x34a449ba5e354), 1234.567); 395 | /// assert_eq!(f64::recompose(true, -1, 0xcccc_cccc_cccd), -0.525); 396 | /// 397 | /// // infinity 398 | /// assert_eq!(f64::recompose(false, 1024, 0), std::f64::INFINITY); 399 | /// 400 | /// // NaN 401 | /// assert!(f64::recompose(false, 1024, 1).is_nan()); 402 | /// ``` 403 | fn recompose(sign: bool, expn: Self::Exponent, signif: Self::Significand) -> Self; 404 | 405 | /// Compare `x` and `y` using the IEEE-754 `totalOrder` predicate 406 | /// (Section 5.10). 407 | /// 408 | /// This orders NaNs before or after all non-NaN floats, depending 409 | /// on the sign bit. Using -qNaN to represent a quiet NaN with 410 | /// negative sign bit and similarly for a signalling NaN (sNaN), 411 | /// the order is: 412 | /// 413 | /// ```txt 414 | /// -qNaN < -sNaN < -∞ < -12.34 < -0.0 < +0.0 < +12.34 < +∞ < +sNaN < +qNaN 415 | /// ``` 416 | /// 417 | /// (NaNs are ordered according to their payload.) 418 | /// 419 | /// # Examples 420 | /// 421 | /// Sorting: 422 | /// 423 | /// ```rust 424 | /// use std::f32; 425 | /// 426 | /// use ieee754::Ieee754; 427 | /// 428 | /// let mut data = vec![0.0, f32::NEG_INFINITY, -1.0, f32::INFINITY, 429 | /// f32::NAN, -0.0, 12.34e5, -f32::NAN]; 430 | /// data.sort_by(|a, b| a.total_cmp(b)); 431 | /// 432 | /// assert_eq!(format!("{:.0?}", data), 433 | /// "[NaN, -inf, -1, -0, 0, 1234000, inf, NaN]"); 434 | /// ``` 435 | /// 436 | /// Single precision: 437 | /// 438 | /// ```rust 439 | /// use std::cmp::Ordering; 440 | /// use std::f32; 441 | /// 442 | /// use ieee754::Ieee754; 443 | /// 444 | /// // normal comparison 445 | /// assert_eq!(0_f32.total_cmp(&0_f32), Ordering::Equal); 446 | /// assert_eq!(0_f32.total_cmp(&1_f32), Ordering::Less); 447 | /// assert_eq!(1e10_f32.total_cmp(&f32::NEG_INFINITY), Ordering::Greater); 448 | /// 449 | /// // signed zero 450 | /// assert_eq!(0_f32.total_cmp(&-0_f32), Ordering::Greater); 451 | /// 452 | /// // NaNs 453 | /// assert_eq!(f32::NAN.total_cmp(&0_f32), Ordering::Greater); 454 | /// assert_eq!(f32::NAN.total_cmp(&f32::INFINITY), Ordering::Greater); 455 | /// assert_eq!((-f32::NAN).total_cmp(&f32::NEG_INFINITY), Ordering::Less); 456 | /// ``` 457 | /// 458 | /// Double precision: 459 | /// 460 | /// ```rust 461 | /// use std::cmp::Ordering; 462 | /// use std::f64; 463 | /// 464 | /// use ieee754::Ieee754; 465 | /// 466 | /// // normal comparison 467 | /// assert_eq!(0_f64.total_cmp(&0_f64), Ordering::Equal); 468 | /// assert_eq!(0_f64.total_cmp(&1_f64), Ordering::Less); 469 | /// assert_eq!(1e10_f64.total_cmp(&f64::NEG_INFINITY), Ordering::Greater); 470 | /// 471 | /// // signed zero 472 | /// assert_eq!(0_f64.total_cmp(&-0_f64), Ordering::Greater); 473 | /// 474 | /// // NaNs 475 | /// assert_eq!(f64::NAN.total_cmp(&0_f64), Ordering::Greater); 476 | /// assert_eq!(f64::NAN.total_cmp(&f64::INFINITY), Ordering::Greater); 477 | /// assert_eq!((-f64::NAN).total_cmp(&f64::NEG_INFINITY), Ordering::Less); 478 | /// ``` 479 | fn total_cmp(&self, other: &Self) -> Ordering; 480 | 481 | /// Return the absolute value of `x`. 482 | /// 483 | /// This provides a no_std/core-only version of the built-in `abs` in 484 | /// `std`, until 485 | /// [#50145](https://github.com/rust-lang/rust/issues/50145) is 486 | /// addressed. 487 | /// 488 | /// # Examples 489 | /// 490 | /// Single precision: 491 | /// 492 | /// ```rust 493 | /// #![no_std] 494 | /// # extern crate std; // this makes this "test" a lie, unfortunately 495 | /// # extern crate ieee754; 496 | /// use core::f32; 497 | /// 498 | /// use ieee754::Ieee754; 499 | /// 500 | /// # fn main() { 501 | /// assert_eq!((0_f32).abs(), 0.0); 502 | /// 503 | /// assert_eq!((12.34_f32).abs(), 12.34); 504 | /// assert_eq!((-12.34_f32).abs(), 12.34); 505 | /// 506 | /// assert_eq!(f32::INFINITY.abs(), f32::INFINITY); 507 | /// assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); 508 | /// assert!(f32::NAN.abs().is_nan()); 509 | /// # } 510 | /// ``` 511 | /// 512 | /// Double precision: 513 | /// 514 | /// ```rust 515 | /// #![no_std] 516 | /// # extern crate std; // this makes this "test" a lie, unfortunately 517 | /// # extern crate ieee754; 518 | /// use core::f64; 519 | /// 520 | /// use ieee754::Ieee754; 521 | /// 522 | /// # fn main() { 523 | /// assert_eq!((0_f64).abs(), 0.0); 524 | /// 525 | /// assert_eq!((12.34_f64).abs(), 12.34); 526 | /// assert_eq!((-12.34_f64).abs(), 12.34); 527 | /// 528 | /// assert_eq!(f64::INFINITY.abs(), f64::INFINITY); 529 | /// assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); 530 | /// assert!(f64::NAN.abs().is_nan()); 531 | /// # } 532 | /// ``` 533 | fn abs(self) -> Self; 534 | 535 | /// Return a float with the magnitude of `self` but the sign of 536 | /// `sign`. 537 | /// 538 | /// If `sign` is NaN, this still uses its sign bit, and does not 539 | /// (necessarily) return NaN. 540 | /// 541 | /// # Examples 542 | /// 543 | /// Single precision: 544 | /// 545 | /// ```rust 546 | /// use std::f32; 547 | /// 548 | /// use ieee754::Ieee754; 549 | /// 550 | /// // normal numbers 551 | /// assert_eq!(1_f32.copy_sign(1.0), 1.0); 552 | /// assert_eq!(2_f32.copy_sign(-1.0), -2.0); 553 | /// assert_eq!((-3_f32).copy_sign(1.0), 3.0); 554 | /// assert_eq!((-4_f32).copy_sign(-1.0), -4.0); 555 | /// 556 | /// // infinities 557 | /// assert_eq!(5_f32.copy_sign(f32::NEG_INFINITY), -5.0); 558 | /// assert_eq!(f32::NEG_INFINITY.copy_sign(1.0), f32::INFINITY); 559 | /// 560 | /// // signs of zeros matter 561 | /// assert_eq!((-6_f32).copy_sign(0.0), 6.0); 562 | /// assert_eq!(7_f32.copy_sign(-0.0), -7.0); 563 | /// 564 | /// // NaNs only propagate on the self argument 565 | /// assert!(f32::NAN.copy_sign(1.0).is_nan()); 566 | /// assert_eq!(8_f32.copy_sign(-f32::NAN), -8.0); 567 | /// ``` 568 | /// 569 | /// Double precision: 570 | /// 571 | /// ```rust 572 | /// use std::f64; 573 | /// 574 | /// use ieee754::Ieee754; 575 | /// 576 | /// // normal numbers 577 | /// assert_eq!(1_f64.copy_sign(1.0), 1.0); 578 | /// assert_eq!(2_f64.copy_sign(-1.0), -2.0); 579 | /// assert_eq!((-3_f64).copy_sign(1.0), 3.0); 580 | /// assert_eq!((-4_f64).copy_sign(-1.0), -4.0); 581 | /// 582 | /// // infinities 583 | /// assert_eq!(5_f64.copy_sign(f64::NEG_INFINITY), -5.0); 584 | /// assert_eq!(f64::NEG_INFINITY.copy_sign(1.0), f64::INFINITY); 585 | /// 586 | /// // signs of zeros matter 587 | /// assert_eq!((-6_f64).copy_sign(0.0), 6.0); 588 | /// assert_eq!(7_f64.copy_sign(-0.0), -7.0); 589 | /// 590 | /// // NaNs only propagate on the self argument 591 | /// assert!(f64::NAN.copy_sign(1.0).is_nan()); 592 | /// assert_eq!(8_f64.copy_sign(-f64::NAN), -8.0); 593 | /// ``` 594 | fn copy_sign(self, sign: Self) -> Self; 595 | 596 | /// Return the sign of `x`. 597 | /// 598 | /// This provides a no_std/core-only function similar to the 599 | /// built-in `signum` in `std` (until 600 | /// [#50145](https://github.com/rust-lang/rust/issues/50145) is 601 | /// addressed). This `sign` function differs at two values; it 602 | /// matches the mathematical definitions when `self == 0.0` : 603 | /// 604 | /// | `x` | `x.signum()` (`std`) | `x.sign()` (`ieee754`) | 605 | /// |--:|--:|--:| 606 | /// |< 0.0|−1.0|−1.0| 607 | /// |−0.0|−1.0|**−0.0**| 608 | /// |+0.0|+1.0|**+0.0**| 609 | /// |> 0.0|+1.0|+1.0| 610 | /// |NaN|NaN|NaN| 611 | /// 612 | /// # Examples 613 | /// 614 | /// Single precision: 615 | /// 616 | /// ```rust 617 | /// use std::f32; 618 | /// use std::cmp::Ordering; 619 | /// 620 | /// use ieee754::Ieee754; 621 | /// 622 | /// // zeros 623 | /// assert_eq!(0_f32.sign().total_cmp(&0.0), Ordering::Equal); 624 | /// assert_eq!((-0_f32).sign().total_cmp(&-0.0), Ordering::Equal); 625 | /// 626 | /// // normal numbers 627 | /// assert_eq!((12.34_f32).sign(), 1.0); 628 | /// assert_eq!((-12.34_f32).sign(), -1.0); 629 | /// 630 | /// // extremes 631 | /// assert_eq!(f32::INFINITY.sign(), 1.0); 632 | /// assert_eq!(f32::NEG_INFINITY.sign(), -1.0); 633 | /// assert!(f32::NAN.sign().is_nan()); 634 | /// ``` 635 | /// 636 | /// Double precision: 637 | /// 638 | /// ```rust 639 | /// use std::f64; 640 | /// use std::cmp::Ordering; 641 | /// 642 | /// use ieee754::Ieee754; 643 | /// 644 | /// // zeros 645 | /// assert_eq!(0_f64.sign().total_cmp(&0.0), Ordering::Equal); 646 | /// assert_eq!((-0_f64).sign().total_cmp(&-0.0), Ordering::Equal); 647 | /// 648 | /// // normal numbers 649 | /// assert_eq!((12.34_f64).sign(), 1.0); 650 | /// assert_eq!((-12.34_f64).sign(), -1.0); 651 | /// 652 | /// // extremes 653 | /// assert_eq!(f64::INFINITY.sign(), 1.0); 654 | /// assert_eq!(f64::NEG_INFINITY.sign(), -1.0); 655 | /// assert!(f64::NAN.sign().is_nan()); 656 | /// ``` 657 | fn sign(self) -> Self; 658 | 659 | /// Compute the (generalized) **signed** relative error of `self` 660 | /// as an approximation to `exact`. 661 | /// 662 | /// This computes the signed value: positive indicates `self` in 663 | /// the opposite direction to 0 from `exact`; negative indicates 664 | /// `self` is in the same direction as 0 from `exact`. Use 665 | /// `x.rel_error(exact).abs()` to get the non-signed relative 666 | /// error. 667 | /// 668 | /// The "generalized" refers to `exact` being 0 or ±∞ the handling 669 | /// of which is designed to indicate a "failure" (infinite error), 670 | /// if `self` doesn't precisely equal `exact`. This behaviour is 671 | /// designed for checking output of algorithms on floats when it 672 | /// is often desirable to match 0.0 and ±∞ perfectly. 673 | /// 674 | /// The values of this function are: 675 | /// 676 | /// |`exact`|`x`|`x.rel_error(exact)`| 677 | /// |--:|--:|--:| 678 | /// |NaN|any value|NaN| 679 | /// |any value|NaN|NaN| 680 | /// |0|equal to `exact`|0| 681 | /// |0|not equal to `exact`|signum(`x`) × ∞| 682 | /// |±∞|equal to `exact`|0| 683 | /// |±∞|not equal to `exact`|-∞| 684 | /// |any other value|any value|`(x - exact) / exact`| 685 | /// 686 | /// The sign of a zero-valued argument has no effect on the result 687 | /// of this function. 688 | /// 689 | /// # Examples 690 | /// 691 | /// Single precision: 692 | /// 693 | /// ```rust 694 | /// use std::f32; 695 | /// 696 | /// use ieee754::Ieee754; 697 | /// 698 | /// assert_eq!(4_f32.rel_error(4.0), 0.0); 699 | /// assert_eq!(3_f32.rel_error(4.0), -0.25); 700 | /// assert_eq!(5_f32.rel_error(4.0), 0.25); 701 | /// 702 | /// // zero 703 | /// assert_eq!(0_f32.rel_error(0.0), 0.0); 704 | /// assert_eq!(1_f32.rel_error(0.0), f32::INFINITY); 705 | /// assert_eq!((-1_f32).rel_error(0.0), f32::NEG_INFINITY); 706 | /// 707 | /// // infinities 708 | /// assert_eq!(f32::INFINITY.rel_error(f32::INFINITY), 0.0); 709 | /// assert_eq!(0_f32.rel_error(f32::INFINITY), f32::NEG_INFINITY); 710 | /// 711 | /// assert_eq!(f32::NEG_INFINITY.rel_error(f32::NEG_INFINITY), 0.0); 712 | /// assert_eq!(0_f32.rel_error(f32::NEG_INFINITY), f32::NEG_INFINITY); 713 | /// 714 | /// // NaNs 715 | /// assert!(f32::NAN.rel_error(4.0).is_nan()); 716 | /// assert!(4_f32.rel_error(f32::NAN).is_nan()); 717 | /// ``` 718 | /// 719 | /// Double precision: 720 | /// 721 | /// ```rust 722 | /// use std::f64; 723 | /// use ieee754::Ieee754; 724 | /// 725 | /// assert_eq!(4_f64.rel_error(4.0), 0.0); 726 | /// assert_eq!(3_f64.rel_error(4.0), -0.25); 727 | /// assert_eq!(5_f64.rel_error(4.0), 0.25); 728 | /// 729 | /// // zero 730 | /// assert_eq!(0_f64.rel_error(0.0), 0.0); 731 | /// assert_eq!(1_f64.rel_error(0.0), f64::INFINITY); 732 | /// assert_eq!((-1_f64).rel_error(0.0), f64::NEG_INFINITY); 733 | /// 734 | /// // infinities 735 | /// assert_eq!(f64::INFINITY.rel_error(f64::INFINITY), 0.0); 736 | /// assert_eq!(0_f64.rel_error(f64::INFINITY), f64::NEG_INFINITY); 737 | /// 738 | /// assert_eq!(f64::NEG_INFINITY.rel_error(f64::NEG_INFINITY), 0.0); 739 | /// assert_eq!(0_f64.rel_error(f64::NEG_INFINITY), f64::NEG_INFINITY); 740 | /// 741 | /// // NaNs 742 | /// assert!(f64::NAN.rel_error(4.0).is_nan()); 743 | /// assert!(4_f64.rel_error(f64::NAN).is_nan()); 744 | /// ``` 745 | fn rel_error(self, exact: Self) -> Self; 746 | } 747 | --------------------------------------------------------------------------------