Rollup merge of #142489 - tgross35:update-builtins, r=tgross35
Update the `compiler-builtins` subtree Update the Josh subtree to https://github.com/rust-lang/compiler-builtins/commit/7c46e921c117. r? `@ghost`
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
[workspace]
|
||||
# As part of the release process, we delete `libm/Cargo.toml`. Since
|
||||
# this is only run in CI, we shouldn't need to worry about it.
|
||||
allow_dirty = true
|
||||
publish_allow_dirty = true
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
semver_check = false
|
||||
changelog_include = ["libm"] # libm is included as part of builtins
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -1,8 +1,8 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"builtins-shim",
|
||||
"builtins-test",
|
||||
"compiler-builtins",
|
||||
"crates/josh-sync",
|
||||
"crates/libm-macros",
|
||||
"crates/musl-math-sys",
|
||||
@@ -14,8 +14,8 @@ members = [
|
||||
]
|
||||
|
||||
default-members = [
|
||||
"builtins-shim",
|
||||
"builtins-test",
|
||||
"compiler-builtins",
|
||||
"crates/libm-macros",
|
||||
"libm",
|
||||
"libm-test",
|
||||
@@ -26,6 +26,10 @@ exclude = [
|
||||
# and `mangled-names` disabled, which is the opposite of what is needed for
|
||||
# other tests, so it makes sense to keep it out of the workspace.
|
||||
"builtins-test-intrinsics",
|
||||
# We test via the `builtins-shim` crate, so exclude the `compiler-builtins`
|
||||
# that has a dependency on `core`. See `builtins-shim/Cargo.toml` for more
|
||||
# details.
|
||||
"compiler-builtins",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
||||
63
library/compiler-builtins/builtins-shim/Cargo.toml
Normal file
63
library/compiler-builtins/builtins-shim/Cargo.toml
Normal file
@@ -0,0 +1,63 @@
|
||||
# NOTE: Must be kept in sync with `../compiler-builtins/Cargo.toml`.
|
||||
#
|
||||
# The manifest at `../compiler-builtins` is what actually gets used in the
|
||||
# rust-lang/rust tree; however, we can't build it out of tree because it
|
||||
# depends on `core` by path, and even optional Cargo dependencies need to be
|
||||
# available at build time. So, we work around this by having this "shim"
|
||||
# manifest that is identical except for the `core` dependency and forwards
|
||||
# to the same sources, which acts as the `compiler-builtins` Cargo entrypoint
|
||||
# for out of tree testing
|
||||
|
||||
[package]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.160"
|
||||
authors = ["Jorge Aparicio <japaricious@gmail.com>"]
|
||||
description = "Compiler intrinsics used by the Rust compiler."
|
||||
repository = "https://github.com/rust-lang/compiler-builtins"
|
||||
license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
links = "compiler-rt"
|
||||
|
||||
build = "../compiler-builtins/build.rs"
|
||||
|
||||
[lib]
|
||||
path = "../compiler-builtins/src/lib.rs"
|
||||
bench = false
|
||||
doctest = false
|
||||
test = false
|
||||
|
||||
[build-dependencies]
|
||||
cc = { optional = true, version = "1.2" }
|
||||
|
||||
[features]
|
||||
default = ["compiler-builtins"]
|
||||
|
||||
# Enable compilation of C code in compiler-rt, filling in some more optimized
|
||||
# implementations and also filling in unimplemented intrinsics
|
||||
c = ["dep:cc"]
|
||||
|
||||
# Workaround for the Cranelift codegen backend. Disables any implementations
|
||||
# which use inline assembly and fall back to pure Rust versions (if available).
|
||||
no-asm = []
|
||||
|
||||
# Workaround for codegen backends which haven't yet implemented `f16` and
|
||||
# `f128` support. Disabled any intrinsics which use those types.
|
||||
no-f16-f128 = []
|
||||
|
||||
# Flag this library as the unstable compiler-builtins lib
|
||||
compiler-builtins = []
|
||||
|
||||
# Generate memory-related intrinsics like memcpy
|
||||
mem = []
|
||||
|
||||
# Mangle all names so this can be linked in with other versions or other
|
||||
# compiler-rt implementations. Also used for testing
|
||||
mangled-names = []
|
||||
|
||||
# Only used in the compiler's build system
|
||||
rustc-dep-of-std = ["compiler-builtins"]
|
||||
|
||||
# This makes certain traits and function specializations public that
|
||||
# are not normally public but are required by the `builtins-test`
|
||||
unstable-public-internals = []
|
||||
@@ -6,7 +6,7 @@ publish = false
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
compiler_builtins = { path = "../compiler-builtins", features = ["compiler-builtins"] }
|
||||
compiler_builtins = { path = "../builtins-shim", features = ["compiler-builtins"] }
|
||||
panic-handler = { path = "../crates/panic-handler" }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -17,7 +17,7 @@ rustc_apfloat = "0.2.2"
|
||||
iai-callgrind = { version = "0.14.1", optional = true }
|
||||
|
||||
[dependencies.compiler_builtins]
|
||||
path = "../compiler-builtins"
|
||||
path = "../builtins-shim"
|
||||
default-features = false
|
||||
features = ["unstable-public-internals"]
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![feature(decl_macro)] // so we can use pub(super)
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![cfg(all(target_arch = "aarch64", target_os = "linux", not(feature = "no-asm")))]
|
||||
|
||||
/// Translate a byte size to a Rust type.
|
||||
@@ -87,7 +88,7 @@ test_op!(add, |left, right| left.wrapping_add(right));
|
||||
test_op!(clr, |left, right| left & !right);
|
||||
test_op!(xor, std::ops::BitXor::bitxor);
|
||||
test_op!(or, std::ops::BitOr::bitor);
|
||||
|
||||
use compiler_builtins::{foreach_bytes, foreach_ordering};
|
||||
compiler_builtins::foreach_cas!(cas::test);
|
||||
compiler_builtins::foreach_cas16!(test_cas16);
|
||||
compiler_builtins::foreach_swp!(swap::test);
|
||||
|
||||
@@ -57,7 +57,7 @@ function run_icount_benchmarks() {
|
||||
# Disregard regressions after merge
|
||||
echo "Benchmarks completed with regressions; ignoring (not in a PR)"
|
||||
else
|
||||
./ci/ci-util.py handle-banch-regressions "$PR_NUMBER"
|
||||
./ci/ci-util.py handle-bench-regressions "$PR_NUMBER"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
# NOTE: Must be kept in sync with `../builtins-shim/Cargo.toml`.
|
||||
#
|
||||
# This manifest is actually used in-tree by rust-lang/rust,
|
||||
# `../builtins-shim/Cargo.toml` is used by out-of-tree testing. See the other
|
||||
# manifest for further details.
|
||||
|
||||
[package]
|
||||
authors = ["Jorge Aparicio <japaricious@gmail.com>"]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.160"
|
||||
license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/rust-lang/compiler-builtins"
|
||||
homepage = "https://github.com/rust-lang/compiler-builtins"
|
||||
documentation = "https://docs.rs/compiler_builtins"
|
||||
edition = "2024"
|
||||
authors = ["Jorge Aparicio <japaricious@gmail.com>"]
|
||||
description = "Compiler intrinsics used by the Rust compiler."
|
||||
repository = "https://github.com/rust-lang/compiler-builtins"
|
||||
license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
links = "compiler-rt"
|
||||
|
||||
[lib]
|
||||
@@ -53,7 +57,3 @@ rustc-dep-of-std = ["compiler-builtins", "dep:core"]
|
||||
# This makes certain traits and function specializations public that
|
||||
# are not normally public but are required by the `builtins-test`
|
||||
unstable-public-internals = []
|
||||
|
||||
[lints.rust]
|
||||
# The cygwin config can be dropped after our benchmark toolchain is bumped
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)', 'cfg(target_os, values("cygwin"))'] }
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
mod configure;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use configure::{Target, configure_aliases, configure_f16_f128};
|
||||
|
||||
@@ -86,10 +83,6 @@ fn main() {
|
||||
{
|
||||
println!("cargo:rustc-cfg=kernel_user_helpers")
|
||||
}
|
||||
|
||||
if llvm_target[0].starts_with("aarch64") {
|
||||
generate_aarch64_outlined_atomics();
|
||||
}
|
||||
}
|
||||
|
||||
/// Run configuration for `libm` since it is included directly.
|
||||
@@ -132,61 +125,6 @@ fn configure_libm(target: &Target) {
|
||||
println!("cargo:rustc-cfg=feature=\"unstable-intrinsics\"");
|
||||
}
|
||||
|
||||
fn aarch64_symbol(ordering: Ordering) -> &'static str {
|
||||
match ordering {
|
||||
Ordering::Relaxed => "relax",
|
||||
Ordering::Acquire => "acq",
|
||||
Ordering::Release => "rel",
|
||||
Ordering::AcqRel => "acq_rel",
|
||||
_ => panic!("unknown symbol for {ordering:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// The `concat_idents` macro is extremely annoying and doesn't allow us to define new items.
|
||||
/// Define them from the build script instead.
|
||||
/// Note that the majority of the code is still defined in `aarch64.rs` through inline macros.
|
||||
fn generate_aarch64_outlined_atomics() {
|
||||
use std::fmt::Write;
|
||||
// #[macro_export] so that we can use this in tests
|
||||
let gen_macro =
|
||||
|name| format!("#[macro_export] macro_rules! foreach_{name} {{ ($macro:path) => {{\n");
|
||||
|
||||
// Generate different macros for add/clr/eor/set so that we can test them separately.
|
||||
let sym_names = ["cas", "ldadd", "ldclr", "ldeor", "ldset", "swp"];
|
||||
let mut macros = BTreeMap::new();
|
||||
for sym in sym_names {
|
||||
macros.insert(sym, gen_macro(sym));
|
||||
}
|
||||
|
||||
// Only CAS supports 16 bytes, and it has a different implementation that uses a different macro.
|
||||
let mut cas16 = gen_macro("cas16");
|
||||
|
||||
for ordering in [
|
||||
Ordering::Relaxed,
|
||||
Ordering::Acquire,
|
||||
Ordering::Release,
|
||||
Ordering::AcqRel,
|
||||
] {
|
||||
let sym_ordering = aarch64_symbol(ordering);
|
||||
for size in [1, 2, 4, 8] {
|
||||
for (sym, macro_) in &mut macros {
|
||||
let name = format!("__aarch64_{sym}{size}_{sym_ordering}");
|
||||
writeln!(macro_, "$macro!( {ordering:?}, {size}, {name} );").unwrap();
|
||||
}
|
||||
}
|
||||
let name = format!("__aarch64_cas16_{sym_ordering}");
|
||||
writeln!(cas16, "$macro!( {ordering:?}, {name} );").unwrap();
|
||||
}
|
||||
|
||||
let mut buf = String::new();
|
||||
for macro_def in macros.values().chain(std::iter::once(&cas16)) {
|
||||
buf += macro_def;
|
||||
buf += "}; }\n";
|
||||
}
|
||||
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
std::fs::write(out_dir.join("outlined_atomics.rs"), buf).unwrap();
|
||||
}
|
||||
|
||||
/// Emit directives for features we expect to support that aren't in `Cargo.toml`.
|
||||
///
|
||||
/// These are mostly cfg elements emitted by this `build.rs`.
|
||||
|
||||
@@ -262,8 +262,78 @@ macro_rules! or {
|
||||
};
|
||||
}
|
||||
|
||||
// See `generate_aarch64_outlined_atomics` in build.rs.
|
||||
include!(concat!(env!("OUT_DIR"), "/outlined_atomics.rs"));
|
||||
#[macro_export]
|
||||
macro_rules! foreach_ordering {
|
||||
($macro:path, $bytes:tt, $name:ident) => {
|
||||
$macro!( Relaxed, $bytes, ${concat($name, _relax)} );
|
||||
$macro!( Acquire, $bytes, ${concat($name, _acq)} );
|
||||
$macro!( Release, $bytes, ${concat($name, _rel)} );
|
||||
$macro!( AcqRel, $bytes, ${concat($name, _acq_rel)} );
|
||||
};
|
||||
($macro:path, $name:ident) => {
|
||||
$macro!( Relaxed, ${concat($name, _relax)} );
|
||||
$macro!( Acquire, ${concat($name, _acq)} );
|
||||
$macro!( Release, ${concat($name, _rel)} );
|
||||
$macro!( AcqRel, ${concat($name, _acq_rel)} );
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! foreach_bytes {
|
||||
($macro:path, $name:ident) => {
|
||||
foreach_ordering!( $macro, 1, ${concat(__aarch64_, $name, "1")} );
|
||||
foreach_ordering!( $macro, 2, ${concat(__aarch64_, $name, "2")} );
|
||||
foreach_ordering!( $macro, 4, ${concat(__aarch64_, $name, "4")} );
|
||||
foreach_ordering!( $macro, 8, ${concat(__aarch64_, $name, "8")} );
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate different macros for cas/swp/add/clr/eor/set so that we can test them separately.
|
||||
#[macro_export]
|
||||
macro_rules! foreach_cas {
|
||||
($macro:path) => {
|
||||
foreach_bytes!($macro, cas);
|
||||
};
|
||||
}
|
||||
|
||||
/// Only CAS supports 16 bytes, and it has a different implementation that uses a different macro.
|
||||
#[macro_export]
|
||||
macro_rules! foreach_cas16 {
|
||||
($macro:path) => {
|
||||
foreach_ordering!($macro, __aarch64_cas16);
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! foreach_swp {
|
||||
($macro:path) => {
|
||||
foreach_bytes!($macro, swp);
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! foreach_ldadd {
|
||||
($macro:path) => {
|
||||
foreach_bytes!($macro, ldadd);
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! foreach_ldclr {
|
||||
($macro:path) => {
|
||||
foreach_bytes!($macro, ldclr);
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! foreach_ldeor {
|
||||
($macro:path) => {
|
||||
foreach_bytes!($macro, ldeor);
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! foreach_ldset {
|
||||
($macro:path) => {
|
||||
foreach_bytes!($macro, ldset);
|
||||
};
|
||||
}
|
||||
|
||||
foreach_cas!(compare_and_swap);
|
||||
foreach_cas16!(compare_and_swap_i128);
|
||||
foreach_swp!(swap);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#![feature(linkage)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(repr_simd)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![cfg_attr(f16_enabled, feature(f16))]
|
||||
#![cfg_attr(f128_enabled, feature(f128))]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
[package]
|
||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
categories = ["no-std"]
|
||||
description = "libm in pure Rust"
|
||||
documentation = "https://docs.rs/libm"
|
||||
keywords = ["libm", "math"]
|
||||
license = "MIT"
|
||||
name = "libm"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/rust-lang/compiler-builtins"
|
||||
version = "0.2.15"
|
||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
description = "libm in pure Rust"
|
||||
categories = ["no-std"]
|
||||
keywords = ["libm", "math"]
|
||||
repository = "https://github.com/rust-lang/compiler-builtins"
|
||||
license = "MIT"
|
||||
edition = "2021"
|
||||
rust-version = "1.63"
|
||||
|
||||
|
||||
@@ -82,22 +82,77 @@ mod tests {
|
||||
fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ONE, F::INFINITY, F::ONE),
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
(F::INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
(F::NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NEG_NAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_NAN, F::ONE, F::ONE),
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmin({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between zeros and NaNs does not matter
|
||||
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
|
||||
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -125,22 +180,77 @@ mod tests {
|
||||
fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::ONE),
|
||||
(F::ONE, F::INFINITY, F::INFINITY),
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ONE, F::INFINITY),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
(F::NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NEG_NAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_NAN, F::ONE, F::ONE),
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmax({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between zeros and NaNs does not matter
|
||||
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
|
||||
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -74,24 +74,77 @@ mod tests {
|
||||
fn fminimum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::NAN),
|
||||
(F::ZERO, F::NAN, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ZERO, F::INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::NAN),
|
||||
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NAN),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ONE, F::INFINITY, F::ONE),
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::NAN),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NAN),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
(F::INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::NAN),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NAN),
|
||||
(F::NAN, F::ZERO, F::NAN),
|
||||
(F::NAN, F::NEG_ZERO, F::NAN),
|
||||
(F::NAN, F::ONE, F::NAN),
|
||||
(F::NAN, F::NEG_ONE, F::NAN),
|
||||
(F::NAN, F::INFINITY, F::NAN),
|
||||
(F::NAN, F::NEG_INFINITY, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fminimum({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -119,24 +172,77 @@ mod tests {
|
||||
fn fmaximum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::ZERO, F::NAN),
|
||||
(F::ZERO, F::NAN, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::ZERO, F::NEG_ZERO, F::ZERO),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::NAN),
|
||||
(F::NEG_ZERO, F::ZERO, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NAN),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::ONE),
|
||||
(F::ONE, F::INFINITY, F::INFINITY),
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::NAN),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NAN),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ONE, F::INFINITY),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::NAN),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NAN),
|
||||
(F::NAN, F::ZERO, F::NAN),
|
||||
(F::NAN, F::NEG_ZERO, F::NAN),
|
||||
(F::NAN, F::ONE, F::NAN),
|
||||
(F::NAN, F::NEG_ONE, F::NAN),
|
||||
(F::NAN, F::INFINITY, F::NAN),
|
||||
(F::NAN, F::NEG_INFINITY, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmaximum({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -74,24 +74,77 @@ mod tests {
|
||||
fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ZERO, F::INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ONE, F::INFINITY, F::ONE),
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
(F::INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
(F::NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NEG_NAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_NAN, F::ONE, F::ONE),
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fminimum_num({}, {})", Hexf(x), Hexf(y));
|
||||
for (x, y, expected) in cases {
|
||||
let actual = f(x, y);
|
||||
assert_biteq!(actual, expected, "fminimum_num({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -119,24 +172,77 @@ mod tests {
|
||||
fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::ZERO, F::NEG_ZERO, F::ZERO),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::ZERO, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::ONE),
|
||||
(F::ONE, F::INFINITY, F::INFINITY),
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ONE, F::INFINITY),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
(F::NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NEG_NAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_NAN, F::ONE, F::ONE),
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
|
||||
for (x, y, expected) in cases {
|
||||
let actual = f(x, y);
|
||||
assert_biteq!(actual, expected, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -19,6 +19,5 @@ use crate::support::Float;
|
||||
#[inline]
|
||||
pub fn fmax<F: Float>(x: F, y: F) -> F {
|
||||
let res = if x.is_nan() || x < y { y } else { x };
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//! Per the spec, returns the canonicalized result of:
|
||||
//! - `x` if `x > y`
|
||||
//! - `y` if `y > x`
|
||||
//! - +0.0 if x and y are zero with opposite signs
|
||||
//! - qNaN if either operation is NaN
|
||||
//! - Logic following +0.0 > -0.0
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
|
||||
@@ -23,6 +23,5 @@ pub fn fmaximum<F: Float>(x: F, y: F) -> F {
|
||||
y
|
||||
};
|
||||
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
//! Per the spec, returns:
|
||||
//! - `x` if `x > y`
|
||||
//! - `y` if `y > x`
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - Logic following +0.0 > -0.0
|
||||
//! - +0.0 if x and y are zero with opposite signs
|
||||
//! - Either `x` or `y` if `x == y` and the signs are the same
|
||||
//! - qNaN if either operand is a NaN
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - qNaN if both operands are NaNx
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
|
||||
@@ -15,12 +15,15 @@ use crate::support::Float;
|
||||
|
||||
#[inline]
|
||||
pub fn fmaximum_num<F: Float>(x: F, y: F) -> F {
|
||||
let res = if x.is_nan() || x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) {
|
||||
y
|
||||
} else {
|
||||
let res = if x > y || y.is_nan() {
|
||||
x
|
||||
} else if y > x || x.is_nan() {
|
||||
y
|
||||
} else if x.is_sign_positive() {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
};
|
||||
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -19,6 +19,5 @@ use crate::support::Float;
|
||||
#[inline]
|
||||
pub fn fmin<F: Float>(x: F, y: F) -> F {
|
||||
let res = if y.is_nan() || x < y { x } else { y };
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//! Per the spec, returns the canonicalized result of:
|
||||
//! - `x` if `x < y`
|
||||
//! - `y` if `y < x`
|
||||
//! - -0.0 if x and y are zero with opposite signs
|
||||
//! - qNaN if either operation is NaN
|
||||
//! - Logic following +0.0 > -0.0
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
|
||||
@@ -23,6 +23,5 @@ pub fn fminimum<F: Float>(x: F, y: F) -> F {
|
||||
y
|
||||
};
|
||||
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
//! Per the spec, returns:
|
||||
//! - `x` if `x < y`
|
||||
//! - `y` if `y < x`
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - Logic following +0.0 > -0.0
|
||||
//! - -0.0 if x and y are zero with opposite signs
|
||||
//! - Either `x` or `y` if `x == y` and the signs are the same
|
||||
//! - qNaN if either operand is a NaN
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - qNaN if both operands are NaNx
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
|
||||
@@ -15,12 +15,15 @@ use crate::support::Float;
|
||||
|
||||
#[inline]
|
||||
pub fn fminimum_num<F: Float>(x: F, y: F) -> F {
|
||||
let res = if y.is_nan() || x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) {
|
||||
x
|
||||
} else {
|
||||
let res = if x > y || x.is_nan() {
|
||||
y
|
||||
} else if y > x || y.is_nan() {
|
||||
x
|
||||
} else if x.is_sign_positive() {
|
||||
y
|
||||
} else {
|
||||
x
|
||||
};
|
||||
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -190,6 +190,15 @@ pub trait Float:
|
||||
Self::ONE.copysign(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a best-effort attempt to canonicalize the number. Note that this is allowed
|
||||
/// to be a nop and does not always quiet sNaNs.
|
||||
fn canonicalize(self) -> Self {
|
||||
// FIXME: LLVM often removes this. We should determine whether we can remove the operation,
|
||||
// or switch to something based on `llvm.canonicalize` (which has crashes,
|
||||
// <https://github.com/llvm/llvm-project/issues/32650>).
|
||||
self * Self::ONE
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the associated `Int` type from a float (helper to avoid ambiguous associated types).
|
||||
|
||||
@@ -143,10 +143,12 @@ macro_rules! assert_biteq {
|
||||
let bits = $crate::support::Int::leading_zeros(l.to_bits() - l.to_bits());
|
||||
assert!(
|
||||
$crate::support::Float::biteq(l, r),
|
||||
"{}\nl: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})",
|
||||
"{}\nl: {l:?} ({lb:#0width$x} {lh})\nr: {r:?} ({rb:#0width$x} {rh})",
|
||||
format_args!($($tt)*),
|
||||
lb = l.to_bits(),
|
||||
lh = $crate::support::Hexf(l),
|
||||
rb = r.to_bits(),
|
||||
rh = $crate::support::Hexf(r),
|
||||
width = ((bits / 4) + 2) as usize,
|
||||
|
||||
);
|
||||
|
||||
@@ -1 +1 @@
|
||||
df8102fe5f24f28a918660b0cd918d7331c3896e
|
||||
d087f112b7d1323446c7b39a8b616aee7fa56b3d
|
||||
|
||||
Reference in New Issue
Block a user