789 lines
25 KiB
Rust
789 lines
25 KiB
Rust
// NOTE we intentionally avoid using the `quote` crate here because it doesn't work with the
|
|
// `x86_64-unknown-linux-musl` target.
|
|
|
|
// NOTE usually the only thing you need to do to test a new math function is to add it to one of the
|
|
// macro invocations found in the bottom of this file.
|
|
|
|
#[macro_use]
|
|
extern crate itertools;
|
|
extern crate rand;
|
|
|
|
use std::error::Error;
|
|
use std::fmt::Write as _0;
|
|
use std::fs::{self, File};
|
|
use std::io::Write as _1;
|
|
use std::{f32, f64, i16, u16, u32, u64, u8};
|
|
|
|
use rand::{Rng, SeedableRng, XorShiftRng};
|
|
|
|
// Number of test cases to generate
|
|
const NTESTS: usize = 10_000;
|
|
|
|
// TODO tweak these functions to generate edge cases (zero, infinity, NaN) more often
|
|
fn f32(rng: &mut XorShiftRng) -> f32 {
|
|
let sign = if rng.gen_bool(0.5) { 1 << 31 } else { 0 };
|
|
let exponent = (rng.gen_range(0, u8::MAX) as u32) << 23;
|
|
let mantissa = rng.gen_range(0, u32::MAX) & ((1 << 23) - 1);
|
|
|
|
f32::from_bits(sign + exponent + mantissa)
|
|
}
|
|
|
|
fn f64(rng: &mut XorShiftRng) -> f64 {
|
|
let sign = if rng.gen_bool(0.5) { 1 << 63 } else { 0 };
|
|
let exponent = (rng.gen_range(0, u16::MAX) as u64 & ((1 << 11) - 1)) << 52;
|
|
let mantissa = rng.gen_range(0, u64::MAX) & ((1 << 52) - 1);
|
|
|
|
f64::from_bits(sign + exponent + mantissa)
|
|
}
|
|
|
|
const EDGE_CASES32: &[f32] = &[
|
|
-0.,
|
|
0.,
|
|
f32::EPSILON,
|
|
f32::INFINITY,
|
|
f32::MAX,
|
|
f32::MIN,
|
|
f32::MIN_POSITIVE,
|
|
f32::NAN,
|
|
f32::NEG_INFINITY,
|
|
];
|
|
|
|
const EDGE_CASES64: &[f64] = &[
|
|
-0.,
|
|
0.,
|
|
f64::EPSILON,
|
|
f64::INFINITY,
|
|
f64::MAX,
|
|
f64::MIN,
|
|
f64::MIN_POSITIVE,
|
|
f64::NAN,
|
|
f64::NEG_INFINITY,
|
|
];
|
|
|
|
// fn(f32) -> f32
|
|
macro_rules! f32_f32 {
|
|
($($intr:ident,)*) => {
|
|
fn f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
|
// MUSL C implementation of the function to test
|
|
extern "C" {
|
|
$(fn $intr(_: f32) -> f32;)*
|
|
}
|
|
|
|
$(
|
|
let mut cases = String::new();
|
|
|
|
// random inputs
|
|
for inp in EDGE_CASES32.iter().cloned().chain((0..NTESTS).map(|_| f32(rng))) {
|
|
let out = unsafe { $intr(inp) };
|
|
|
|
let inp = inp.to_bits();
|
|
let out = out.to_bits();
|
|
|
|
write!(cases, "({}, {})", inp, out).unwrap();
|
|
cases.push(',');
|
|
}
|
|
|
|
let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
|
|
write!(f, "
|
|
#![deny(warnings)]
|
|
|
|
extern crate libm;
|
|
|
|
use std::panic;
|
|
|
|
#[test]
|
|
fn {0}() {{
|
|
const CASES: &[(u32, u32)] = &[
|
|
{1}
|
|
];
|
|
|
|
for case in CASES {{
|
|
let (inp, expected) = *case;
|
|
|
|
if let Ok(outf) =
|
|
panic::catch_unwind(|| libm::{0}(f32::from_bits(inp)))
|
|
{{
|
|
let outi = outf.to_bits();
|
|
|
|
if !((outf.is_nan() && f32::from_bits(expected).is_nan())
|
|
|| libm::_eqf(outi, expected))
|
|
{{
|
|
panic!(
|
|
\"input: {{}}, output: {{}}, expected: {{}}\",
|
|
inp, outi, expected,
|
|
);
|
|
}}
|
|
}} else {{
|
|
panic!(
|
|
\"input: {{}}, output: PANIC, expected: {{}}\",
|
|
inp, expected,
|
|
);
|
|
}}
|
|
}}
|
|
}}
|
|
",
|
|
stringify!($intr),
|
|
cases)?;
|
|
)*
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
// fn(f32, f32) -> f32
|
|
macro_rules! f32f32_f32 {
|
|
($($intr:ident,)*) => {
|
|
fn f32f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
|
extern "C" {
|
|
$(fn $intr(_: f32, _: f32) -> f32;)*
|
|
}
|
|
|
|
let mut rng2 = rng.clone();
|
|
let mut rng3 = rng.clone();
|
|
$(
|
|
let mut cases = String::new();
|
|
for (i1, i2) in iproduct!(
|
|
EDGE_CASES32.iter().cloned(),
|
|
EDGE_CASES32.iter().cloned()
|
|
).chain(EDGE_CASES32.iter().map(|i1| (*i1, f32(rng))))
|
|
.chain(EDGE_CASES32.iter().map(|i2| (f32(&mut rng2), *i2)))
|
|
.chain((0..NTESTS).map(|_| (f32(&mut rng3), f32(&mut rng3))))
|
|
{
|
|
let out = unsafe { $intr(i1, i2) };
|
|
|
|
let i1 = i1.to_bits();
|
|
let i2 = i2.to_bits();
|
|
let out = out.to_bits();
|
|
|
|
write!(cases, "(({}, {}), {})", i1, i2, out).unwrap();
|
|
cases.push(',');
|
|
}
|
|
|
|
let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
|
|
write!(f, "
|
|
#![deny(warnings)]
|
|
|
|
extern crate libm;
|
|
|
|
use std::panic;
|
|
|
|
#[test]
|
|
fn {0}() {{
|
|
const CASES: &[((u32, u32), u32)] = &[
|
|
{1}
|
|
];
|
|
|
|
for case in CASES {{
|
|
let ((i1, i2), expected) = *case;
|
|
|
|
if let Ok(outf) = panic::catch_unwind(|| {{
|
|
libm::{0}(f32::from_bits(i1), f32::from_bits(i2))
|
|
}}) {{
|
|
let outi = outf.to_bits();
|
|
|
|
if !((outf.is_nan() && f32::from_bits(expected).is_nan())
|
|
|| libm::_eqf(outi, expected))
|
|
{{
|
|
panic!(
|
|
\"input: {{:?}}, output: {{}}, expected: {{}}\",
|
|
(i1, i2),
|
|
outi,
|
|
expected,
|
|
);
|
|
}}
|
|
}} else {{
|
|
panic!(
|
|
\"input: {{:?}}, output: PANIC, expected: {{}}\",
|
|
(i1, i2),
|
|
expected,
|
|
);
|
|
}}
|
|
}}
|
|
}}
|
|
",
|
|
stringify!($intr),
|
|
cases)?;
|
|
)*
|
|
|
|
Ok(())
|
|
}
|
|
};
|
|
}
|
|
|
|
// fn(f32, f32, f32) -> f32
|
|
macro_rules! f32f32f32_f32 {
|
|
($($intr:ident,)*) => {
|
|
fn f32f32f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
|
extern "C" {
|
|
$(fn $intr(_: f32, _: f32, _: f32) -> f32;)*
|
|
}
|
|
|
|
let mut rng2 = rng.clone();
|
|
$(
|
|
let mut cases = String::new();
|
|
for (i1, i2, i3) in iproduct!(
|
|
EDGE_CASES32.iter().cloned(),
|
|
EDGE_CASES32.iter().cloned(),
|
|
EDGE_CASES32.iter().cloned()
|
|
).chain(EDGE_CASES32.iter().map(|i1| (*i1, f32(rng), f32(rng))))
|
|
.chain((0..NTESTS).map(|_| (f32(&mut rng2), f32(&mut rng2), f32(&mut rng2))))
|
|
{
|
|
let out = unsafe { $intr(i1, i2, i3) };
|
|
|
|
let i1 = i1.to_bits();
|
|
let i2 = i2.to_bits();
|
|
let i3 = i3.to_bits();
|
|
let out = out.to_bits();
|
|
|
|
write!(cases, "(({}, {}, {}), {})", i1, i2, i3, out).unwrap();
|
|
cases.push(',');
|
|
}
|
|
|
|
let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
|
|
write!(f, "
|
|
#![deny(warnings)]
|
|
|
|
extern crate libm;
|
|
|
|
use std::panic;
|
|
|
|
#[test]
|
|
fn {0}() {{
|
|
const CASES: &[((u32, u32, u32), u32)] = &[
|
|
{1}
|
|
];
|
|
|
|
for case in CASES {{
|
|
let ((i1, i2, i3), expected) = *case;
|
|
|
|
if let Ok(outf) = panic::catch_unwind(|| {{
|
|
libm::{0}(
|
|
f32::from_bits(i1),
|
|
f32::from_bits(i2),
|
|
f32::from_bits(i3),
|
|
)
|
|
}}) {{
|
|
let outi = outf.to_bits();
|
|
|
|
if !((outf.is_nan() && f32::from_bits(expected).is_nan())
|
|
|| libm::_eqf(outi, expected))
|
|
{{
|
|
panic!(
|
|
\"input: {{:?}}, output: {{}}, expected: {{}}\",
|
|
(i1, i2, i3),
|
|
outi,
|
|
expected,
|
|
);
|
|
}}
|
|
}} else {{
|
|
panic!(
|
|
\"input: {{:?}}, output: PANIC, expected: {{}}\",
|
|
(i1, i2, i3),
|
|
expected,
|
|
);
|
|
}}
|
|
}}
|
|
}}
|
|
",
|
|
stringify!($intr),
|
|
cases)?;
|
|
)*
|
|
|
|
Ok(())
|
|
}
|
|
};
|
|
}
|
|
|
|
// fn(f32, i32) -> f32
|
|
macro_rules! f32i32_f32 {
|
|
($($intr:ident,)*) => {
|
|
fn f32i32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
|
extern "C" {
|
|
$(fn $intr(_: f32, _: i32) -> f32;)*
|
|
}
|
|
|
|
let mut rng2 = rng.clone();
|
|
$(
|
|
let mut cases = String::new();
|
|
for i1 in EDGE_CASES32.iter().cloned().chain((0..NTESTS).map(|_| f32(&mut rng2))) {
|
|
let i2 = rng.gen_range(i16::MIN, i16::MAX);
|
|
let out = unsafe { $intr(i1, i2 as i32) };
|
|
|
|
let i1 = i1.to_bits();
|
|
let out = out.to_bits();
|
|
|
|
write!(cases, "(({}, {}), {})", i1, i2, out).unwrap();
|
|
cases.push(',');
|
|
}
|
|
|
|
let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
|
|
write!(f, "
|
|
#![deny(warnings)]
|
|
|
|
extern crate libm;
|
|
|
|
use std::panic;
|
|
|
|
#[test]
|
|
fn {0}() {{
|
|
const CASES: &[((u32, i16), u32)] = &[
|
|
{1}
|
|
];
|
|
|
|
for case in CASES {{
|
|
let ((i1, i2), expected) = *case;
|
|
|
|
if let Ok(outf) = panic::catch_unwind(|| {{
|
|
libm::{0}(f32::from_bits(i1), i2 as i32)
|
|
}}) {{
|
|
let outi = outf.to_bits();
|
|
|
|
if !((outf.is_nan() && f32::from_bits(expected).is_nan())
|
|
|| libm::_eqf(outi, expected))
|
|
{{
|
|
panic!(
|
|
\"input: {{:?}}, output: {{}}, expected: {{}}\",
|
|
(i1, i2),
|
|
outi,
|
|
expected,
|
|
);
|
|
}}
|
|
}} else {{
|
|
panic!(
|
|
\"input: {{:?}}, output: PANIC, expected: {{}}\",
|
|
(i1, i2),
|
|
expected,
|
|
);
|
|
}}
|
|
}}
|
|
}}
|
|
",
|
|
stringify!($intr),
|
|
cases)?;
|
|
)*
|
|
|
|
Ok(())
|
|
}
|
|
};
|
|
}
|
|
|
|
// fn(f64) -> f64
|
|
macro_rules! f64_f64 {
|
|
($($intr:ident,)*) => {
|
|
fn f64_f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
|
// MUSL C implementation of the function to test
|
|
extern "C" {
|
|
$(fn $intr(_: f64) -> f64;)*
|
|
}
|
|
|
|
$(
|
|
let mut cases = String::new();
|
|
for inp in EDGE_CASES64.iter().cloned().chain((0..NTESTS).map(|_| f64(rng))) {
|
|
let out = unsafe { $intr(inp) };
|
|
|
|
let inp = inp.to_bits();
|
|
let out = out.to_bits();
|
|
|
|
write!(cases, "({}, {})", inp, out).unwrap();
|
|
cases.push(',');
|
|
}
|
|
|
|
let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
|
|
write!(f, "
|
|
#![deny(warnings)]
|
|
|
|
extern crate libm;
|
|
|
|
use std::panic;
|
|
|
|
#[test]
|
|
fn {0}() {{
|
|
const CASES: &[(u64, u64)] = &[
|
|
{1}
|
|
];
|
|
|
|
for case in CASES {{
|
|
let (inp, expected) = *case;
|
|
|
|
if let Ok(outf) = panic::catch_unwind(|| {{
|
|
libm::{0}(f64::from_bits(inp))
|
|
}}) {{
|
|
let outi = outf.to_bits();
|
|
|
|
if !((outf.is_nan() && f64::from_bits(expected).is_nan())
|
|
|| libm::_eq(outi, expected))
|
|
{{
|
|
panic!(
|
|
\"input: {{}}, output: {{}}, expected: {{}}\",
|
|
inp,
|
|
outi,
|
|
expected,
|
|
);
|
|
}}
|
|
}} else {{
|
|
panic!(
|
|
\"input: {{}}, output: PANIC, expected: {{}}\",
|
|
inp,
|
|
expected,
|
|
);
|
|
}}
|
|
}}
|
|
}}
|
|
",
|
|
stringify!($intr),
|
|
cases)?;
|
|
)*
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
// fn(f64, f64) -> f64
|
|
macro_rules! f64f64_f64 {
|
|
($($intr:ident,)*) => {
|
|
fn f64f64_f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
|
extern "C" {
|
|
$(fn $intr(_: f64, _: f64) -> f64;)*
|
|
}
|
|
|
|
let mut rng2 = rng.clone();
|
|
let mut rng3 = rng.clone();
|
|
$(
|
|
let mut cases = String::new();
|
|
for (i1, i2) in iproduct!(
|
|
EDGE_CASES64.iter().cloned(),
|
|
EDGE_CASES64.iter().cloned()
|
|
).chain(EDGE_CASES64.iter().map(|i1| (*i1, f64(rng))))
|
|
.chain(EDGE_CASES64.iter().map(|i2| (f64(&mut rng2), *i2)))
|
|
.chain((0..NTESTS).map(|_| (f64(&mut rng3), f64(&mut rng3))))
|
|
{
|
|
let out = unsafe { $intr(i1, i2) };
|
|
|
|
let i1 = i1.to_bits();
|
|
let i2 = i2.to_bits();
|
|
let out = out.to_bits();
|
|
|
|
write!(cases, "(({}, {}), {})", i1, i2, out).unwrap();
|
|
cases.push(',');
|
|
}
|
|
|
|
let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
|
|
write!(f, "
|
|
#![deny(warnings)]
|
|
|
|
extern crate libm;
|
|
|
|
use std::panic;
|
|
|
|
#[test]
|
|
fn {0}() {{
|
|
const CASES: &[((u64, u64), u64)] = &[
|
|
{1}
|
|
];
|
|
|
|
for case in CASES {{
|
|
let ((i1, i2), expected) = *case;
|
|
|
|
if let Ok(outf) = panic::catch_unwind(|| {{
|
|
libm::{0}(f64::from_bits(i1), f64::from_bits(i2))
|
|
}}) {{
|
|
let outi = outf.to_bits();
|
|
|
|
if !((outf.is_nan() && f64::from_bits(expected).is_nan()) ||
|
|
libm::_eq(outi, expected)) {{
|
|
panic!(
|
|
\"input: {{:?}}, output: {{}}, expected: {{}}\",
|
|
(i1, i2),
|
|
outi,
|
|
expected,
|
|
);
|
|
}}
|
|
}} else {{
|
|
panic!(
|
|
\"input: {{:?}}, output: PANIC, expected: {{}}\",
|
|
(i1, i2),
|
|
expected,
|
|
);
|
|
}}
|
|
}}
|
|
}}
|
|
",
|
|
stringify!($intr),
|
|
cases)?;
|
|
)*
|
|
|
|
Ok(())
|
|
}
|
|
};
|
|
}
|
|
|
|
// fn(f64, f64, f64) -> f64
|
|
macro_rules! f64f64f64_f64 {
|
|
($($intr:ident,)*) => {
|
|
fn f64f64f64_f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
|
extern "C" {
|
|
$(fn $intr(_: f64, _: f64, _: f64) -> f64;)*
|
|
}
|
|
|
|
let mut rng2 = rng.clone();
|
|
$(
|
|
let mut cases = String::new();
|
|
for (i1, i2, i3) in iproduct!(
|
|
EDGE_CASES64.iter().cloned(),
|
|
EDGE_CASES64.iter().cloned(),
|
|
EDGE_CASES64.iter().cloned()
|
|
).chain(EDGE_CASES64.iter().map(|i1| (*i1, f64(rng), f64(rng))))
|
|
.chain((0..NTESTS).map(|_| (f64(&mut rng2), f64(&mut rng2), f64(&mut rng2))))
|
|
{
|
|
let out = unsafe { $intr(i1, i2, i3) };
|
|
|
|
let i1 = i1.to_bits();
|
|
let i2 = i2.to_bits();
|
|
let i3 = i3.to_bits();
|
|
let out = out.to_bits();
|
|
|
|
write!(cases, "(({}, {}, {}), {})", i1, i2, i3, out).unwrap();
|
|
cases.push(',');
|
|
}
|
|
|
|
let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
|
|
write!(f, "
|
|
#![deny(warnings)]
|
|
|
|
extern crate libm;
|
|
|
|
use std::panic;
|
|
|
|
#[test]
|
|
fn {0}() {{
|
|
const CASES: &[((u64, u64, u64), u64)] = &[
|
|
{1}
|
|
];
|
|
|
|
for case in CASES {{
|
|
let ((i1, i2, i3), expected) = *case;
|
|
|
|
if let Ok(outf) = panic::catch_unwind(|| {{
|
|
libm::{0}(
|
|
f64::from_bits(i1),
|
|
f64::from_bits(i2),
|
|
f64::from_bits(i3),
|
|
)
|
|
}}) {{
|
|
let outi = outf.to_bits();
|
|
|
|
if !((outf.is_nan() && f64::from_bits(expected).is_nan())
|
|
|| libm::_eq(outi, expected))
|
|
{{
|
|
panic!(
|
|
\"input: {{:?}}, output: {{}}, expected: {{}}\",
|
|
(i1, i2, i3),
|
|
outi,
|
|
expected,
|
|
);
|
|
}}
|
|
}} else {{
|
|
panic!(
|
|
\"input: {{:?}}, output: PANIC, expected: {{}}\",
|
|
(i1, i2, i3),
|
|
expected,
|
|
);
|
|
}}
|
|
}}
|
|
}}
|
|
",
|
|
stringify!($intr),
|
|
cases)?;
|
|
)*
|
|
|
|
Ok(())
|
|
}
|
|
};
|
|
}
|
|
|
|
// fn(f64, i32) -> f64
|
|
macro_rules! f64i32_f64 {
|
|
($($intr:ident,)*) => {
|
|
fn f64i32_f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
|
extern "C" {
|
|
$(fn $intr(_: f64, _: i32) -> f64;)*
|
|
}
|
|
|
|
let mut rng2 = rng.clone();
|
|
$(
|
|
let mut cases = String::new();
|
|
for i1 in EDGE_CASES64.iter().cloned().chain((0..NTESTS).map(|_| f64(&mut rng2))) {
|
|
let i2 = rng.gen_range(i16::MIN, i16::MAX);
|
|
let out = unsafe { $intr(i1, i2 as i32) };
|
|
|
|
let i1 = i1.to_bits();
|
|
let out = out.to_bits();
|
|
|
|
write!(cases, "(({}, {}), {})", i1, i2, out).unwrap();
|
|
cases.push(',');
|
|
}
|
|
|
|
let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
|
|
write!(f, "
|
|
#![deny(warnings)]
|
|
|
|
extern crate libm;
|
|
|
|
use std::panic;
|
|
|
|
#[test]
|
|
fn {0}() {{
|
|
const CASES: &[((u64, i16), u64)] = &[
|
|
{1}
|
|
];
|
|
|
|
for case in CASES {{
|
|
let ((i1, i2), expected) = *case;
|
|
|
|
if let Ok(outf) = panic::catch_unwind(|| {{
|
|
libm::{0}(f64::from_bits(i1), i2 as i32)
|
|
}}) {{
|
|
let outi = outf.to_bits();
|
|
|
|
if !((outf.is_nan() && f64::from_bits(expected).is_nan()) ||
|
|
libm::_eq(outi, expected)) {{
|
|
panic!(
|
|
\"input: {{:?}}, output: {{}}, expected: {{}}\",
|
|
(i1, i2),
|
|
outi,
|
|
expected,
|
|
);
|
|
}}
|
|
}} else {{
|
|
panic!(
|
|
\"input: {{:?}}, output: PANIC, expected: {{}}\",
|
|
(i1, i2),
|
|
expected,
|
|
);
|
|
}}
|
|
}}
|
|
}}
|
|
",
|
|
stringify!($intr),
|
|
cases)?;
|
|
)*
|
|
|
|
Ok(())
|
|
}
|
|
};
|
|
}
|
|
|
|
fn main() -> Result<(), Box<Error>> {
|
|
fs::remove_dir_all("tests").ok();
|
|
fs::create_dir("tests")?;
|
|
|
|
let mut rng = XorShiftRng::from_rng(&mut rand::thread_rng())?;
|
|
|
|
f32_f32(&mut rng)?;
|
|
f32f32_f32(&mut rng)?;
|
|
f32f32f32_f32(&mut rng)?;
|
|
f32i32_f32(&mut rng)?;
|
|
f64_f64(&mut rng)?;
|
|
f64f64_f64(&mut rng)?;
|
|
f64f64f64_f64(&mut rng)?;
|
|
f64i32_f64(&mut rng)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/* Functions to test */
|
|
|
|
// With signature `fn(f32) -> f32`
|
|
f32_f32! {
|
|
acosf,
|
|
floorf,
|
|
truncf,
|
|
asinf,
|
|
atanf,
|
|
cbrtf,
|
|
cosf,
|
|
ceilf,
|
|
coshf,
|
|
exp2f,
|
|
expf,
|
|
expm1f,
|
|
log10f,
|
|
log1pf,
|
|
log2f,
|
|
logf,
|
|
roundf,
|
|
sinf,
|
|
sinhf,
|
|
tanf,
|
|
tanhf,
|
|
fabsf,
|
|
sqrtf,
|
|
}
|
|
|
|
// With signature `fn(f32, f32) -> f32`
|
|
f32f32_f32! {
|
|
atan2f,
|
|
fdimf,
|
|
hypotf,
|
|
fmodf,
|
|
powf,
|
|
}
|
|
|
|
// With signature `fn(f32, f32, f32) -> f32`
|
|
f32f32f32_f32! {
|
|
fmaf,
|
|
}
|
|
|
|
// With signature `fn(f32, i32) -> f32`
|
|
f32i32_f32! {
|
|
scalbnf,
|
|
}
|
|
|
|
// With signature `fn(f64) -> f64`
|
|
f64_f64! {
|
|
acos,
|
|
asin,
|
|
atan,
|
|
cbrt,
|
|
ceil,
|
|
cos,
|
|
cosh,
|
|
exp,
|
|
exp2,
|
|
expm1,
|
|
floor,
|
|
log,
|
|
log10,
|
|
log1p,
|
|
log2,
|
|
round,
|
|
sin,
|
|
sinh,
|
|
sqrt,
|
|
tan,
|
|
tanh,
|
|
trunc,
|
|
fabs,
|
|
}
|
|
|
|
// With signature `fn(f64, f64) -> f64`
|
|
f64f64_f64! {
|
|
atan2,
|
|
fdim,
|
|
fmod,
|
|
hypot,
|
|
// pow,
|
|
}
|
|
|
|
// With signature `fn(f64, f64, f64) -> f64`
|
|
f64f64f64_f64! {
|
|
fma,
|
|
}
|
|
|
|
// With signature `fn(f64, i32) -> f64`
|
|
f64i32_f64! {
|
|
scalbn,
|
|
}
|