Run cargo fmt on all projects
Apply the same formatting rules to both `libm` and `compiler-builtins`.
This commit is contained in:
parent
569b40209d
commit
8d70be87e6
71 changed files with 1070 additions and 283 deletions
|
|
@ -480,6 +480,7 @@ mod intrinsics {
|
|||
|
||||
fn run() {
|
||||
use core::hint::black_box as bb;
|
||||
|
||||
use intrinsics::*;
|
||||
|
||||
// FIXME(f16_f128): some PPC f128 <-> int conversion functions have the wrong names
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
#![cfg_attr(f128_enabled, feature(f128))]
|
||||
|
||||
use builtins_test::float_bench;
|
||||
use criterion::{Criterion, criterion_main};
|
||||
|
||||
use compiler_builtins::float::cmp;
|
||||
use criterion::{Criterion, criterion_main};
|
||||
|
||||
/// `gt` symbols are allowed to return differing results, they just get compared
|
||||
/// to 0.
|
||||
|
|
|
|||
|
|
@ -239,9 +239,10 @@ mod mcmp {
|
|||
}
|
||||
|
||||
mod mmove {
|
||||
use super::*;
|
||||
use Spread::{Aligned, Large, Medium, Small};
|
||||
|
||||
use super::*;
|
||||
|
||||
struct Cfg {
|
||||
len: usize,
|
||||
spread: Spread,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::cell::RefCell;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use compiler_builtins::float::Float;
|
||||
|
||||
/// Fuzz with these many items to ensure equal functions
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ extern crate alloc;
|
|||
|
||||
use compiler_builtins::float::Float;
|
||||
use compiler_builtins::int::{Int, MinInt};
|
||||
|
||||
use rand_xoshiro::Xoshiro128StarStar;
|
||||
use rand_xoshiro::rand_core::{RngCore, SeedableRng};
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,6 @@ mod float_comparisons {
|
|||
__eqkf2 as __eqtf2, __gekf2 as __getf2, __gtkf2 as __gttf2, __lekf2 as __letf2,
|
||||
__ltkf2 as __lttf2, __nekf2 as __netf2, __unordkf2 as __unordtf2,
|
||||
};
|
||||
|
||||
#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
|
||||
use compiler_builtins::float::cmp::{
|
||||
__eqtf2, __getf2, __gttf2, __letf2, __lttf2, __netf2, __unordtf2,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
#![feature(f128)]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
use builtins_test::*;
|
||||
use compiler_builtins::int::sdiv::{__divmoddi4, __divmodsi4, __divmodti4};
|
||||
use compiler_builtins::int::udiv::{__udivmoddi4, __udivmodsi4, __udivmodti4, u128_divide_sparc};
|
||||
|
||||
use builtins_test::*;
|
||||
|
||||
// Division algorithms have by far the nastiest and largest number of edge cases, and experience shows
|
||||
// that sometimes 100_000 iterations of the random fuzzer is needed.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
mod configure;
|
||||
|
||||
use std::{collections::BTreeMap, env, path::PathBuf, sync::atomic::Ordering};
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use configure::{configure_aliases, configure_f16_f128, Target};
|
||||
use configure::{Target, configure_aliases, configure_f16_f128};
|
||||
|
||||
fn main() {
|
||||
println!("cargo::rerun-if-changed=build.rs");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use core::arch;
|
||||
use core::mem;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use core::{arch, mem};
|
||||
|
||||
// Kernel-provided user-mode helper functions:
|
||||
// https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use core::ops::Neg;
|
||||
|
||||
use crate::int::{CastFrom, CastInto, Int, MinInt};
|
||||
|
||||
use super::Float;
|
||||
use crate::int::{CastFrom, CastInto, Int, MinInt};
|
||||
|
||||
/// Conversions from integers to floats.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -79,11 +79,12 @@
|
|||
//!
|
||||
//! [Newton-Raphson method]: https://en.wikipedia.org/wiki/Newton%27s_method
|
||||
|
||||
use core::mem::size_of;
|
||||
use core::ops;
|
||||
|
||||
use super::HalfRep;
|
||||
use crate::float::Float;
|
||||
use crate::int::{CastFrom, CastInto, DInt, HInt, Int, MinInt};
|
||||
use core::mem::size_of;
|
||||
use core::ops;
|
||||
|
||||
fn div<F: Float>(a: F, b: F) -> F
|
||||
where
|
||||
|
|
@ -487,7 +488,7 @@ where
|
|||
};
|
||||
|
||||
residual_lo += abs_result & one; // tie to even
|
||||
// conditionally turns the below LT comparison into LTE
|
||||
// conditionally turns the below LT comparison into LTE
|
||||
abs_result += u8::from(residual_lo > b_significand).into();
|
||||
|
||||
if F::BITS == 128 || (F::BITS == 32 && half_iterations > 0) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,5 @@ pub mod trunc;
|
|||
|
||||
#[cfg(not(feature = "public-test-deps"))]
|
||||
pub(crate) use traits::{Float, HalfRep};
|
||||
|
||||
#[cfg(feature = "public-test-deps")]
|
||||
pub use traits::{Float, HalfRep};
|
||||
|
|
|
|||
|
|
@ -18,11 +18,7 @@ fn pow<F: Float>(a: F, b: i32) -> F {
|
|||
a *= a;
|
||||
}
|
||||
|
||||
if recip {
|
||||
F::ONE / mul
|
||||
} else {
|
||||
mul
|
||||
}
|
||||
if recip { F::ONE / mul } else { mul }
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
#![allow(unused)]
|
||||
|
||||
use crate::int::{DInt, HInt, Int, MinInt};
|
||||
use core::{fmt, ops};
|
||||
|
||||
use crate::int::{DInt, HInt, Int, MinInt};
|
||||
|
||||
const WORD_LO_MASK: u64 = 0x00000000ffffffff;
|
||||
const WORD_HI_MASK: u64 = 0xffffffff00000000;
|
||||
const WORD_FULL_MASK: u64 = 0xffffffffffffffff;
|
||||
|
|
|
|||
|
|
@ -60,11 +60,7 @@ mod implementation {
|
|||
}
|
||||
// the last two bisections are combined into one conditional
|
||||
t = x >> 1;
|
||||
if t != T::ZERO {
|
||||
z - 2
|
||||
} else {
|
||||
z - x.cast()
|
||||
}
|
||||
if t != T::ZERO { z - 2 } else { z - x.cast() }
|
||||
|
||||
// We could potentially save a few cycles by using the LUT trick from
|
||||
// "https://embeddedgurus.com/state-space/2014/09/
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ mod traits;
|
|||
pub mod udiv;
|
||||
|
||||
pub use big::{i256, u256};
|
||||
|
||||
#[cfg(not(feature = "public-test-deps"))]
|
||||
pub(crate) use traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt};
|
||||
|
||||
#[cfg(feature = "public-test-deps")]
|
||||
pub use traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt};
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ mod delegate;
|
|||
#[allow(unused_imports)]
|
||||
#[cfg(not(feature = "public-test-deps"))]
|
||||
pub(crate) use self::delegate::u128_divide_sparc;
|
||||
|
||||
#[cfg(feature = "public-test-deps")]
|
||||
pub use self::delegate::u128_divide_sparc;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#[cfg(not(feature = "public-test-deps"))]
|
||||
pub(crate) use crate::int::specialized_div_rem::*;
|
||||
|
||||
#[cfg(feature = "public-test-deps")]
|
||||
pub use crate::int::specialized_div_rem::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@
|
|||
// Note that ERMSB does not enhance the backwards (DF=1) "rep movsb".
|
||||
|
||||
use core::arch::asm;
|
||||
use core::intrinsics;
|
||||
use core::mem;
|
||||
use core::{intrinsics, mem};
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_feature = "ermsb")]
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ pub fn function_enum(
|
|||
};
|
||||
|
||||
if let Some(tt) = attr.next() {
|
||||
return Err(syn::Error::new(tt.span(), "unexpected token after identifier"));
|
||||
return Err(syn::Error::new(
|
||||
tt.span(),
|
||||
"unexpected token after identifier",
|
||||
));
|
||||
}
|
||||
|
||||
let enum_name = &item.ident;
|
||||
|
|
@ -46,8 +49,12 @@ pub fn function_enum(
|
|||
// Match arm for `fn base_name(self)` matcher
|
||||
base_arms.push(quote! { Self::#ident => #base_enum::#bname_ident });
|
||||
|
||||
let variant =
|
||||
Variant { attrs: Vec::new(), ident, fields: Fields::Unit, discriminant: None };
|
||||
let variant = Variant {
|
||||
attrs: Vec::new(),
|
||||
ident,
|
||||
fields: Fields::Unit,
|
||||
discriminant: None,
|
||||
};
|
||||
|
||||
item.variants.push(variant);
|
||||
}
|
||||
|
|
@ -108,7 +115,10 @@ pub fn base_name_enum(
|
|||
return Err(syn::Error::new(sp.span(), "no attributes expected"));
|
||||
}
|
||||
|
||||
let mut base_names: Vec<_> = ALL_OPERATIONS.iter().map(|func| base_name(func.name)).collect();
|
||||
let mut base_names: Vec<_> = ALL_OPERATIONS
|
||||
.iter()
|
||||
.map(|func| base_name(func.name))
|
||||
.collect();
|
||||
base_names.sort_unstable();
|
||||
base_names.dedup();
|
||||
|
||||
|
|
@ -121,8 +131,12 @@ pub fn base_name_enum(
|
|||
// Match arm for `fn as_str(self)` matcher
|
||||
as_str_arms.push(quote! { Self::#ident => #base_name });
|
||||
|
||||
let variant =
|
||||
Variant { attrs: Vec::new(), ident, fields: Fields::Unit, discriminant: None };
|
||||
let variant = Variant {
|
||||
attrs: Vec::new(),
|
||||
ident,
|
||||
fields: Fields::Unit,
|
||||
discriminant: None,
|
||||
};
|
||||
|
||||
item.variants.push(variant);
|
||||
}
|
||||
|
|
@ -147,7 +161,10 @@ pub fn base_name_enum(
|
|||
/// Verify that an enum is empty, otherwise return an error
|
||||
fn expect_empty_enum(item: &ItemEnum) -> syn::Result<()> {
|
||||
if !item.variants.is_empty() {
|
||||
Err(syn::Error::new(item.variants.span(), "expected an empty enum"))
|
||||
Err(syn::Error::new(
|
||||
item.variants.span(),
|
||||
"expected an empty enum",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ use syn::spanned::Spanned;
|
|||
use syn::visit_mut::VisitMut;
|
||||
use syn::{Ident, ItemEnum};
|
||||
|
||||
const KNOWN_TYPES: &[&str] = &["FTy", "CFn", "CArgs", "CRet", "RustFn", "RustArgs", "RustRet"];
|
||||
const KNOWN_TYPES: &[&str] = &[
|
||||
"FTy", "CFn", "CArgs", "CRet", "RustFn", "RustArgs", "RustRet",
|
||||
];
|
||||
|
||||
/// Populate an enum with a variant representing function. Names are in upper camel case.
|
||||
///
|
||||
|
|
@ -142,10 +144,17 @@ fn validate(input: &mut StructuredInput) -> syn::Result<Vec<&'static MathOpInfo>
|
|||
.flat_map(|map_list| map_list.iter())
|
||||
.flat_map(|attr_map| attr_map.names.iter());
|
||||
let only_mentions = input.only.iter().flat_map(|only_list| only_list.iter());
|
||||
let fn_extra_mentions =
|
||||
input.fn_extra.iter().flat_map(|v| v.keys()).filter(|name| *name != "_");
|
||||
let all_mentioned_fns =
|
||||
input.skip.iter().chain(only_mentions).chain(attr_mentions).chain(fn_extra_mentions);
|
||||
let fn_extra_mentions = input
|
||||
.fn_extra
|
||||
.iter()
|
||||
.flat_map(|v| v.keys())
|
||||
.filter(|name| *name != "_");
|
||||
let all_mentioned_fns = input
|
||||
.skip
|
||||
.iter()
|
||||
.chain(only_mentions)
|
||||
.chain(attr_mentions)
|
||||
.chain(fn_extra_mentions);
|
||||
|
||||
// Make sure that every function mentioned is a real function
|
||||
for mentioned in all_mentioned_fns {
|
||||
|
|
@ -171,7 +180,11 @@ fn validate(input: &mut StructuredInput) -> syn::Result<Vec<&'static MathOpInfo>
|
|||
for func in ALL_OPERATIONS.iter() {
|
||||
let fn_name = func.name;
|
||||
// If we have an `only` list and it does _not_ contain this function name, skip it
|
||||
if input.only.as_ref().is_some_and(|only| !only.iter().any(|o| o == fn_name)) {
|
||||
if input
|
||||
.only
|
||||
.as_ref()
|
||||
.is_some_and(|only| !only.iter().any(|o| o == fn_name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -296,8 +309,11 @@ fn expand(input: StructuredInput, fn_list: &[&MathOpInfo]) -> syn::Result<pm2::T
|
|||
// Prepare function-specific extra in a `fn_extra: ...` field, running the replacer
|
||||
let fn_extra_field = match input.fn_extra {
|
||||
Some(ref map) => {
|
||||
let mut fn_extra =
|
||||
map.get(&fn_name).or_else(|| map.get(&default_ident)).unwrap().clone();
|
||||
let mut fn_extra = map
|
||||
.get(&fn_name)
|
||||
.or_else(|| map.get(&default_ident))
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let mut v = MacroReplace::new(func.name);
|
||||
v.visit_expr_mut(&mut fn_extra);
|
||||
|
|
@ -357,7 +373,11 @@ struct MacroReplace {
|
|||
impl MacroReplace {
|
||||
fn new(name: &'static str) -> Self {
|
||||
let norm_name = base_name(name);
|
||||
Self { fn_name: name, norm_name: norm_name.to_owned(), error: None }
|
||||
Self {
|
||||
fn_name: name,
|
||||
norm_name: norm_name.to_owned(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(self) -> syn::Result<()> {
|
||||
|
|
@ -377,8 +397,10 @@ impl MacroReplace {
|
|||
"MACRO_FN_NAME" => *i = Ident::new(self.fn_name, i.span()),
|
||||
"MACRO_FN_NAME_NORMALIZED" => *i = Ident::new(&self.norm_name, i.span()),
|
||||
_ => {
|
||||
self.error =
|
||||
Some(syn::Error::new(i.span(), format!("unrecognized meta expression `{s}`")));
|
||||
self.error = Some(syn::Error::new(
|
||||
i.span(),
|
||||
format!("unrecognized meta expression `{s}`"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ pub struct Invocation {
|
|||
|
||||
impl Parse for Invocation {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self { fields: input.parse_terminated(Mapping::parse, Token![,])? })
|
||||
Ok(Self {
|
||||
fields: input.parse_terminated(Mapping::parse, Token![,])?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +32,11 @@ struct Mapping {
|
|||
|
||||
impl Parse for Mapping {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self { name: input.parse()?, _sep: input.parse()?, expr: input.parse()? })
|
||||
Ok(Self {
|
||||
name: input.parse()?,
|
||||
_sep: input.parse()?,
|
||||
expr: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +139,13 @@ fn extract_fn_extra_field(expr: Expr) -> syn::Result<BTreeMap<Ident, Expr>> {
|
|||
return Err(e);
|
||||
};
|
||||
|
||||
let ExprMatch { attrs, match_token: _, expr, brace_token: _, arms } = mexpr;
|
||||
let ExprMatch {
|
||||
attrs,
|
||||
match_token: _,
|
||||
expr,
|
||||
brace_token: _,
|
||||
arms,
|
||||
} = mexpr;
|
||||
|
||||
expect_empty_attrs(&attrs)?;
|
||||
|
||||
|
|
@ -146,7 +158,14 @@ fn extract_fn_extra_field(expr: Expr) -> syn::Result<BTreeMap<Ident, Expr>> {
|
|||
let mut res = BTreeMap::new();
|
||||
|
||||
for arm in arms {
|
||||
let Arm { attrs, pat, guard, fat_arrow_token: _, body, comma: _ } = arm;
|
||||
let Arm {
|
||||
attrs,
|
||||
pat,
|
||||
guard,
|
||||
fat_arrow_token: _,
|
||||
body,
|
||||
comma: _,
|
||||
} = arm;
|
||||
|
||||
expect_empty_attrs(&attrs)?;
|
||||
|
||||
|
|
@ -177,15 +196,20 @@ fn expect_empty_attrs(attrs: &[Attribute]) -> syn::Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let e =
|
||||
syn::Error::new(attrs.first().unwrap().span(), "no attributes allowed in this position");
|
||||
let e = syn::Error::new(
|
||||
attrs.first().unwrap().span(),
|
||||
"no attributes allowed in this position",
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
|
||||
/// Extract a named field from a map, raising an error if it doesn't exist.
|
||||
fn expect_field(v: &mut Vec<Mapping>, name: &str) -> syn::Result<Expr> {
|
||||
let pos = v.iter().position(|v| v.name == name).ok_or_else(|| {
|
||||
syn::Error::new(Span::call_site(), format!("missing expected field `{name}`"))
|
||||
syn::Error::new(
|
||||
Span::call_site(),
|
||||
format!("missing expected field `{name}`"),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(v.remove(pos).expr)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `fn(f16) -> f16`
|
||||
FloatTy::F16,
|
||||
Signature { args: &[Ty::F16], returns: &[Ty::F16] },
|
||||
Signature {
|
||||
args: &[Ty::F16],
|
||||
returns: &[Ty::F16],
|
||||
},
|
||||
None,
|
||||
&[
|
||||
"ceilf16",
|
||||
|
|
@ -23,7 +26,10 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `fn(f32) -> f32`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32], returns: &[Ty::F32] },
|
||||
Signature {
|
||||
args: &[Ty::F32],
|
||||
returns: &[Ty::F32],
|
||||
},
|
||||
None,
|
||||
&[
|
||||
"acosf",
|
||||
|
|
@ -68,7 +74,10 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `(f64) -> f64`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64], returns: &[Ty::F64] },
|
||||
Signature {
|
||||
args: &[Ty::F64],
|
||||
returns: &[Ty::F64],
|
||||
},
|
||||
None,
|
||||
&[
|
||||
"acos",
|
||||
|
|
@ -113,7 +122,10 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `fn(f128) -> f128`
|
||||
FloatTy::F128,
|
||||
Signature { args: &[Ty::F128], returns: &[Ty::F128] },
|
||||
Signature {
|
||||
args: &[Ty::F128],
|
||||
returns: &[Ty::F128],
|
||||
},
|
||||
None,
|
||||
&[
|
||||
"ceilf128",
|
||||
|
|
@ -129,7 +141,10 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `(f16, f16) -> f16`
|
||||
FloatTy::F16,
|
||||
Signature { args: &[Ty::F16, Ty::F16], returns: &[Ty::F16] },
|
||||
Signature {
|
||||
args: &[Ty::F16, Ty::F16],
|
||||
returns: &[Ty::F16],
|
||||
},
|
||||
None,
|
||||
&[
|
||||
"copysignf16",
|
||||
|
|
@ -146,7 +161,10 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `(f32, f32) -> f32`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32, Ty::F32], returns: &[Ty::F32] },
|
||||
Signature {
|
||||
args: &[Ty::F32, Ty::F32],
|
||||
returns: &[Ty::F32],
|
||||
},
|
||||
None,
|
||||
&[
|
||||
"atan2f",
|
||||
|
|
@ -168,7 +186,10 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `(f64, f64) -> f64`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64, Ty::F64], returns: &[Ty::F64] },
|
||||
Signature {
|
||||
args: &[Ty::F64, Ty::F64],
|
||||
returns: &[Ty::F64],
|
||||
},
|
||||
None,
|
||||
&[
|
||||
"atan2",
|
||||
|
|
@ -190,7 +211,10 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `(f128, f128) -> f128`
|
||||
FloatTy::F128,
|
||||
Signature { args: &[Ty::F128, Ty::F128], returns: &[Ty::F128] },
|
||||
Signature {
|
||||
args: &[Ty::F128, Ty::F128],
|
||||
returns: &[Ty::F128],
|
||||
},
|
||||
None,
|
||||
&[
|
||||
"copysignf128",
|
||||
|
|
@ -207,134 +231,215 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
|
|||
(
|
||||
// `(f32, f32, f32) -> f32`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32, Ty::F32, Ty::F32], returns: &[Ty::F32] },
|
||||
Signature {
|
||||
args: &[Ty::F32, Ty::F32, Ty::F32],
|
||||
returns: &[Ty::F32],
|
||||
},
|
||||
None,
|
||||
&["fmaf"],
|
||||
),
|
||||
(
|
||||
// `(f64, f64, f64) -> f64`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64, Ty::F64, Ty::F64], returns: &[Ty::F64] },
|
||||
Signature {
|
||||
args: &[Ty::F64, Ty::F64, Ty::F64],
|
||||
returns: &[Ty::F64],
|
||||
},
|
||||
None,
|
||||
&["fma"],
|
||||
),
|
||||
(
|
||||
// `(f128, f128, f128) -> f128`
|
||||
FloatTy::F128,
|
||||
Signature { args: &[Ty::F128, Ty::F128, Ty::F128], returns: &[Ty::F128] },
|
||||
Signature {
|
||||
args: &[Ty::F128, Ty::F128, Ty::F128],
|
||||
returns: &[Ty::F128],
|
||||
},
|
||||
None,
|
||||
&["fmaf128"],
|
||||
),
|
||||
(
|
||||
// `(f32) -> i32`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32], returns: &[Ty::I32] },
|
||||
Signature {
|
||||
args: &[Ty::F32],
|
||||
returns: &[Ty::I32],
|
||||
},
|
||||
None,
|
||||
&["ilogbf"],
|
||||
),
|
||||
(
|
||||
// `(f64) -> i32`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64], returns: &[Ty::I32] },
|
||||
Signature {
|
||||
args: &[Ty::F64],
|
||||
returns: &[Ty::I32],
|
||||
},
|
||||
None,
|
||||
&["ilogb"],
|
||||
),
|
||||
(
|
||||
// `(i32, f32) -> f32`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::I32, Ty::F32], returns: &[Ty::F32] },
|
||||
Signature {
|
||||
args: &[Ty::I32, Ty::F32],
|
||||
returns: &[Ty::F32],
|
||||
},
|
||||
None,
|
||||
&["jnf", "ynf"],
|
||||
),
|
||||
(
|
||||
// `(i32, f64) -> f64`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::I32, Ty::F64], returns: &[Ty::F64] },
|
||||
Signature {
|
||||
args: &[Ty::I32, Ty::F64],
|
||||
returns: &[Ty::F64],
|
||||
},
|
||||
None,
|
||||
&["jn", "yn"],
|
||||
),
|
||||
(
|
||||
// `(f16, i32) -> f16`
|
||||
FloatTy::F16,
|
||||
Signature { args: &[Ty::F16, Ty::I32], returns: &[Ty::F16] },
|
||||
Signature {
|
||||
args: &[Ty::F16, Ty::I32],
|
||||
returns: &[Ty::F16],
|
||||
},
|
||||
None,
|
||||
&["ldexpf16", "scalbnf16"],
|
||||
),
|
||||
(
|
||||
// `(f32, i32) -> f32`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32, Ty::I32], returns: &[Ty::F32] },
|
||||
Signature {
|
||||
args: &[Ty::F32, Ty::I32],
|
||||
returns: &[Ty::F32],
|
||||
},
|
||||
None,
|
||||
&["ldexpf", "scalbnf"],
|
||||
),
|
||||
(
|
||||
// `(f64, i64) -> f64`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64, Ty::I32], returns: &[Ty::F64] },
|
||||
Signature {
|
||||
args: &[Ty::F64, Ty::I32],
|
||||
returns: &[Ty::F64],
|
||||
},
|
||||
None,
|
||||
&["ldexp", "scalbn"],
|
||||
),
|
||||
(
|
||||
// `(f128, i32) -> f128`
|
||||
FloatTy::F128,
|
||||
Signature { args: &[Ty::F128, Ty::I32], returns: &[Ty::F128] },
|
||||
Signature {
|
||||
args: &[Ty::F128, Ty::I32],
|
||||
returns: &[Ty::F128],
|
||||
},
|
||||
None,
|
||||
&["ldexpf128", "scalbnf128"],
|
||||
),
|
||||
(
|
||||
// `(f32, &mut f32) -> f32` as `(f32) -> (f32, f32)`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32], returns: &[Ty::F32, Ty::F32] },
|
||||
Some(Signature { args: &[Ty::F32, Ty::MutF32], returns: &[Ty::F32] }),
|
||||
Signature {
|
||||
args: &[Ty::F32],
|
||||
returns: &[Ty::F32, Ty::F32],
|
||||
},
|
||||
Some(Signature {
|
||||
args: &[Ty::F32, Ty::MutF32],
|
||||
returns: &[Ty::F32],
|
||||
}),
|
||||
&["modff"],
|
||||
),
|
||||
(
|
||||
// `(f64, &mut f64) -> f64` as `(f64) -> (f64, f64)`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64], returns: &[Ty::F64, Ty::F64] },
|
||||
Some(Signature { args: &[Ty::F64, Ty::MutF64], returns: &[Ty::F64] }),
|
||||
Signature {
|
||||
args: &[Ty::F64],
|
||||
returns: &[Ty::F64, Ty::F64],
|
||||
},
|
||||
Some(Signature {
|
||||
args: &[Ty::F64, Ty::MutF64],
|
||||
returns: &[Ty::F64],
|
||||
}),
|
||||
&["modf"],
|
||||
),
|
||||
(
|
||||
// `(f32, &mut c_int) -> f32` as `(f32) -> (f32, i32)`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32], returns: &[Ty::F32, Ty::I32] },
|
||||
Some(Signature { args: &[Ty::F32, Ty::MutCInt], returns: &[Ty::F32] }),
|
||||
Signature {
|
||||
args: &[Ty::F32],
|
||||
returns: &[Ty::F32, Ty::I32],
|
||||
},
|
||||
Some(Signature {
|
||||
args: &[Ty::F32, Ty::MutCInt],
|
||||
returns: &[Ty::F32],
|
||||
}),
|
||||
&["frexpf", "lgammaf_r"],
|
||||
),
|
||||
(
|
||||
// `(f64, &mut c_int) -> f64` as `(f64) -> (f64, i32)`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64], returns: &[Ty::F64, Ty::I32] },
|
||||
Some(Signature { args: &[Ty::F64, Ty::MutCInt], returns: &[Ty::F64] }),
|
||||
Signature {
|
||||
args: &[Ty::F64],
|
||||
returns: &[Ty::F64, Ty::I32],
|
||||
},
|
||||
Some(Signature {
|
||||
args: &[Ty::F64, Ty::MutCInt],
|
||||
returns: &[Ty::F64],
|
||||
}),
|
||||
&["frexp", "lgamma_r"],
|
||||
),
|
||||
(
|
||||
// `(f32, f32, &mut c_int) -> f32` as `(f32, f32) -> (f32, i32)`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32, Ty::F32], returns: &[Ty::F32, Ty::I32] },
|
||||
Some(Signature { args: &[Ty::F32, Ty::F32, Ty::MutCInt], returns: &[Ty::F32] }),
|
||||
Signature {
|
||||
args: &[Ty::F32, Ty::F32],
|
||||
returns: &[Ty::F32, Ty::I32],
|
||||
},
|
||||
Some(Signature {
|
||||
args: &[Ty::F32, Ty::F32, Ty::MutCInt],
|
||||
returns: &[Ty::F32],
|
||||
}),
|
||||
&["remquof"],
|
||||
),
|
||||
(
|
||||
// `(f64, f64, &mut c_int) -> f64` as `(f64, f64) -> (f64, i32)`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64, Ty::F64], returns: &[Ty::F64, Ty::I32] },
|
||||
Some(Signature { args: &[Ty::F64, Ty::F64, Ty::MutCInt], returns: &[Ty::F64] }),
|
||||
Signature {
|
||||
args: &[Ty::F64, Ty::F64],
|
||||
returns: &[Ty::F64, Ty::I32],
|
||||
},
|
||||
Some(Signature {
|
||||
args: &[Ty::F64, Ty::F64, Ty::MutCInt],
|
||||
returns: &[Ty::F64],
|
||||
}),
|
||||
&["remquo"],
|
||||
),
|
||||
(
|
||||
// `(f32, &mut f32, &mut f32)` as `(f32) -> (f32, f32)`
|
||||
FloatTy::F32,
|
||||
Signature { args: &[Ty::F32], returns: &[Ty::F32, Ty::F32] },
|
||||
Some(Signature { args: &[Ty::F32, Ty::MutF32, Ty::MutF32], returns: &[] }),
|
||||
Signature {
|
||||
args: &[Ty::F32],
|
||||
returns: &[Ty::F32, Ty::F32],
|
||||
},
|
||||
Some(Signature {
|
||||
args: &[Ty::F32, Ty::MutF32, Ty::MutF32],
|
||||
returns: &[],
|
||||
}),
|
||||
&["sincosf"],
|
||||
),
|
||||
(
|
||||
// `(f64, &mut f64, &mut f64)` as `(f64) -> (f64, f64)`
|
||||
FloatTy::F64,
|
||||
Signature { args: &[Ty::F64], returns: &[Ty::F64, Ty::F64] },
|
||||
Some(Signature { args: &[Ty::F64, Ty::MutF64, Ty::MutF64], returns: &[] }),
|
||||
Signature {
|
||||
args: &[Ty::F64],
|
||||
returns: &[Ty::F64, Ty::F64],
|
||||
},
|
||||
Some(Signature {
|
||||
args: &[Ty::F64, Ty::MutF64, Ty::MutF64],
|
||||
returns: &[],
|
||||
}),
|
||||
&["sincos"],
|
||||
),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const LIB_NAME: &str = "musl_math_prefixed";
|
|||
|
||||
/// Files that have more than one symbol. Map of file names to the symbols defined in that file.
|
||||
const MULTIPLE_SYMBOLS: &[(&str, &[&str])] = &[
|
||||
("__invtrigl", &["__invtrigl", "__invtrigl_R", "__pio2_hi", "__pio2_lo"]),
|
||||
(
|
||||
"__invtrigl",
|
||||
&["__invtrigl", "__invtrigl_R", "__pio2_hi", "__pio2_lo"],
|
||||
),
|
||||
("__polevll", &["__polevll", "__p1evll"]),
|
||||
("erf", &["erf", "erfc"]),
|
||||
("erff", &["erff", "erfcf"]),
|
||||
|
|
@ -82,9 +85,16 @@ impl Config {
|
|||
let musl_dir = manifest_dir.join("musl");
|
||||
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
let musl_arch = if target_arch == "x86" { "i386".to_owned() } else { target_arch.clone() };
|
||||
let musl_arch = if target_arch == "x86" {
|
||||
"i386".to_owned()
|
||||
} else {
|
||||
target_arch.clone()
|
||||
};
|
||||
|
||||
println!("cargo::rerun-if-changed={}/c_patches", manifest_dir.display());
|
||||
println!(
|
||||
"cargo::rerun-if-changed={}/c_patches",
|
||||
manifest_dir.display()
|
||||
);
|
||||
println!("cargo::rerun-if-changed={}", musl_dir.display());
|
||||
|
||||
Self {
|
||||
|
|
@ -108,7 +118,10 @@ fn build_musl_math(cfg: &Config) {
|
|||
let musl_dir = &cfg.musl_dir;
|
||||
let math = musl_dir.join("src/math");
|
||||
let arch_dir = musl_dir.join("arch").join(&cfg.musl_arch);
|
||||
assert!(math.exists(), "musl source not found. Is the submodule up to date?");
|
||||
assert!(
|
||||
math.exists(),
|
||||
"musl source not found. Is the submodule up to date?"
|
||||
);
|
||||
|
||||
let source_map = find_math_source(&math, cfg);
|
||||
let out_path = cfg.out_dir.join(format!("lib{LIB_NAME}.a"));
|
||||
|
|
@ -125,7 +138,11 @@ fn build_musl_math(cfg: &Config) {
|
|||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(sed_stat.status.success(), "sed command failed: {:?}", sed_stat.status);
|
||||
assert!(
|
||||
sed_stat.status.success(),
|
||||
"sed command failed: {:?}",
|
||||
sed_stat.status
|
||||
);
|
||||
|
||||
fs::write(obj_include.join("bits/alltypes.h"), sed_stat.stdout).unwrap();
|
||||
|
||||
|
|
@ -163,8 +180,9 @@ fn build_musl_math(cfg: &Config) {
|
|||
|
||||
// Trickery! Redefine the symbol names to have the prefix `musl_`, which allows us to
|
||||
// differentiate these symbols from whatever we provide.
|
||||
if let Some((_names, syms)) =
|
||||
MULTIPLE_SYMBOLS.iter().find(|(name, _syms)| *name == sym_name)
|
||||
if let Some((_names, syms)) = MULTIPLE_SYMBOLS
|
||||
.iter()
|
||||
.find(|(name, _syms)| *name == sym_name)
|
||||
{
|
||||
// Handle the occasional file that defines multiple symbols
|
||||
for sym in *syms {
|
||||
|
|
@ -291,21 +309,34 @@ fn validate_archive_symbols(out_path: &Path) {
|
|||
];
|
||||
|
||||
// List global undefined symbols
|
||||
let out =
|
||||
Command::new("nm").arg("-guj").arg(out_path).stderr(Stdio::inherit()).output().unwrap();
|
||||
let out = Command::new("nm")
|
||||
.arg("-guj")
|
||||
.arg(out_path)
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let undef = str::from_utf8(&out.stdout).unwrap();
|
||||
let mut undef = undef.lines().collect::<Vec<_>>();
|
||||
undef.retain(|sym| {
|
||||
// Account for file formats that add a leading `_`
|
||||
!ALLOWED_UNDEF_PFX.iter().any(|pfx| sym.starts_with(pfx) || sym[1..].starts_with(pfx))
|
||||
!ALLOWED_UNDEF_PFX
|
||||
.iter()
|
||||
.any(|pfx| sym.starts_with(pfx) || sym[1..].starts_with(pfx))
|
||||
});
|
||||
|
||||
assert!(undef.is_empty(), "found disallowed undefined symbols: {undef:#?}");
|
||||
assert!(
|
||||
undef.is_empty(),
|
||||
"found disallowed undefined symbols: {undef:#?}"
|
||||
);
|
||||
|
||||
// Find any symbols that are missing the `_musl_` prefix`
|
||||
let out =
|
||||
Command::new("nm").arg("-gUj").arg(out_path).stderr(Stdio::inherit()).output().unwrap();
|
||||
let out = Command::new("nm")
|
||||
.arg("-gUj")
|
||||
.arg(out_path)
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let defined = str::from_utf8(&out.stdout).unwrap();
|
||||
let mut defined = defined.lines().collect::<Vec<_>>();
|
||||
|
|
|
|||
|
|
@ -221,7 +221,11 @@ macro_rules! impl_parse_tuple_via_rug {
|
|||
impl ParseTuple for ($ty, $ty, $ty) {
|
||||
fn parse(input: &[&str]) -> Self {
|
||||
assert_eq!(input.len(), 3, "expected three arguments, got {input:?}");
|
||||
(parse_rug(input, 0), parse_rug(input, 1), parse_rug(input, 2))
|
||||
(
|
||||
parse_rug(input, 0),
|
||||
parse_rug(input, 1),
|
||||
parse_rug(input, 2),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ use libm_test::generate::random::RandomInput;
|
|||
use libm_test::{CheckBasis, CheckCtx, GeneratorKind, MathOp, TupleCall};
|
||||
|
||||
/// Benchmark with this many items to get a variety
|
||||
const BENCH_ITER_ITEMS: usize = if cfg!(feature = "short-benchmarks") { 50 } else { 500 };
|
||||
const BENCH_ITER_ITEMS: usize = if cfg!(feature = "short-benchmarks") {
|
||||
50
|
||||
} else {
|
||||
500
|
||||
};
|
||||
|
||||
/// Extra parameters we only care about if we are benchmarking against musl.
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -53,8 +57,10 @@ where
|
|||
let name = Op::NAME;
|
||||
|
||||
let ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Musl, GeneratorKind::Random);
|
||||
let benchvec: Vec<_> =
|
||||
random::get_test_cases::<Op::RustArgs>(&ctx).0.take(BENCH_ITER_ITEMS).collect();
|
||||
let benchvec: Vec<_> = random::get_test_cases::<Op::RustArgs>(&ctx)
|
||||
.0
|
||||
.take(BENCH_ITER_ITEMS)
|
||||
.collect();
|
||||
|
||||
// Perform a sanity check that we are benchmarking the same thing
|
||||
// Don't test against musl if it is not available
|
||||
|
|
@ -73,7 +79,10 @@ where
|
|||
let musl_res = input.call(musl_fn);
|
||||
let crate_res = input.call(Op::ROUTINE);
|
||||
|
||||
crate_res.validate(musl_res, input, &ctx).context(name).unwrap();
|
||||
crate_res
|
||||
.validate(musl_res, input, &ctx)
|
||||
.context(name)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "build-musl"))]
|
||||
|
|
|
|||
|
|
@ -56,7 +56,13 @@ where
|
|||
Op::RustArgs: SpacedInput<Op>,
|
||||
{
|
||||
let mut ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr, GeneratorKind::QuickSpaced);
|
||||
plot_one_generator(out_dir, &ctx, "logspace", config, spaced::get_test_cases::<Op>(&ctx).0);
|
||||
plot_one_generator(
|
||||
out_dir,
|
||||
&ctx,
|
||||
"logspace",
|
||||
config,
|
||||
spaced::get_test_cases::<Op>(&ctx).0,
|
||||
);
|
||||
ctx.gen_kind = GeneratorKind::EdgeCases;
|
||||
plot_one_generator(
|
||||
out_dir,
|
||||
|
|
|
|||
|
|
@ -67,16 +67,25 @@ impl<F: fmt::Debug, I: fmt::Debug> EitherPrim<F, I> {
|
|||
/// Convenience 1-dimensional float domains.
|
||||
impl<F: Float> Domain<F> {
|
||||
/// x ∈ ℝ
|
||||
const UNBOUNDED: Self =
|
||||
Self { start: Bound::Unbounded, end: Bound::Unbounded, check_points: None };
|
||||
const UNBOUNDED: Self = Self {
|
||||
start: Bound::Unbounded,
|
||||
end: Bound::Unbounded,
|
||||
check_points: None,
|
||||
};
|
||||
|
||||
/// x ∈ ℝ >= 0
|
||||
const POSITIVE: Self =
|
||||
Self { start: Bound::Included(F::ZERO), end: Bound::Unbounded, check_points: None };
|
||||
const POSITIVE: Self = Self {
|
||||
start: Bound::Included(F::ZERO),
|
||||
end: Bound::Unbounded,
|
||||
check_points: None,
|
||||
};
|
||||
|
||||
/// x ∈ ℝ > 0
|
||||
const STRICTLY_POSITIVE: Self =
|
||||
Self { start: Bound::Excluded(F::ZERO), end: Bound::Unbounded, check_points: None };
|
||||
const STRICTLY_POSITIVE: Self = Self {
|
||||
start: Bound::Excluded(F::ZERO),
|
||||
end: Bound::Unbounded,
|
||||
check_points: None,
|
||||
};
|
||||
|
||||
/// Wrap in the float variant of [`EitherPrim`].
|
||||
const fn into_prim_float<I>(self) -> EitherPrim<Self, Domain<I>> {
|
||||
|
|
@ -87,8 +96,11 @@ impl<F: Float> Domain<F> {
|
|||
/// Convenience 1-dimensional integer domains.
|
||||
impl<I: Int> Domain<I> {
|
||||
/// x ∈ ℝ
|
||||
const UNBOUNDED_INT: Self =
|
||||
Self { start: Bound::Unbounded, end: Bound::Unbounded, check_points: None };
|
||||
const UNBOUNDED_INT: Self = Self {
|
||||
start: Bound::Unbounded,
|
||||
end: Bound::Unbounded,
|
||||
check_points: None,
|
||||
};
|
||||
|
||||
/// Wrap in the int variant of [`EitherPrim`].
|
||||
const fn into_prim_int<F>(self) -> EitherPrim<Domain<F>, Self> {
|
||||
|
|
@ -99,13 +111,18 @@ impl<I: Int> Domain<I> {
|
|||
/// Multidimensional domains, represented as an array of 1-D domains.
|
||||
impl<F: Float, I: Int> EitherPrim<Domain<F>, Domain<I>> {
|
||||
/// x ∈ ℝ
|
||||
const UNBOUNDED1: [Self; 1] =
|
||||
[Domain { start: Bound::Unbounded, end: Bound::Unbounded, check_points: None }
|
||||
.into_prim_float()];
|
||||
const UNBOUNDED1: [Self; 1] = [Domain {
|
||||
start: Bound::Unbounded,
|
||||
end: Bound::Unbounded,
|
||||
check_points: None,
|
||||
}
|
||||
.into_prim_float()];
|
||||
|
||||
/// {x1, x2} ∈ ℝ
|
||||
const UNBOUNDED2: [Self; 2] =
|
||||
[Domain::UNBOUNDED.into_prim_float(), Domain::UNBOUNDED.into_prim_float()];
|
||||
const UNBOUNDED2: [Self; 2] = [
|
||||
Domain::UNBOUNDED.into_prim_float(),
|
||||
Domain::UNBOUNDED.into_prim_float(),
|
||||
];
|
||||
|
||||
/// {x1, x2, x3} ∈ ℝ
|
||||
const UNBOUNDED3: [Self; 3] = [
|
||||
|
|
@ -115,8 +132,10 @@ impl<F: Float, I: Int> EitherPrim<Domain<F>, Domain<I>> {
|
|||
];
|
||||
|
||||
/// {x1, x2} ∈ ℝ, one float and one int
|
||||
const UNBOUNDED_F_I: [Self; 2] =
|
||||
[Domain::UNBOUNDED.into_prim_float(), Domain::UNBOUNDED_INT.into_prim_int()];
|
||||
const UNBOUNDED_F_I: [Self; 2] = [
|
||||
Domain::UNBOUNDED.into_prim_float(),
|
||||
Domain::UNBOUNDED_INT.into_prim_int(),
|
||||
];
|
||||
|
||||
/// x ∈ ℝ >= 0
|
||||
const POSITIVE: [Self; 1] = [Domain::POSITIVE.into_prim_float()];
|
||||
|
|
@ -133,9 +152,12 @@ impl<F: Float, I: Int> EitherPrim<Domain<F>, Domain<I>> {
|
|||
.into_prim_float()];
|
||||
|
||||
/// Domain for `acosh`
|
||||
const ACOSH: [Self; 1] =
|
||||
[Domain { start: Bound::Included(F::ONE), end: Bound::Unbounded, check_points: None }
|
||||
.into_prim_float()];
|
||||
const ACOSH: [Self; 1] = [Domain {
|
||||
start: Bound::Included(F::ONE),
|
||||
end: Bound::Unbounded,
|
||||
check_points: None,
|
||||
}
|
||||
.into_prim_float()];
|
||||
|
||||
/// Domain for `atanh`
|
||||
const ATANH: [Self; 1] = [Domain {
|
||||
|
|
@ -157,9 +179,12 @@ impl<F: Float, I: Int> EitherPrim<Domain<F>, Domain<I>> {
|
|||
const LOG: [Self; 1] = Self::STRICTLY_POSITIVE;
|
||||
|
||||
/// Domain for `log1p` i.e. `log(1 + x)`
|
||||
const LOG1P: [Self; 1] =
|
||||
[Domain { start: Bound::Excluded(F::NEG_ONE), end: Bound::Unbounded, check_points: None }
|
||||
.into_prim_float()];
|
||||
const LOG1P: [Self; 1] = [Domain {
|
||||
start: Bound::Excluded(F::NEG_ONE),
|
||||
end: Bound::Unbounded,
|
||||
check_points: None,
|
||||
}
|
||||
.into_prim_float()];
|
||||
|
||||
/// Domain for `sqrt`
|
||||
const SQRT: [Self; 1] = Self::POSITIVE;
|
||||
|
|
@ -187,8 +212,10 @@ impl<F: Float, I: Int> EitherPrim<Domain<F>, Domain<I>> {
|
|||
/// Domain for `jn` and `yn`.
|
||||
// FIXME: the domain should provide some sort of "reasonable range" so we don't actually test
|
||||
// the entire system unbounded.
|
||||
const BESSEL_N: [Self; 2] =
|
||||
[Domain::UNBOUNDED_INT.into_prim_int(), Domain::UNBOUNDED.into_prim_float()];
|
||||
const BESSEL_N: [Self; 2] = [
|
||||
Domain::UNBOUNDED_INT.into_prim_int(),
|
||||
Domain::UNBOUNDED.into_prim_float(),
|
||||
];
|
||||
}
|
||||
|
||||
/// Get the domain for a given function.
|
||||
|
|
|
|||
|
|
@ -498,6 +498,8 @@ impl fmt::LowerHex for f8 {
|
|||
}
|
||||
|
||||
pub const fn hf8(s: &str) -> f8 {
|
||||
let Ok(bits) = libm::support::hex_float::parse_hex_exact(s, 8, 3) else { panic!() };
|
||||
let Ok(bits) = libm::support::hex_float::parse_hex_exact(s, 8, 3) else {
|
||||
panic!()
|
||||
};
|
||||
f8(bits as u8)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ pub struct KnownSize<I> {
|
|||
|
||||
impl<I> KnownSize<I> {
|
||||
pub fn new(iter: I, total: u64) -> Self {
|
||||
Self { total, current: 0, iter }
|
||||
Self {
|
||||
total,
|
||||
current: 0,
|
||||
iter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +34,10 @@ impl<I: Iterator> Iterator for KnownSize<I> {
|
|||
return next;
|
||||
}
|
||||
|
||||
assert_eq!(self.current, self.total, "total items did not match expected");
|
||||
assert_eq!(
|
||||
self.current, self.total,
|
||||
"total items did not match expected"
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,14 +20,21 @@ pub struct TestCase<Op: MathOp> {
|
|||
impl<Op: MathOp> TestCase<Op> {
|
||||
#[expect(dead_code)]
|
||||
fn append_inputs(v: &mut Vec<Self>, l: &[Op::RustArgs]) {
|
||||
v.extend(l.iter().copied().map(|input| Self { input, output: None }));
|
||||
v.extend(l.iter().copied().map(|input| Self {
|
||||
input,
|
||||
output: None,
|
||||
}));
|
||||
}
|
||||
|
||||
fn append_pairs(v: &mut Vec<Self>, l: &[(Op::RustArgs, Option<Op::RustRet>)])
|
||||
where
|
||||
Op::RustRet: Copy,
|
||||
{
|
||||
v.extend(l.iter().copied().map(|(input, output)| Self { input, output }));
|
||||
v.extend(
|
||||
l.iter()
|
||||
.copied()
|
||||
.map(|(input, output)| Self { input, output }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -603,9 +610,15 @@ fn rint_cases() -> Vec<TestCase<op::rint::Routine>> {
|
|||
&[
|
||||
// Known failure on i586
|
||||
#[cfg(not(x86_no_sse))]
|
||||
((hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff994000p+38"))),
|
||||
(
|
||||
(hf64!("-0x1.e3f13ff995ffcp+38"),),
|
||||
Some(hf64!("-0x1.e3f13ff994000p+38")),
|
||||
),
|
||||
#[cfg(x86_no_sse)]
|
||||
((hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff998000p+38"))),
|
||||
(
|
||||
(hf64!("-0x1.e3f13ff995ffcp+38"),),
|
||||
Some(hf64!("-0x1.e3f13ff998000p+38")),
|
||||
),
|
||||
],
|
||||
);
|
||||
v
|
||||
|
|
@ -655,9 +668,15 @@ fn roundeven_cases() -> Vec<TestCase<op::roundeven::Routine>> {
|
|||
&[
|
||||
// Known failure on i586
|
||||
#[cfg(not(x86_no_sse))]
|
||||
((hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff994000p+38"))),
|
||||
(
|
||||
(hf64!("-0x1.e3f13ff995ffcp+38"),),
|
||||
Some(hf64!("-0x1.e3f13ff994000p+38")),
|
||||
),
|
||||
#[cfg(x86_no_sse)]
|
||||
((hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff998000p+38"))),
|
||||
(
|
||||
(hf64!("-0x1.e3f13ff995ffcp+38"),),
|
||||
Some(hf64!("-0x1.e3f13ff998000p+38")),
|
||||
),
|
||||
],
|
||||
);
|
||||
v
|
||||
|
|
@ -832,7 +851,9 @@ where
|
|||
{
|
||||
assert_eq!(ctx.basis, CheckBasis::None);
|
||||
assert_eq!(ctx.gen_kind, GeneratorKind::List);
|
||||
Op::get_cases().into_iter().filter_map(|x| x.output.map(|o| (x.input, o)))
|
||||
Op::get_cases()
|
||||
.into_iter()
|
||||
.filter_map(|x| x.output.map(|o| (x.input, o)))
|
||||
}
|
||||
|
||||
/// Opposite of the above; extract only test cases that don't have a known output, to be run
|
||||
|
|
@ -847,7 +868,18 @@ where
|
|||
assert_eq!(ctx.gen_kind, GeneratorKind::List);
|
||||
|
||||
let cases = Op::get_cases();
|
||||
let count: u64 = cases.iter().filter(|case| case.output.is_none()).count().try_into().unwrap();
|
||||
let count: u64 = cases
|
||||
.iter()
|
||||
.filter(|case| case.output.is_none())
|
||||
.count()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
(cases.into_iter().filter(|x| x.output.is_none()).map(|x| x.input), count)
|
||||
(
|
||||
cases
|
||||
.into_iter()
|
||||
.filter(|x| x.output.is_none())
|
||||
.map(|x| x.input),
|
||||
count,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,7 +249,11 @@ macro_rules! impl_edge_case_input {
|
|||
.flat_map(move |(first, second)| {
|
||||
iter2.clone().map(move |third| (first, second, third))
|
||||
});
|
||||
let count = steps0.checked_mul(steps1).unwrap().checked_mul(steps2).unwrap();
|
||||
let count = steps0
|
||||
.checked_mul(steps1)
|
||||
.unwrap()
|
||||
.checked_mul(steps2)
|
||||
.unwrap();
|
||||
|
||||
(iter, count)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,10 @@ impl_random_input!(f128);
|
|||
/// Create a test case iterator.
|
||||
pub fn get_test_cases<RustArgs: RandomInput>(
|
||||
ctx: &CheckCtx,
|
||||
) -> (impl Iterator<Item = RustArgs> + Send + use<'_, RustArgs>, u64) {
|
||||
) -> (
|
||||
impl Iterator<Item = RustArgs> + Send + use<'_, RustArgs>,
|
||||
u64,
|
||||
) {
|
||||
let (iter, count) = RustArgs::get_cases(ctx);
|
||||
|
||||
// Wrap in `KnownSize` so we get an assertion if the cuunt is wrong.
|
||||
|
|
|
|||
|
|
@ -70,7 +70,9 @@ fn value_count<F: Float>() -> Option<u64>
|
|||
where
|
||||
u64: TryFrom<F::Int>,
|
||||
{
|
||||
u64::try_from(F::Int::MAX).ok().and_then(|max| max.checked_add(1))
|
||||
u64::try_from(F::Int::MAX)
|
||||
.ok()
|
||||
.and_then(|max| max.checked_add(1))
|
||||
}
|
||||
|
||||
/// Returns an iterator of every possible value of type `F`.
|
||||
|
|
@ -162,8 +164,11 @@ macro_rules! impl_spaced_input {
|
|||
.flat_map(move |(first, second)| {
|
||||
iter2.clone().map(move |third| (first, second, third))
|
||||
});
|
||||
let count =
|
||||
steps0.checked_mul(steps1).unwrap().checked_mul(steps2).unwrap();
|
||||
let count = steps0
|
||||
.checked_mul(steps1)
|
||||
.unwrap()
|
||||
.checked_mul(steps2)
|
||||
.unwrap();
|
||||
|
||||
(EitherIter::B(iter), count)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,12 @@ pub fn test_log(s: &str) {
|
|||
return None;
|
||||
};
|
||||
|
||||
PathBuf::from(x).parent().unwrap().parent().unwrap().join("target")
|
||||
PathBuf::from(x)
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("target")
|
||||
}
|
||||
};
|
||||
let outfile = target_dir.join("test-log.txt");
|
||||
|
|
@ -81,7 +86,9 @@ pub fn test_log(s: &str) {
|
|||
.append(true)
|
||||
.open(outfile)
|
||||
.expect("failed to open logfile");
|
||||
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
|
||||
let now = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
|
||||
writeln!(f, "\n\nTest run at {}", now.as_secs()).unwrap();
|
||||
writeln!(f, "arch: {}", env::consts::ARCH).unwrap();
|
||||
|
|
|
|||
|
|
@ -180,8 +180,17 @@ impl<F: FloatExt> Consts<F> {
|
|||
neg_max_snan,
|
||||
} = self;
|
||||
|
||||
[pos_nan, neg_nan, max_qnan, min_snan, max_snan, neg_max_qnan, neg_min_snan, neg_max_snan]
|
||||
.into_iter()
|
||||
[
|
||||
pos_nan,
|
||||
neg_nan,
|
||||
max_qnan,
|
||||
min_snan,
|
||||
max_snan,
|
||||
neg_max_qnan,
|
||||
neg_min_snan,
|
||||
neg_max_snan,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +238,9 @@ where
|
|||
assert!(!end.is_nan());
|
||||
assert!(end >= start);
|
||||
|
||||
let steps = steps.checked_sub(F::Int::ONE).expect("`steps` must be at least 2");
|
||||
let steps = steps
|
||||
.checked_sub(F::Int::ONE)
|
||||
.expect("`steps` must be at least 2");
|
||||
let between = ulp_between(start, end).expect("`start` or `end` is NaN");
|
||||
let spacing = (between / steps).max(F::Int::ONE);
|
||||
let steps = steps.min(between); // At maximum, one step per ULP
|
||||
|
|
@ -283,15 +294,22 @@ mod tests {
|
|||
if i == 0 {
|
||||
assert_eq!(down, f8::NEG_INFINITY.to_bits(), "{i} next_down({v:#010b})");
|
||||
} else {
|
||||
let expected =
|
||||
if v == f8::ZERO { 1 | f8::SIGN_MASK } else { f8::ALL[i - 1].to_bits() };
|
||||
let expected = if v == f8::ZERO {
|
||||
1 | f8::SIGN_MASK
|
||||
} else {
|
||||
f8::ALL[i - 1].to_bits()
|
||||
};
|
||||
assert_eq!(down, expected, "{i} next_down({v:#010b})");
|
||||
}
|
||||
|
||||
if i == f8::ALL_LEN - 1 {
|
||||
assert_eq!(up, f8::INFINITY.to_bits(), "{i} next_up({v:#010b})");
|
||||
} else {
|
||||
let expected = if v == f8::NEG_ZERO { 1 } else { f8::ALL[i + 1].to_bits() };
|
||||
let expected = if v == f8::NEG_ZERO {
|
||||
1
|
||||
} else {
|
||||
f8::ALL[i + 1].to_bits()
|
||||
};
|
||||
assert_eq!(up, expected, "{i} next_up({v:#010b})");
|
||||
}
|
||||
}
|
||||
|
|
@ -300,8 +318,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_next_up_down_inf_nan() {
|
||||
assert_eq!(f8::NEG_INFINITY.next_up().to_bits(), f8::ALL[0].to_bits(),);
|
||||
assert_eq!(f8::NEG_INFINITY.next_down().to_bits(), f8::NEG_INFINITY.to_bits(),);
|
||||
assert_eq!(f8::INFINITY.next_down().to_bits(), f8::ALL[f8::ALL_LEN - 1].to_bits(),);
|
||||
assert_eq!(
|
||||
f8::NEG_INFINITY.next_down().to_bits(),
|
||||
f8::NEG_INFINITY.to_bits(),
|
||||
);
|
||||
assert_eq!(
|
||||
f8::INFINITY.next_down().to_bits(),
|
||||
f8::ALL[f8::ALL_LEN - 1].to_bits(),
|
||||
);
|
||||
assert_eq!(f8::INFINITY.next_up().to_bits(), f8::INFINITY.to_bits(),);
|
||||
assert_eq!(f8::NAN.next_up().to_bits(), f8::NAN.to_bits(),);
|
||||
assert_eq!(f8::NAN.next_down().to_bits(), f8::NAN.to_bits(),);
|
||||
|
|
@ -321,7 +345,10 @@ mod tests {
|
|||
|
||||
// Check across zero
|
||||
assert_eq!(f8::from_bits(0b1_0000_111).n_up(8).to_bits(), 0b0_0000_001);
|
||||
assert_eq!(f8::from_bits(0b0_0000_111).n_down(8).to_bits(), 0b1_0000_001);
|
||||
assert_eq!(
|
||||
f8::from_bits(0b0_0000_111).n_down(8).to_bits(),
|
||||
0b1_0000_001
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -337,13 +364,25 @@ mod tests {
|
|||
#[test]
|
||||
fn test_n_up_down_inf_nan_zero() {
|
||||
assert_eq!(f8::NEG_INFINITY.n_up(1).to_bits(), f8::ALL[0].to_bits());
|
||||
assert_eq!(f8::NEG_INFINITY.n_up(239).to_bits(), f8::ALL[f8::ALL_LEN - 1].to_bits());
|
||||
assert_eq!(
|
||||
f8::NEG_INFINITY.n_up(239).to_bits(),
|
||||
f8::ALL[f8::ALL_LEN - 1].to_bits()
|
||||
);
|
||||
assert_eq!(f8::NEG_INFINITY.n_up(240).to_bits(), f8::INFINITY.to_bits());
|
||||
assert_eq!(f8::NEG_INFINITY.n_down(u8::MAX).to_bits(), f8::NEG_INFINITY.to_bits());
|
||||
assert_eq!(
|
||||
f8::NEG_INFINITY.n_down(u8::MAX).to_bits(),
|
||||
f8::NEG_INFINITY.to_bits()
|
||||
);
|
||||
|
||||
assert_eq!(f8::INFINITY.n_down(1).to_bits(), f8::ALL[f8::ALL_LEN - 1].to_bits());
|
||||
assert_eq!(
|
||||
f8::INFINITY.n_down(1).to_bits(),
|
||||
f8::ALL[f8::ALL_LEN - 1].to_bits()
|
||||
);
|
||||
assert_eq!(f8::INFINITY.n_down(239).to_bits(), f8::ALL[0].to_bits());
|
||||
assert_eq!(f8::INFINITY.n_down(240).to_bits(), f8::NEG_INFINITY.to_bits());
|
||||
assert_eq!(
|
||||
f8::INFINITY.n_down(240).to_bits(),
|
||||
f8::NEG_INFINITY.to_bits()
|
||||
);
|
||||
assert_eq!(f8::INFINITY.n_up(u8::MAX).to_bits(), f8::INFINITY.to_bits());
|
||||
|
||||
assert_eq!(f8::NAN.n_up(u8::MAX).to_bits(), f8::NAN.to_bits());
|
||||
|
|
@ -381,7 +420,11 @@ mod tests {
|
|||
assert_eq!(down, expected, "{i} {n} n_down({v:#010b})");
|
||||
} else {
|
||||
// Overflow to -inf
|
||||
assert_eq!(down, f8::NEG_INFINITY.to_bits(), "{i} {n} n_down({v:#010b})");
|
||||
assert_eq!(
|
||||
down,
|
||||
f8::NEG_INFINITY.to_bits(),
|
||||
"{i} {n} n_down({v:#010b})"
|
||||
);
|
||||
}
|
||||
|
||||
let mut up_exp_idx = i + n;
|
||||
|
|
@ -438,13 +481,22 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_ulp_between_inf_nan_zero() {
|
||||
assert_eq!(ulp_between(f8::NEG_INFINITY, f8::INFINITY).unwrap(), f8::ALL_LEN as u8);
|
||||
assert_eq!(ulp_between(f8::INFINITY, f8::NEG_INFINITY).unwrap(), f8::ALL_LEN as u8);
|
||||
assert_eq!(
|
||||
ulp_between(f8::NEG_INFINITY, f8::INFINITY).unwrap(),
|
||||
f8::ALL_LEN as u8
|
||||
);
|
||||
assert_eq!(
|
||||
ulp_between(f8::INFINITY, f8::NEG_INFINITY).unwrap(),
|
||||
f8::ALL_LEN as u8
|
||||
);
|
||||
assert_eq!(
|
||||
ulp_between(f8::NEG_INFINITY, f8::ALL[f8::ALL_LEN - 1]).unwrap(),
|
||||
f8::ALL_LEN as u8 - 1
|
||||
);
|
||||
assert_eq!(ulp_between(f8::INFINITY, f8::ALL[0]).unwrap(), f8::ALL_LEN as u8 - 1);
|
||||
assert_eq!(
|
||||
ulp_between(f8::INFINITY, f8::ALL[0]).unwrap(),
|
||||
f8::ALL_LEN as u8 - 1
|
||||
);
|
||||
|
||||
assert_eq!(ulp_between(f8::ZERO, f8::NEG_ZERO).unwrap(), 0);
|
||||
assert_eq!(ulp_between(f8::NAN, f8::ZERO), None);
|
||||
|
|
@ -469,7 +521,12 @@ mod tests {
|
|||
// of steps.
|
||||
let (ls, count) = logspace(f8::from_bits(0x0), f8::from_bits(0x3), 10);
|
||||
let ls: Vec<_> = ls.collect();
|
||||
let exp = [f8::from_bits(0x0), f8::from_bits(0x1), f8::from_bits(0x2), f8::from_bits(0x3)];
|
||||
let exp = [
|
||||
f8::from_bits(0x0),
|
||||
f8::from_bits(0x1),
|
||||
f8::from_bits(0x2),
|
||||
f8::from_bits(0x3),
|
||||
];
|
||||
assert_eq!(ls, exp);
|
||||
assert_eq!(ls.len(), usize::from(count));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
use std::fmt;
|
||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
pub use shared::{FloatTy, MathOpInfo, Ty, ALL_OPERATIONS};
|
||||
pub use shared::{ALL_OPERATIONS, FloatTy, MathOpInfo, Ty};
|
||||
|
||||
use crate::{CheckOutput, Float, TupleCall};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ pub const EXTENSIVE_ITER_ENV: &str = "LIBM_EXTENSIVE_ITERATIONS";
|
|||
|
||||
/// The override value, if set by the above environment.
|
||||
static EXTENSIVE_ITER_OVERRIDE: LazyLock<Option<u64>> = LazyLock::new(|| {
|
||||
env::var(EXTENSIVE_ITER_ENV).map(|v| v.parse().expect("failed to parse iteration count")).ok()
|
||||
env::var(EXTENSIVE_ITER_ENV)
|
||||
.map(|v| v.parse().expect("failed to parse iteration count"))
|
||||
.ok()
|
||||
});
|
||||
|
||||
/// Specific tests that need to have a reduced amount of iterations to complete in a reasonable
|
||||
|
|
@ -115,7 +117,10 @@ static EXTENSIVE: LazyLock<Vec<Identifier>> = LazyLock::new(|| {
|
|||
let mut ret = Vec::new();
|
||||
|
||||
let append_ty_ops = |ret: &mut Vec<_>, fty: FloatTy| {
|
||||
let iter = Identifier::ALL.iter().filter(move |id| id.math_op().float_ty == fty).copied();
|
||||
let iter = Identifier::ALL
|
||||
.iter()
|
||||
.filter(move |id| id.math_op().float_ty == fty)
|
||||
.copied();
|
||||
ret.extend(iter);
|
||||
};
|
||||
|
||||
|
|
@ -276,7 +281,10 @@ pub fn iteration_count(ctx: &CheckCtx, argnum: usize) -> u64 {
|
|||
let seed_msg = match ctx.gen_kind {
|
||||
GeneratorKind::QuickSpaced | GeneratorKind::Extensive => String::new(),
|
||||
GeneratorKind::Random => {
|
||||
format!(" using `{SEED_ENV}={}`", str::from_utf8(SEED.as_slice()).unwrap())
|
||||
format!(
|
||||
" using `{SEED_ENV}={}`",
|
||||
str::from_utf8(SEED.as_slice()).unwrap()
|
||||
)
|
||||
}
|
||||
GeneratorKind::EdgeCases | GeneratorKind::List => unimplemented!(),
|
||||
};
|
||||
|
|
@ -303,7 +311,10 @@ pub fn int_range(ctx: &CheckCtx, argnum: usize) -> RangeInclusive<i32> {
|
|||
return i32::MIN..=i32::MAX;
|
||||
}
|
||||
|
||||
assert_eq!(argnum, 0, "For `jn`/`yn`, only the first argument takes an integer");
|
||||
assert_eq!(
|
||||
argnum, 0,
|
||||
"For `jn`/`yn`, only the first argument takes an integer"
|
||||
);
|
||||
|
||||
// The integer argument to `jn` is an iteration count. Limit this to ensure tests can be
|
||||
// completed in a reasonable amount of time.
|
||||
|
|
@ -331,7 +342,11 @@ pub fn check_point_count(ctx: &CheckCtx) -> usize {
|
|||
"check_point_count is intended for edge case tests"
|
||||
);
|
||||
let t_env = TestEnv::from_env(ctx);
|
||||
if t_env.slow_platform || !cfg!(optimizations_enabled) { 4 } else { 10 }
|
||||
if t_env.slow_platform || !cfg!(optimizations_enabled) {
|
||||
4
|
||||
} else {
|
||||
10
|
||||
}
|
||||
}
|
||||
|
||||
/// When validating points of interest (e.g. asymptotes, inflection points, extremes), also check
|
||||
|
|
|
|||
|
|
@ -328,7 +328,10 @@ where
|
|||
// Check when both are NaNs
|
||||
if actual.is_nan() && expected.is_nan() {
|
||||
if require_biteq && ctx.basis == CheckBasis::None {
|
||||
ensure!(actual.to_bits() == expected.to_bits(), "mismatched NaN bitpatterns");
|
||||
ensure!(
|
||||
actual.to_bits() == expected.to_bits(),
|
||||
"mismatched NaN bitpatterns"
|
||||
);
|
||||
}
|
||||
// By default, NaNs have nothing special to check.
|
||||
return Ok(());
|
||||
|
|
@ -340,7 +343,10 @@ where
|
|||
// Make sure that the signs are the same before checing ULP to avoid wraparound
|
||||
let act_sig = actual.signum();
|
||||
let exp_sig = expected.signum();
|
||||
ensure!(act_sig == exp_sig, "mismatched signs {act_sig:?} {exp_sig:?}");
|
||||
ensure!(
|
||||
act_sig == exp_sig,
|
||||
"mismatched signs {act_sig:?} {exp_sig:?}"
|
||||
);
|
||||
|
||||
if actual.is_infinite() ^ expected.is_infinite() {
|
||||
bail!("mismatched infinities");
|
||||
|
|
|
|||
|
|
@ -40,7 +40,10 @@ fn from_bigint(bx: &mut BigInt) -> u256 {
|
|||
let mut bres = [0u128, 0];
|
||||
bx.write_digits(&mut bres, Order::Lsf);
|
||||
bx.assign(0);
|
||||
u256 { lo: bres[0], hi: bres[1] }
|
||||
u256 {
|
||||
lo: bres[0],
|
||||
hi: bres[1],
|
||||
}
|
||||
}
|
||||
|
||||
fn check_one(
|
||||
|
|
@ -142,6 +145,11 @@ fn mp_u256_widen_mul() {
|
|||
by.assign(y);
|
||||
let actual = x.widen_mul(y);
|
||||
bx *= &by;
|
||||
check_one(|| format!("{x:#034x}"), || Some(format!("{y:#034x}")), actual, &mut bx);
|
||||
check_one(
|
||||
|| format!("{x:#034x}"),
|
||||
|| Some(format!("{y:#034x}")),
|
||||
actual,
|
||||
&mut bx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,15 @@ pub fn run() {
|
|||
|
||||
// With default parallelism, the CPU doesn't saturate. We don't need to be nice to
|
||||
// other processes, so do 1.5x to make sure we use all available resources.
|
||||
let threads = std::thread::available_parallelism().map(Into::into).unwrap_or(0) * 3 / 2;
|
||||
rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap();
|
||||
let threads = std::thread::available_parallelism()
|
||||
.map(Into::into)
|
||||
.unwrap_or(0)
|
||||
* 3
|
||||
/ 2;
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(threads)
|
||||
.build_global()
|
||||
.unwrap();
|
||||
|
||||
libtest_mimic::run(&args, tests).exit();
|
||||
}
|
||||
|
|
@ -134,7 +141,9 @@ where
|
|||
});
|
||||
|
||||
// Run the actual tests
|
||||
let res = chunks.par_bridge().try_for_each_init(Op::new_mp, test_single_chunk);
|
||||
let res = chunks
|
||||
.par_bridge()
|
||||
.try_for_each_init(Op::new_mp, test_single_chunk);
|
||||
|
||||
let real_total = completed.load(Ordering::Relaxed);
|
||||
pb.complete(real_total);
|
||||
|
|
@ -179,7 +188,12 @@ impl Progress {
|
|||
let pb = ProgressBar::new(total);
|
||||
pb.set_style(initial_style);
|
||||
|
||||
Self { pb, final_style, name_padded, is_tty }
|
||||
Self {
|
||||
pb,
|
||||
final_style,
|
||||
name_padded,
|
||||
is_tty,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&self, completed: u64, input: impl fmt::Debug) {
|
||||
|
|
|
|||
|
|
@ -107,9 +107,15 @@ fn emit_cfg_shorthands(cfg: &Config) {
|
|||
|
||||
/// Reemit config that we make use of for test logging.
|
||||
fn emit_cfg_env(cfg: &Config) {
|
||||
println!("cargo:rustc-env=CFG_CARGO_FEATURES={:?}", cfg.cargo_features);
|
||||
println!(
|
||||
"cargo:rustc-env=CFG_CARGO_FEATURES={:?}",
|
||||
cfg.cargo_features
|
||||
);
|
||||
println!("cargo:rustc-env=CFG_OPT_LEVEL={}", cfg.opt_level);
|
||||
println!("cargo:rustc-env=CFG_TARGET_FEATURES={:?}", cfg.target_features);
|
||||
println!(
|
||||
"cargo:rustc-env=CFG_TARGET_FEATURES={:?}",
|
||||
cfg.target_features
|
||||
);
|
||||
}
|
||||
|
||||
/// Configure whether or not `f16` and `f128` support should be enabled.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
#![no_std]
|
||||
#![cfg_attr(intrinsics_enabled, allow(internal_features))]
|
||||
#![cfg_attr(intrinsics_enabled, feature(core_intrinsics))]
|
||||
#![cfg_attr(all(intrinsics_enabled, target_family = "wasm"), feature(wasm_numeric_instr))]
|
||||
#![cfg_attr(
|
||||
all(intrinsics_enabled, target_family = "wasm"),
|
||||
feature(wasm_numeric_instr)
|
||||
)]
|
||||
#![cfg_attr(f128_enabled, feature(f128))]
|
||||
#![cfg_attr(f16_enabled, feature(f16))]
|
||||
#![allow(clippy::assign_op_pattern)]
|
||||
|
|
|
|||
|
|
@ -29,8 +29,13 @@ const ATAN_LO: [f32; 4] = [
|
|||
7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
|
||||
];
|
||||
|
||||
const A_T: [f32; 5] =
|
||||
[3.3333328366e-01, -1.9999158382e-01, 1.4253635705e-01, -1.0648017377e-01, 6.1687607318e-02];
|
||||
const A_T: [f32; 5] = [
|
||||
3.3333328366e-01,
|
||||
-1.9999158382e-01,
|
||||
1.4253635705e-01,
|
||||
-1.0648017377e-01,
|
||||
6.1687607318e-02,
|
||||
];
|
||||
|
||||
/// Arctangent (f32)
|
||||
///
|
||||
|
|
|
|||
|
|
@ -171,7 +171,11 @@ pub fn cbrt_round(x: f64, round: Round) -> FpResult<f64> {
|
|||
|
||||
for (a, b) in wlist {
|
||||
if azz == a {
|
||||
let tmp = if round as u64 + sign == 2 { hf64!("0x1p-52") } else { 0.0 };
|
||||
let tmp = if round as u64 + sign == 2 {
|
||||
hf64!("0x1p-52")
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
y1 = (b + tmp).copysign(zz);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -306,5 +306,9 @@ pub fn erfc(x: f64) -> f64 {
|
|||
}
|
||||
|
||||
let x1p_1022 = f64::from_bits(0x0010000000000000);
|
||||
if sign != 0 { 2.0 - x1p_1022 } else { x1p_1022 * x1p_1022 }
|
||||
if sign != 0 {
|
||||
2.0 - x1p_1022
|
||||
} else {
|
||||
x1p_1022 * x1p_1022
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,5 +218,9 @@ pub fn erfcf(x: f32) -> f32 {
|
|||
}
|
||||
|
||||
let x1p_120 = f32::from_bits(0x03800000);
|
||||
if sign != 0 { 2.0 - x1p_120 } else { x1p_120 * x1p_120 }
|
||||
if sign != 0 {
|
||||
2.0 - x1p_120
|
||||
} else {
|
||||
x1p_120 * x1p_120
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ use super::{exp2, exp2f, modff};
|
|||
|
||||
const LN10_F32: f32 = 3.32192809488736234787031942948939;
|
||||
const LN10_F64: f64 = 3.32192809488736234787031942948939;
|
||||
const P10: &[f32] =
|
||||
&[1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7];
|
||||
const P10: &[f32] = &[
|
||||
1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
|
||||
];
|
||||
|
||||
/// Calculates 10 raised to the power of `x` (f32).
|
||||
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
||||
|
|
|
|||
|
|
@ -126,5 +126,9 @@ pub fn expm1f(mut x: f32) -> f32 {
|
|||
return y - 1.;
|
||||
}
|
||||
let uf = f32::from_bits(((0x7f - k) << 23) as u32); /* 2^-k */
|
||||
if k < 23 { (x - e + (1. - uf)) * twopk } else { (x - (e + uf) + 1.) * twopk }
|
||||
if k < 23 {
|
||||
(x - e + (1. - uf)) * twopk
|
||||
} else {
|
||||
(x - (e + uf) + 1.) * twopk
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -387,11 +387,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn fma_sbb() {
|
||||
assert_eq!(fma(-(1.0 - f64::EPSILON), f64::MIN, f64::MIN), -3991680619069439e277);
|
||||
assert_eq!(
|
||||
fma(-(1.0 - f64::EPSILON), f64::MIN, f64::MIN),
|
||||
-3991680619069439e277
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fma_underflow() {
|
||||
assert_eq!(fma(1.1102230246251565e-16, -9.812526705433188e-305, 1.0894e-320), 0.0,);
|
||||
assert_eq!(
|
||||
fma(1.1102230246251565e-16, -9.812526705433188e-305, 1.0894e-320),
|
||||
0.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,11 +75,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
return FpResult { val: result.narrow(), status };
|
||||
return FpResult {
|
||||
val: result.narrow(),
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
let neg = ui >> (B::BITS - 1) != IntTy::<B>::ZERO;
|
||||
let err = if neg == (zb > xy) { xy - result + zb } else { zb - result + xy };
|
||||
let err = if neg == (zb > xy) {
|
||||
xy - result + zb
|
||||
} else {
|
||||
zb - result + xy
|
||||
};
|
||||
if neg == (err < B::ZERO) {
|
||||
ui += one;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,14 @@ mod tests {
|
|||
|
||||
/// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
|
||||
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
|
||||
let roundtrip = [F::ZERO, F::ONE, F::NEG_ONE, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY];
|
||||
let roundtrip = [
|
||||
F::ZERO,
|
||||
F::ONE,
|
||||
F::NEG_ONE,
|
||||
F::NEG_ZERO,
|
||||
F::INFINITY,
|
||||
F::NEG_INFINITY,
|
||||
];
|
||||
|
||||
for x in roundtrip {
|
||||
let FpResult { val, status } = ceil_status(x);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,14 @@ mod tests {
|
|||
|
||||
/// Test against https://en.cppreference.com/w/cpp/numeric/math/floor
|
||||
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
|
||||
let roundtrip = [F::ZERO, F::ONE, F::NEG_ONE, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY];
|
||||
let roundtrip = [
|
||||
F::ZERO,
|
||||
F::ONE,
|
||||
F::NEG_ONE,
|
||||
F::NEG_ZERO,
|
||||
F::INFINITY,
|
||||
F::NEG_INFINITY,
|
||||
];
|
||||
|
||||
for x in roundtrip {
|
||||
let FpResult { val, status } = floor_status(x);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,11 @@ pub fn rint_round<F: Float>(x: F, _round: Round) -> FpResult<F> {
|
|||
// On i386 `force_eval!` must be used to force rounding via storage to memory. Otherwise,
|
||||
// the excess precission from x87 would cause an incorrect final result.
|
||||
let force = |x| {
|
||||
if cfg!(x86_no_sse) && (F::BITS == 32 || F::BITS == 64) { force_eval!(x) } else { x }
|
||||
if cfg!(x86_no_sse) && (F::BITS == 32 || F::BITS == 64) {
|
||||
force_eval!(x)
|
||||
} else {
|
||||
x
|
||||
}
|
||||
};
|
||||
|
||||
let res = if e >= F::EXP_BIAS + F::SIG_BITS {
|
||||
|
|
@ -47,7 +51,14 @@ mod tests {
|
|||
use crate::support::{Hexf, Status};
|
||||
|
||||
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
|
||||
let roundtrip = [F::ZERO, F::ONE, F::NEG_ONE, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY];
|
||||
let roundtrip = [
|
||||
F::ZERO,
|
||||
F::ONE,
|
||||
F::NEG_ONE,
|
||||
F::NEG_ZERO,
|
||||
F::INFINITY,
|
||||
F::NEG_INFINITY,
|
||||
];
|
||||
|
||||
for x in roundtrip {
|
||||
let FpResult { val, status } = rint_round(x, Round::Nearest);
|
||||
|
|
|
|||
|
|
@ -521,7 +521,10 @@ mod tests {
|
|||
f128::from_bits(0x400c3880000000000000000000000000),
|
||||
0x40059000000000000000000000000000_u128,
|
||||
),
|
||||
(f128::from_bits(0x0000000f), 0x1fc9efbdeb14f4ed9b17ae807907e1e9_u128),
|
||||
(
|
||||
f128::from_bits(0x0000000f),
|
||||
0x1fc9efbdeb14f4ed9b17ae807907e1e9_u128,
|
||||
),
|
||||
(f128::INFINITY, f128::INFINITY.to_bits()),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,11 @@ pub fn trunc_status<F: Float>(x: F) -> FpResult<F> {
|
|||
// C5: Otherwise the result is inexact and we will truncate. Raise `FE_INEXACT`, mask the
|
||||
// result, and return.
|
||||
|
||||
let status = if xi & F::SIG_MASK == F::Int::ZERO { Status::OK } else { Status::INEXACT };
|
||||
let status = if xi & F::SIG_MASK == F::Int::ZERO {
|
||||
Status::OK
|
||||
} else {
|
||||
Status::INEXACT
|
||||
};
|
||||
xi &= mask;
|
||||
FpResult::new(F::from_bits(xi), status)
|
||||
}
|
||||
|
|
@ -47,7 +51,14 @@ mod tests {
|
|||
use crate::support::Hexf;
|
||||
|
||||
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
|
||||
let roundtrip = [F::ZERO, F::ONE, F::NEG_ONE, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY];
|
||||
let roundtrip = [
|
||||
F::ZERO,
|
||||
F::ONE,
|
||||
F::NEG_ONE,
|
||||
F::NEG_ZERO,
|
||||
F::INFINITY,
|
||||
F::NEG_INFINITY,
|
||||
];
|
||||
|
||||
for x in roundtrip {
|
||||
let FpResult { val, status } = trunc_status(x);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ pub fn ilogb(x: f64) -> i32 {
|
|||
e
|
||||
} else if e == 0x7ff {
|
||||
force_eval!(0.0 / 0.0);
|
||||
if (i << 12) != 0 { FP_ILOGBNAN } else { i32::MAX }
|
||||
if (i << 12) != 0 {
|
||||
FP_ILOGBNAN
|
||||
} else {
|
||||
i32::MAX
|
||||
}
|
||||
} else {
|
||||
e - 0x3ff
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,5 +49,9 @@ pub(crate) fn k_sin(x: f64, y: f64, iy: i32) -> f64 {
|
|||
let w = z * z;
|
||||
let r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6);
|
||||
let v = z * x;
|
||||
if iy == 0 { x + v * (S1 + z * r) } else { x - ((z * (0.5 * y - v * r) - y) - v * S1) }
|
||||
if iy == 0 {
|
||||
x + v * (S1 + z * r)
|
||||
} else {
|
||||
x - ((z * (0.5 * y - v * r) - y) - v * S1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,11 @@ pub fn log1p(x: f64) -> f64 {
|
|||
k = (hu >> 20) as i32 - 0x3ff;
|
||||
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
|
||||
if k < 54 {
|
||||
c = if k >= 2 { 1. - (f64::from_bits(ui) - x) } else { x - (f64::from_bits(ui) - 1.) };
|
||||
c = if k >= 2 {
|
||||
1. - (f64::from_bits(ui) - x)
|
||||
} else {
|
||||
x - (f64::from_bits(ui) - 1.)
|
||||
};
|
||||
c /= f64::from_bits(ui);
|
||||
} else {
|
||||
c = 0.;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,11 @@ pub fn log1pf(x: f32) -> f32 {
|
|||
k = (iu >> 23) as i32 - 0x7f;
|
||||
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
|
||||
if k < 25 {
|
||||
c = if k >= 2 { 1. - (f32::from_bits(ui) - x) } else { x - (f32::from_bits(ui) - 1.) };
|
||||
c = if k >= 2 {
|
||||
1. - (f32::from_bits(ui) - x)
|
||||
} else {
|
||||
x - (f32::from_bits(ui) - 1.)
|
||||
};
|
||||
c /= f32::from_bits(ui);
|
||||
} else {
|
||||
c = 0.;
|
||||
|
|
|
|||
|
|
@ -239,10 +239,18 @@ pub fn pow(x: f64, y: f64) -> f64 {
|
|||
|
||||
/* over/underflow if x is not close to one */
|
||||
if ix < 0x3fefffff {
|
||||
return if hy < 0 { s * HUGE * HUGE } else { s * TINY * TINY };
|
||||
return if hy < 0 {
|
||||
s * HUGE * HUGE
|
||||
} else {
|
||||
s * TINY * TINY
|
||||
};
|
||||
}
|
||||
if ix > 0x3ff00000 {
|
||||
return if hy > 0 { s * HUGE * HUGE } else { s * TINY * TINY };
|
||||
return if hy > 0 {
|
||||
s * HUGE * HUGE
|
||||
} else {
|
||||
s * TINY * TINY
|
||||
};
|
||||
}
|
||||
|
||||
/* now |1-x| is TINY <= 2**-20, suffice to compute
|
||||
|
|
@ -439,7 +447,11 @@ mod tests {
|
|||
fn pow_test(base: f64, exponent: f64, expected: f64) {
|
||||
let res = pow(base, exponent);
|
||||
assert!(
|
||||
if expected.is_nan() { res.is_nan() } else { pow(base, exponent) == expected },
|
||||
if expected.is_nan() {
|
||||
res.is_nan()
|
||||
} else {
|
||||
pow(base, exponent) == expected
|
||||
},
|
||||
"{} ** {} was {} instead of {}",
|
||||
base,
|
||||
exponent,
|
||||
|
|
@ -449,11 +461,13 @@ mod tests {
|
|||
}
|
||||
|
||||
fn test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) {
|
||||
sets.iter().for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected)));
|
||||
sets.iter()
|
||||
.for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected)));
|
||||
}
|
||||
|
||||
fn test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) {
|
||||
sets.iter().for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected)));
|
||||
sets.iter()
|
||||
.for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected)));
|
||||
}
|
||||
|
||||
fn test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) {
|
||||
|
|
@ -467,7 +481,11 @@ mod tests {
|
|||
#[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
|
||||
let res = force_eval!(res);
|
||||
assert!(
|
||||
if exp.is_nan() { res.is_nan() } else { exp == res },
|
||||
if exp.is_nan() {
|
||||
res.is_nan()
|
||||
} else {
|
||||
exp == res
|
||||
},
|
||||
"test for {} was {} instead of {}",
|
||||
val,
|
||||
res,
|
||||
|
|
@ -515,7 +533,9 @@ mod tests {
|
|||
// (-Infinity ^ anything but odd ints should be == -0 ^ (-anything))
|
||||
// We can lump in pos/neg odd ints here because they don't seem to
|
||||
// cause panics (div by zero) in release mode (I think).
|
||||
test_sets(ALL, &|v: f64| pow(f64::NEG_INFINITY, v), &|v: f64| pow(-0.0, -v));
|
||||
test_sets(ALL, &|v: f64| pow(f64::NEG_INFINITY, v), &|v: f64| {
|
||||
pow(-0.0, -v)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -582,11 +602,15 @@ mod tests {
|
|||
|
||||
// Factoring -1 out:
|
||||
// (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer))
|
||||
[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS].iter().for_each(|int_set| {
|
||||
int_set.iter().for_each(|int| {
|
||||
test_sets(ALL, &|v: f64| pow(-v, *int), &|v: f64| pow(-1.0, *int) * pow(v, *int));
|
||||
})
|
||||
});
|
||||
[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS]
|
||||
.iter()
|
||||
.for_each(|int_set| {
|
||||
int_set.iter().for_each(|int| {
|
||||
test_sets(ALL, &|v: f64| pow(-v, *int), &|v: f64| {
|
||||
pow(-1.0, *int) * pow(v, *int)
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// Negative base (imaginary results):
|
||||
// (-anything except 0 and Infinity ^ non-integer should be NAN)
|
||||
|
|
|
|||
|
|
@ -182,11 +182,19 @@ pub fn powf(x: f32, y: f32) -> f32 {
|
|||
/* if |y| > 2**27 */
|
||||
/* over/underflow if x is not close to one */
|
||||
if ix < 0x3f7ffff8 {
|
||||
return if hy < 0 { sn * HUGE * HUGE } else { sn * TINY * TINY };
|
||||
return if hy < 0 {
|
||||
sn * HUGE * HUGE
|
||||
} else {
|
||||
sn * TINY * TINY
|
||||
};
|
||||
}
|
||||
|
||||
if ix > 0x3f800007 {
|
||||
return if hy > 0 { sn * HUGE * HUGE } else { sn * TINY * TINY };
|
||||
return if hy > 0 {
|
||||
sn * HUGE * HUGE
|
||||
} else {
|
||||
sn * TINY * TINY
|
||||
};
|
||||
}
|
||||
|
||||
/* now |1-x| is TINY <= 2**-20, suffice to compute
|
||||
|
|
|
|||
|
|
@ -199,16 +199,28 @@ mod tests {
|
|||
fn test_near_pi() {
|
||||
let arg = 3.141592025756836;
|
||||
let arg = force_eval!(arg);
|
||||
assert_eq!(rem_pio2(arg), (2, -6.278329573009626e-7, -2.1125998133974653e-23));
|
||||
assert_eq!(
|
||||
rem_pio2(arg),
|
||||
(2, -6.278329573009626e-7, -2.1125998133974653e-23)
|
||||
);
|
||||
let arg = 3.141592033207416;
|
||||
let arg = force_eval!(arg);
|
||||
assert_eq!(rem_pio2(arg), (2, -6.20382377148128e-7, -2.1125998133974653e-23));
|
||||
assert_eq!(
|
||||
rem_pio2(arg),
|
||||
(2, -6.20382377148128e-7, -2.1125998133974653e-23)
|
||||
);
|
||||
let arg = 3.141592144966125;
|
||||
let arg = force_eval!(arg);
|
||||
assert_eq!(rem_pio2(arg), (2, -5.086236681942706e-7, -2.1125998133974653e-23));
|
||||
assert_eq!(
|
||||
rem_pio2(arg),
|
||||
(2, -5.086236681942706e-7, -2.1125998133974653e-23)
|
||||
);
|
||||
let arg = 3.141592979431152;
|
||||
let arg = force_eval!(arg);
|
||||
assert_eq!(rem_pio2(arg), (2, 3.2584135866119817e-7, -2.1125998133974653e-23));
|
||||
assert_eq!(
|
||||
rem_pio2(arg),
|
||||
(2, 3.2584135866119817e-7, -2.1125998133974653e-23)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -42,7 +42,11 @@ pub fn sinf(x: f32) -> f32 {
|
|||
if ix < 0x39800000 {
|
||||
/* |x| < 2**-12 */
|
||||
/* raise inexact if x!=0 and underflow if subnormal */
|
||||
force_eval!(if ix < 0x00800000 { x / x1p120 } else { x + x1p120 });
|
||||
force_eval!(if ix < 0x00800000 {
|
||||
x / x1p120
|
||||
} else {
|
||||
x + x1p120
|
||||
});
|
||||
return x;
|
||||
}
|
||||
return k_sinf(x64);
|
||||
|
|
@ -57,7 +61,11 @@ pub fn sinf(x: f32) -> f32 {
|
|||
return k_cosf(x64 - S1_PIO2);
|
||||
}
|
||||
}
|
||||
return k_sinf(if sign { -(x64 + S2_PIO2) } else { -(x64 - S2_PIO2) });
|
||||
return k_sinf(if sign {
|
||||
-(x64 + S2_PIO2)
|
||||
} else {
|
||||
-(x64 - S2_PIO2)
|
||||
});
|
||||
}
|
||||
if ix <= 0x40e231d5 {
|
||||
/* |x| ~<= 9*pi/4 */
|
||||
|
|
|
|||
|
|
@ -19,11 +19,17 @@ pub struct u256 {
|
|||
|
||||
impl u256 {
|
||||
#[cfg(any(test, feature = "unstable-public-internals"))]
|
||||
pub const MAX: Self = Self { lo: u128::MAX, hi: u128::MAX };
|
||||
pub const MAX: Self = Self {
|
||||
lo: u128::MAX,
|
||||
hi: u128::MAX,
|
||||
};
|
||||
|
||||
/// Reinterpret as a signed integer
|
||||
pub fn signed(self) -> i256 {
|
||||
i256 { lo: self.lo, hi: self.hi }
|
||||
i256 {
|
||||
lo: self.lo,
|
||||
hi: self.hi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +45,10 @@ impl i256 {
|
|||
/// Reinterpret as an unsigned integer
|
||||
#[cfg(any(test, feature = "unstable-public-internals"))]
|
||||
pub fn unsigned(self) -> u256 {
|
||||
u256 { lo: self.lo, hi: self.hi }
|
||||
u256 {
|
||||
lo: self.lo,
|
||||
hi: self.hi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +62,10 @@ impl MinInt for u256 {
|
|||
const ZERO: Self = Self { lo: 0, hi: 0 };
|
||||
const ONE: Self = Self { lo: 1, hi: 0 };
|
||||
const MIN: Self = Self { lo: 0, hi: 0 };
|
||||
const MAX: Self = Self { lo: u128::MAX, hi: u128::MAX };
|
||||
const MAX: Self = Self {
|
||||
lo: u128::MAX,
|
||||
hi: u128::MAX,
|
||||
};
|
||||
}
|
||||
|
||||
impl MinInt for i256 {
|
||||
|
|
@ -65,8 +77,14 @@ impl MinInt for i256 {
|
|||
const BITS: u32 = 256;
|
||||
const ZERO: Self = Self { lo: 0, hi: 0 };
|
||||
const ONE: Self = Self { lo: 1, hi: 0 };
|
||||
const MIN: Self = Self { lo: 0, hi: 1 << 127 };
|
||||
const MAX: Self = Self { lo: u128::MAX, hi: u128::MAX << 1 };
|
||||
const MIN: Self = Self {
|
||||
lo: 0,
|
||||
hi: 1 << 127,
|
||||
};
|
||||
const MAX: Self = Self {
|
||||
lo: u128::MAX,
|
||||
hi: u128::MAX << 1,
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_common {
|
||||
|
|
|
|||
|
|
@ -13,23 +13,62 @@ fn hexu(v: u256) -> String {
|
|||
|
||||
#[test]
|
||||
fn widen_u128() {
|
||||
assert_eq!(u128::MAX.widen(), u256 { lo: u128::MAX, hi: 0 });
|
||||
assert_eq!(LOHI_SPLIT.widen(), u256 { lo: LOHI_SPLIT, hi: 0 });
|
||||
assert_eq!(
|
||||
u128::MAX.widen(),
|
||||
u256 {
|
||||
lo: u128::MAX,
|
||||
hi: 0
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
LOHI_SPLIT.widen(),
|
||||
u256 {
|
||||
lo: LOHI_SPLIT,
|
||||
hi: 0
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn widen_i128() {
|
||||
assert_eq!((-1i128).widen(), u256::MAX.signed());
|
||||
assert_eq!((LOHI_SPLIT as i128).widen(), i256 { lo: LOHI_SPLIT, hi: u128::MAX });
|
||||
assert_eq!(
|
||||
(LOHI_SPLIT as i128).widen(),
|
||||
i256 {
|
||||
lo: LOHI_SPLIT,
|
||||
hi: u128::MAX
|
||||
}
|
||||
);
|
||||
assert_eq!((-1i128).zero_widen().unsigned(), (u128::MAX).widen());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn widen_mul_u128() {
|
||||
let tests = [
|
||||
(u128::MAX / 2, 2_u128, u256 { lo: u128::MAX - 1, hi: 0 }),
|
||||
(u128::MAX, 2_u128, u256 { lo: u128::MAX - 1, hi: 1 }),
|
||||
(u128::MAX, u128::MAX, u256 { lo: 1, hi: u128::MAX - 1 }),
|
||||
(
|
||||
u128::MAX / 2,
|
||||
2_u128,
|
||||
u256 {
|
||||
lo: u128::MAX - 1,
|
||||
hi: 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
u128::MAX,
|
||||
2_u128,
|
||||
u256 {
|
||||
lo: u128::MAX - 1,
|
||||
hi: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
u128::MAX,
|
||||
u128::MAX,
|
||||
u256 {
|
||||
lo: 1,
|
||||
hi: u128::MAX - 1,
|
||||
},
|
||||
),
|
||||
(0, 0, u256::ZERO),
|
||||
(1234u128, 0, u256::ZERO),
|
||||
(0, 1234, u256::ZERO),
|
||||
|
|
@ -68,7 +107,13 @@ fn not_u256() {
|
|||
|
||||
#[test]
|
||||
fn shr_u256() {
|
||||
let only_low = [1, u16::MAX.into(), u32::MAX.into(), u64::MAX.into(), u128::MAX];
|
||||
let only_low = [
|
||||
1,
|
||||
u16::MAX.into(),
|
||||
u32::MAX.into(),
|
||||
u64::MAX.into(),
|
||||
u128::MAX,
|
||||
];
|
||||
let mut has_errors = false;
|
||||
|
||||
let mut add_error = |a, b, expected, actual| {
|
||||
|
|
@ -99,23 +144,106 @@ fn shr_u256() {
|
|||
}
|
||||
|
||||
let check = [
|
||||
(u256::MAX, 1, u256 { lo: u128::MAX, hi: u128::MAX >> 1 }),
|
||||
(u256::MAX, 5, u256 { lo: u128::MAX, hi: u128::MAX >> 5 }),
|
||||
(u256::MAX, 63, u256 { lo: u128::MAX, hi: u64::MAX as u128 | (1 << 64) }),
|
||||
(u256::MAX, 64, u256 { lo: u128::MAX, hi: u64::MAX as u128 }),
|
||||
(u256::MAX, 65, u256 { lo: u128::MAX, hi: (u64::MAX >> 1) as u128 }),
|
||||
(u256::MAX, 127, u256 { lo: u128::MAX, hi: 1 }),
|
||||
(u256::MAX, 128, u256 { lo: u128::MAX, hi: 0 }),
|
||||
(u256::MAX, 129, u256 { lo: u128::MAX >> 1, hi: 0 }),
|
||||
(u256::MAX, 191, u256 { lo: u64::MAX as u128 | 1 << 64, hi: 0 }),
|
||||
(u256::MAX, 192, u256 { lo: u64::MAX as u128, hi: 0 }),
|
||||
(u256::MAX, 193, u256 { lo: u64::MAX as u128 >> 1, hi: 0 }),
|
||||
(
|
||||
u256::MAX,
|
||||
1,
|
||||
u256 {
|
||||
lo: u128::MAX,
|
||||
hi: u128::MAX >> 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
5,
|
||||
u256 {
|
||||
lo: u128::MAX,
|
||||
hi: u128::MAX >> 5,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
63,
|
||||
u256 {
|
||||
lo: u128::MAX,
|
||||
hi: u64::MAX as u128 | (1 << 64),
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
64,
|
||||
u256 {
|
||||
lo: u128::MAX,
|
||||
hi: u64::MAX as u128,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
65,
|
||||
u256 {
|
||||
lo: u128::MAX,
|
||||
hi: (u64::MAX >> 1) as u128,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
127,
|
||||
u256 {
|
||||
lo: u128::MAX,
|
||||
hi: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
128,
|
||||
u256 {
|
||||
lo: u128::MAX,
|
||||
hi: 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
129,
|
||||
u256 {
|
||||
lo: u128::MAX >> 1,
|
||||
hi: 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
191,
|
||||
u256 {
|
||||
lo: u64::MAX as u128 | 1 << 64,
|
||||
hi: 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
192,
|
||||
u256 {
|
||||
lo: u64::MAX as u128,
|
||||
hi: 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
u256::MAX,
|
||||
193,
|
||||
u256 {
|
||||
lo: u64::MAX as u128 >> 1,
|
||||
hi: 0,
|
||||
},
|
||||
),
|
||||
(u256::MAX, 254, u256 { lo: 0b11, hi: 0 }),
|
||||
(u256::MAX, 255, u256 { lo: 1, hi: 0 }),
|
||||
(
|
||||
u256 { hi: LOHI_SPLIT, lo: 0 },
|
||||
u256 {
|
||||
hi: LOHI_SPLIT,
|
||||
lo: 0,
|
||||
},
|
||||
64,
|
||||
u256 { lo: 0xffffffffffffffff0000000000000000, hi: 0xaaaaaaaaaaaaaaaa },
|
||||
u256 {
|
||||
lo: 0xffffffffffffffff0000000000000000,
|
||||
hi: 0xaaaaaaaaaaaaaaaa,
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@ impl<T> FpResult<T> {
|
|||
|
||||
/// Return `val` with `Status::OK`.
|
||||
pub fn ok(val: T) -> Self {
|
||||
Self { val, status: Status::OK }
|
||||
Self {
|
||||
val,
|
||||
status: Status::OK,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,11 @@ pub trait Float:
|
|||
/// if `NaN` should not be treated separately.
|
||||
#[allow(dead_code)]
|
||||
fn eq_repr(self, rhs: Self) -> bool {
|
||||
if self.is_nan() && rhs.is_nan() { true } else { self.biteq(rhs) }
|
||||
if self.is_nan() && rhs.is_nan() {
|
||||
true
|
||||
} else {
|
||||
self.biteq(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the value is NaN.
|
||||
|
|
@ -149,7 +153,11 @@ pub trait Float:
|
|||
|
||||
/// Constructs a `Self` from its parts. Inputs are treated as bits and shifted into position.
|
||||
fn from_parts(negative: bool, exponent: u32, significand: Self::Int) -> Self {
|
||||
let sign = if negative { Self::Int::ONE } else { Self::Int::ZERO };
|
||||
let sign = if negative {
|
||||
Self::Int::ONE
|
||||
} else {
|
||||
Self::Int::ZERO
|
||||
};
|
||||
Self::from_bits(
|
||||
(sign << (Self::BITS - 1))
|
||||
| (Self::Int::cast_from(exponent & Self::EXP_SAT) << Self::SIG_BITS)
|
||||
|
|
@ -173,7 +181,11 @@ pub trait Float:
|
|||
/// Returns a number that represents the sign of self.
|
||||
#[allow(dead_code)]
|
||||
fn signum(self) -> Self {
|
||||
if self.is_nan() { self } else { Self::ONE.copysign(self) }
|
||||
if self.is_nan() {
|
||||
self
|
||||
} else {
|
||||
Self::ONE.copysign(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -273,18 +285,61 @@ macro_rules! float_impl {
|
|||
}
|
||||
fn normalize(significand: Self::Int) -> (i32, Self::Int) {
|
||||
let shift = significand.leading_zeros().wrapping_sub(Self::EXP_BITS);
|
||||
(1i32.wrapping_sub(shift as i32), significand << shift as Self::Int)
|
||||
(
|
||||
1i32.wrapping_sub(shift as i32),
|
||||
significand << shift as Self::Int,
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(f16_enabled)]
|
||||
float_impl!(f16, u16, i16, 16, 10, f16::from_bits, f16::to_bits, fmaf16, fmaf16);
|
||||
float_impl!(f32, u32, i32, 32, 23, f32_from_bits, f32_to_bits, fmaf, fmaf32);
|
||||
float_impl!(f64, u64, i64, 64, 52, f64_from_bits, f64_to_bits, fma, fmaf64);
|
||||
float_impl!(
|
||||
f16,
|
||||
u16,
|
||||
i16,
|
||||
16,
|
||||
10,
|
||||
f16::from_bits,
|
||||
f16::to_bits,
|
||||
fmaf16,
|
||||
fmaf16
|
||||
);
|
||||
float_impl!(
|
||||
f32,
|
||||
u32,
|
||||
i32,
|
||||
32,
|
||||
23,
|
||||
f32_from_bits,
|
||||
f32_to_bits,
|
||||
fmaf,
|
||||
fmaf32
|
||||
);
|
||||
float_impl!(
|
||||
f64,
|
||||
u64,
|
||||
i64,
|
||||
64,
|
||||
52,
|
||||
f64_from_bits,
|
||||
f64_to_bits,
|
||||
fma,
|
||||
fmaf64
|
||||
);
|
||||
#[cfg(f128_enabled)]
|
||||
float_impl!(f128, u128, i128, 128, 112, f128::from_bits, f128::to_bits, fmaf128, fmaf128);
|
||||
float_impl!(
|
||||
f128,
|
||||
u128,
|
||||
i128,
|
||||
128,
|
||||
112,
|
||||
f128::from_bits,
|
||||
f128::to_bits,
|
||||
fmaf128,
|
||||
fmaf128
|
||||
);
|
||||
|
||||
/* FIXME(msrv): vendor some things that are not const stable at our MSRV */
|
||||
|
||||
|
|
@ -424,7 +479,10 @@ mod tests {
|
|||
|
||||
// `from_parts`
|
||||
assert_biteq!(f32::from_parts(true, f32::EXP_BIAS, 0), -1.0f32);
|
||||
assert_biteq!(f32::from_parts(false, 10 + f32::EXP_BIAS, 0), hf32!("0x1p10"));
|
||||
assert_biteq!(
|
||||
f32::from_parts(false, 10 + f32::EXP_BIAS, 0),
|
||||
hf32!("0x1p10")
|
||||
);
|
||||
assert_biteq!(f32::from_parts(false, 0, 1), f32::from_bits(0x1));
|
||||
}
|
||||
|
||||
|
|
@ -451,7 +509,10 @@ mod tests {
|
|||
|
||||
// `from_parts`
|
||||
assert_biteq!(f64::from_parts(true, f64::EXP_BIAS, 0), -1.0f64);
|
||||
assert_biteq!(f64::from_parts(false, 10 + f64::EXP_BIAS, 0), hf64!("0x1p10"));
|
||||
assert_biteq!(
|
||||
f64::from_parts(false, 10 + f64::EXP_BIAS, 0),
|
||||
hf64!("0x1p10")
|
||||
);
|
||||
assert_biteq!(f64::from_parts(false, 0, 1), f64::from_bits(0x1));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -234,7 +234,9 @@ const fn parse_hex(mut b: &[u8]) -> Result<Parsed, HexFloatParseError> {
|
|||
match c {
|
||||
b'.' => {
|
||||
if seen_point {
|
||||
return Err(HexFloatParseError("unexpected '.' parsing fractional digits"));
|
||||
return Err(HexFloatParseError(
|
||||
"unexpected '.' parsing fractional digits",
|
||||
));
|
||||
}
|
||||
seen_point = true;
|
||||
continue;
|
||||
|
|
@ -294,7 +296,9 @@ const fn parse_hex(mut b: &[u8]) -> Result<Parsed, HexFloatParseError> {
|
|||
}
|
||||
|
||||
if !some_digits {
|
||||
return Err(HexFloatParseError("at least one exponent digit is required"));
|
||||
return Err(HexFloatParseError(
|
||||
"at least one exponent digit is required",
|
||||
));
|
||||
};
|
||||
|
||||
{
|
||||
|
|
@ -542,7 +546,11 @@ mod parse_tests {
|
|||
for k in -149..=127 {
|
||||
let s = format!("0x1p{k}");
|
||||
let x = hf32(&s);
|
||||
let y = if k < 0 { 0.5f32.powi(-k) } else { 2.0f32.powi(k) };
|
||||
let y = if k < 0 {
|
||||
0.5f32.powi(-k)
|
||||
} else {
|
||||
2.0f32.powi(k)
|
||||
};
|
||||
assert_eq!(x, y);
|
||||
}
|
||||
|
||||
|
|
@ -613,9 +621,14 @@ mod parse_tests {
|
|||
fn rounding_extreme_underflow() {
|
||||
for k in 1..1000 {
|
||||
let s = format!("0x1p{}", -149 - k);
|
||||
let Ok((bits, status)) = parse_any(&s, 32, 23, Round::Nearest) else { unreachable!() };
|
||||
let Ok((bits, status)) = parse_any(&s, 32, 23, Round::Nearest) else {
|
||||
unreachable!()
|
||||
};
|
||||
assert_eq!(bits, 0, "{s} should round to zero, got bits={bits}");
|
||||
assert!(status.underflow(), "should indicate underflow when parsing {s}");
|
||||
assert!(
|
||||
status.underflow(),
|
||||
"should indicate underflow when parsing {s}"
|
||||
);
|
||||
assert!(status.inexact(), "should indicate inexact when parsing {s}");
|
||||
}
|
||||
}
|
||||
|
|
@ -623,11 +636,15 @@ mod parse_tests {
|
|||
fn long_tail() {
|
||||
for k in 1..1000 {
|
||||
let s = format!("0x1.{}p0", "0".repeat(k));
|
||||
let Ok(bits) = parse_hex_exact(&s, 32, 23) else { panic!("parsing {s} failed") };
|
||||
let Ok(bits) = parse_hex_exact(&s, 32, 23) else {
|
||||
panic!("parsing {s} failed")
|
||||
};
|
||||
assert_eq!(f32::from_bits(bits as u32), 1.0);
|
||||
|
||||
let s = format!("0x1.{}1p0", "0".repeat(k));
|
||||
let Ok((bits, status)) = parse_any(&s, 32, 23, Round::Nearest) else { unreachable!() };
|
||||
let Ok((bits, status)) = parse_any(&s, 32, 23, Round::Nearest) else {
|
||||
unreachable!()
|
||||
};
|
||||
if status.inexact() {
|
||||
assert!(1.0 == f32::from_bits(bits as u32));
|
||||
} else {
|
||||
|
|
@ -839,7 +856,10 @@ mod parse_tests {
|
|||
assert_eq!(hf32!("0x1.ffep+8").to_bits(), 0x43fff000_u32);
|
||||
assert_eq!(hf64!("0x1.ffep+8").to_bits(), 0x407ffe0000000000_u64);
|
||||
#[cfg(f128_enabled)]
|
||||
assert_eq!(hf128!("0x1.ffep+8").to_bits(), 0x4007ffe0000000000000000000000000_u128);
|
||||
assert_eq!(
|
||||
hf128!("0x1.ffep+8").to_bits(),
|
||||
0x4007ffe0000000000000000000000000_u128
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1143,8 +1163,14 @@ mod print_tests {
|
|||
|
||||
#[cfg(f128_enabled)]
|
||||
{
|
||||
assert_eq!(Hexf(f128::MAX).to_string(), "0x1.ffffffffffffffffffffffffffffp+16383");
|
||||
assert_eq!(Hexf(f128::MIN).to_string(), "-0x1.ffffffffffffffffffffffffffffp+16383");
|
||||
assert_eq!(
|
||||
Hexf(f128::MAX).to_string(),
|
||||
"0x1.ffffffffffffffffffffffffffffp+16383"
|
||||
);
|
||||
assert_eq!(
|
||||
Hexf(f128::MIN).to_string(),
|
||||
"-0x1.ffffffffffffffffffffffffffffp+16383"
|
||||
);
|
||||
assert_eq!(Hexf(f128::ZERO).to_string(), "0x0p+0");
|
||||
assert_eq!(Hexf(f128::NEG_ZERO).to_string(), "-0x0p+0");
|
||||
assert_eq!(Hexf(f128::NAN).to_string(), "NaN");
|
||||
|
|
|
|||
|
|
@ -53,7 +53,11 @@ pub fn tan(x: f64) -> f64 {
|
|||
if ix < 0x3e400000 {
|
||||
/* |x| < 2**-27 */
|
||||
/* raise inexact if x!=0 and underflow if subnormal */
|
||||
force_eval!(if ix < 0x00100000 { x / x1p120 as f64 } else { x + x1p120 as f64 });
|
||||
force_eval!(if ix < 0x00100000 {
|
||||
x / x1p120 as f64
|
||||
} else {
|
||||
x + x1p120 as f64
|
||||
});
|
||||
return x;
|
||||
}
|
||||
return k_tan(x, 0.0, 0);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,11 @@ pub fn tanf(x: f32) -> f32 {
|
|||
if ix < 0x39800000 {
|
||||
/* |x| < 2**-12 */
|
||||
/* raise inexact if x!=0 and underflow if subnormal */
|
||||
force_eval!(if ix < 0x00800000 { x / x1p120 } else { x + x1p120 });
|
||||
force_eval!(if ix < 0x00800000 {
|
||||
x / x1p120
|
||||
} else {
|
||||
x + x1p120
|
||||
});
|
||||
return x;
|
||||
}
|
||||
return k_tanf(x64, false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue