add newlib support to the test generator
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
.#*
|
.#*
|
||||||
|
/bin
|
||||||
|
/math/src
|
||||||
|
/math/target
|
||||||
/target
|
/target
|
||||||
/tests
|
/tests
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|||||||
+23
-14
@@ -5,21 +5,29 @@ sudo: required
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- env: TARGET=aarch64-unknown-linux-gnu
|
- env: TARGET=aarch64-unknown-linux-gnu
|
||||||
- env: TARGET=armv7-unknown-linux-gnueabihf
|
|
||||||
- env: TARGET=i686-unknown-linux-gnu
|
|
||||||
- env: TARGET=mips-unknown-linux-gnu
|
|
||||||
- env: TARGET=mips64-unknown-linux-gnuabi64
|
|
||||||
- env: TARGET=mips64el-unknown-linux-gnuabi64
|
|
||||||
- env: TARGET=mipsel-unknown-linux-gnu
|
|
||||||
- env: TARGET=powerpc-unknown-linux-gnu
|
|
||||||
- env: TARGET=powerpc64-unknown-linux-gnu
|
|
||||||
- env: TARGET=powerpc64le-unknown-linux-gnu
|
|
||||||
- env: TARGET=x86_64-unknown-linux-gnu
|
|
||||||
- env: TARGET=cargo-fmt
|
|
||||||
rust: beta
|
|
||||||
# no-panic link test
|
|
||||||
- env: TARGET=armv7-unknown-linux-gnueabihf
|
|
||||||
rust: nightly
|
rust: nightly
|
||||||
|
# - env: TARGET=armv7-unknown-linux-gnueabihf
|
||||||
|
# rust: nightly
|
||||||
|
# - env: TARGET=i686-unknown-linux-gnu
|
||||||
|
# rust: nightly
|
||||||
|
# - env: TARGET=mips-unknown-linux-gnu
|
||||||
|
# rust: nightly
|
||||||
|
# - env: TARGET=mips64-unknown-linux-gnuabi64
|
||||||
|
# rust: nightly
|
||||||
|
# - env: TARGET=mips64el-unknown-linux-gnuabi64
|
||||||
|
# rust: nightly
|
||||||
|
# - env: TARGET=mipsel-unknown-linux-gnu
|
||||||
|
# rust: nightly
|
||||||
|
# - env: TARGET=powerpc-unknown-linux-gnu
|
||||||
|
# rust: nightly
|
||||||
|
# - env: TARGET=powerpc64-unknown-linux-gnu
|
||||||
|
# rust: nightly
|
||||||
|
# - env: TARGET=powerpc64le-unknown-linux-gnu
|
||||||
|
# rust: nightly
|
||||||
|
- env: TARGET=x86_64-unknown-linux-gnu
|
||||||
|
rust: nightly
|
||||||
|
# - env: TARGET=cargo-fmt
|
||||||
|
# rust: beta
|
||||||
|
|
||||||
before_install: set -e
|
before_install: set -e
|
||||||
|
|
||||||
@@ -27,6 +35,7 @@ install:
|
|||||||
- bash ci/install.sh
|
- bash ci/install.sh
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
- export PATH=$HOME/.local/bin:$PATH
|
||||||
- bash ci/script.sh
|
- bash ci/script.sh
|
||||||
|
|
||||||
after_script: set +e
|
after_script: set +e
|
||||||
|
|||||||
+10
-1
@@ -14,4 +14,13 @@ version = "0.1.2"
|
|||||||
checked = []
|
checked = []
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["cb", "test-generator"]
|
members = [
|
||||||
|
"cb",
|
||||||
|
"input-generator",
|
||||||
|
"musl-generator",
|
||||||
|
"newlib-generator",
|
||||||
|
"shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
shared = { path = "shared" }
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ main() {
|
|||||||
if [ $TARGET != x86_64-unknown-linux-gnu ]; then
|
if [ $TARGET != x86_64-unknown-linux-gnu ]; then
|
||||||
rustup target add $TARGET
|
rustup target add $TARGET
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mkdir -p ~/.local/bin
|
||||||
|
curl -L https://github.com/japaric/qemu-bin/raw/master/14.04/qemu-arm-2.12.0 > ~/.local/bin/qemu-arm
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
main
|
||||||
|
|||||||
+10
-7
@@ -7,10 +7,11 @@ main() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# test that the functions don't contain invocations of `panic!`
|
# test that the functions don't contain invocations of `panic!`
|
||||||
if [ $TRAVIS_RUST_VERSION = nightly ]; then
|
case $TARGET in
|
||||||
cross build --release --target $TARGET --example no-panic
|
armv7-unknown-linux-gnueabihf)
|
||||||
return
|
cross build --release --target $TARGET --example no-panic
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
# quick check
|
# quick check
|
||||||
cargo check
|
cargo check
|
||||||
@@ -18,12 +19,14 @@ main() {
|
|||||||
# check that we can source import libm into compiler-builtins
|
# check that we can source import libm into compiler-builtins
|
||||||
cargo check --package cb
|
cargo check --package cb
|
||||||
|
|
||||||
|
# generate tests
|
||||||
|
cargo run -p input-generator --target x86_64-unknown-linux-musl
|
||||||
|
cargo run -p musl-generator --target x86_64-unknown-linux-musl
|
||||||
|
cargo run -p newlib-generator
|
||||||
|
|
||||||
# run unit tests
|
# run unit tests
|
||||||
cross test --lib --features checked --target $TARGET --release
|
cross test --lib --features checked --target $TARGET --release
|
||||||
|
|
||||||
# generate tests
|
|
||||||
cargo run --package test-generator --target x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
# run generated tests
|
# run generated tests
|
||||||
cross test --tests --features checked --target $TARGET --release
|
cross test --tests --features checked --target $TARGET --release
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "test-generator"
|
name = "input-generator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||||
publish = false
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.5.3"
|
rand = "0.5.4"
|
||||||
itertools = "0.7.8"
|
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
extern crate rand;
|
||||||
|
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use rand::{RngCore, SeedableRng, XorShiftRng};
|
||||||
|
|
||||||
|
const NTESTS: usize = 10_000;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
let mut rng = XorShiftRng::from_rng(&mut rand::thread_rng())?;
|
||||||
|
|
||||||
|
fs::remove_dir_all("bin").ok();
|
||||||
|
fs::create_dir_all("bin/input")?;
|
||||||
|
fs::create_dir_all("bin/output")?;
|
||||||
|
|
||||||
|
f32(&mut rng)?;
|
||||||
|
f32f32(&mut rng)?;
|
||||||
|
f32f32f32(&mut rng)?;
|
||||||
|
f32i16(&mut rng)?;
|
||||||
|
f64(&mut rng)?;
|
||||||
|
f64f64(&mut rng)?;
|
||||||
|
f64f64f64(&mut rng)?;
|
||||||
|
f64i16(&mut rng)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
|
let mut set = BTreeSet::new();
|
||||||
|
|
||||||
|
while set.len() < NTESTS {
|
||||||
|
let f = f32::from_bits(rng.next_u32());
|
||||||
|
|
||||||
|
if f.is_nan() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
set.insert(f.to_bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut f = File::create("bin/input/f32")?;
|
||||||
|
for i in set {
|
||||||
|
f.write_all(&i.to_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f32f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
|
let mut f = File::create("bin/input/f32f32")?;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < NTESTS {
|
||||||
|
let x0 = f32::from_bits(rng.next_u32());
|
||||||
|
let x1 = f32::from_bits(rng.next_u32());
|
||||||
|
|
||||||
|
if x0.is_nan() || x1.is_nan() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
f.write_all(&x0.to_bits().to_bytes())?;
|
||||||
|
f.write_all(&x1.to_bits().to_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f32i16(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
|
let mut f = File::create("bin/input/f32i16")?;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < NTESTS {
|
||||||
|
let x0 = f32::from_bits(rng.next_u32());
|
||||||
|
let x1 = rng.next_u32() as i16;
|
||||||
|
|
||||||
|
if x0.is_nan() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
f.write_all(&x0.to_bits().to_bytes())?;
|
||||||
|
f.write_all(&x1.to_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f32f32f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
|
let mut f = File::create("bin/input/f32f32f32")?;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < NTESTS {
|
||||||
|
let x0 = f32::from_bits(rng.next_u32());
|
||||||
|
let x1 = f32::from_bits(rng.next_u32());
|
||||||
|
let x2 = f32::from_bits(rng.next_u32());
|
||||||
|
|
||||||
|
if x0.is_nan() || x1.is_nan() || x2.is_nan() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
f.write_all(&x0.to_bits().to_bytes())?;
|
||||||
|
f.write_all(&x1.to_bits().to_bytes())?;
|
||||||
|
f.write_all(&x2.to_bits().to_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
|
let mut set = BTreeSet::new();
|
||||||
|
|
||||||
|
while set.len() < NTESTS {
|
||||||
|
let f = f64::from_bits(rng.next_u64());
|
||||||
|
|
||||||
|
if f.is_nan() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
set.insert(f.to_bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut f = File::create("bin/input/f64")?;
|
||||||
|
for i in set {
|
||||||
|
f.write_all(&i.to_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f64f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
|
let mut f = File::create("bin/input/f64f64")?;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < NTESTS {
|
||||||
|
let x0 = f64::from_bits(rng.next_u64());
|
||||||
|
let x1 = f64::from_bits(rng.next_u64());
|
||||||
|
|
||||||
|
if x0.is_nan() || x1.is_nan() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
f.write_all(&x0.to_bits().to_bytes())?;
|
||||||
|
f.write_all(&x1.to_bits().to_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f64f64f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
|
let mut f = File::create("bin/input/f64f64f64")?;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < NTESTS {
|
||||||
|
let x0 = f64::from_bits(rng.next_u64());
|
||||||
|
let x1 = f64::from_bits(rng.next_u64());
|
||||||
|
let x2 = f64::from_bits(rng.next_u64());
|
||||||
|
|
||||||
|
if x0.is_nan() || x1.is_nan() || x2.is_nan() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
f.write_all(&x0.to_bits().to_bytes())?;
|
||||||
|
f.write_all(&x1.to_bits().to_bytes())?;
|
||||||
|
f.write_all(&x2.to_bits().to_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f64i16(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
|
||||||
|
let mut f = File::create("bin/input/f64i16")?;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < NTESTS {
|
||||||
|
let x0 = f64::from_bits(rng.next_u64());
|
||||||
|
let x1 = rng.next_u32() as i16;
|
||||||
|
|
||||||
|
if x0.is_nan() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
f.write_all(&x0.to_bits().to_bytes())?;
|
||||||
|
f.write_all(&x1.to_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[target.thumbv7em-none-eabi]
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Wl,-Tlink.x",
|
||||||
|
"-C", "link-arg=-nostartfiles",
|
||||||
|
"-C", "link-arg=-mthumb",
|
||||||
|
"-C", "link-arg=-march=armv7e-m",
|
||||||
|
"-C", "link-arg=-mfloat-abi=soft",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7em-none-eabi"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "math"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
qemu-arm-rt = { git = "https://github.com/japaric/qemu-arm-rt" }
|
||||||
|
|
||||||
|
[workspace]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[target.thumbv7em-none-eabi]
|
||||||
|
xargo = false
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "musl-generator"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lazy_static = "1.0.2"
|
||||||
|
shared = { path = "../shared" }
|
||||||
|
libm = { path = ".." }
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
macro_rules! f32 {
|
||||||
|
($($fun:ident,)+) => {{
|
||||||
|
$(
|
||||||
|
// check type signature
|
||||||
|
let _: fn(f32) -> f32 = libm::$fun;
|
||||||
|
let mut $fun = File::create(concat!("bin/output/musl.", stringify!($fun)))?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
for x in shared::F32.iter() {
|
||||||
|
$(
|
||||||
|
let y = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $fun(_: f32) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fun(*x)
|
||||||
|
};
|
||||||
|
|
||||||
|
$fun.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f32f32 {
|
||||||
|
($($fun:ident,)+) => {{
|
||||||
|
$(
|
||||||
|
// check type signature
|
||||||
|
let _: fn(f32, f32) -> f32 = libm::$fun;
|
||||||
|
let mut $fun = File::create(concat!("bin/output/musl.", stringify!($fun)))?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
for (x0, x1) in shared::F32F32.iter() {
|
||||||
|
$(
|
||||||
|
let y = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $fun(_: f32, _: f32) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fun(*x0, *x1)
|
||||||
|
};
|
||||||
|
|
||||||
|
$fun.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f32f32f32 {
|
||||||
|
($($fun:ident,)+) => {{
|
||||||
|
$(
|
||||||
|
// check type signature
|
||||||
|
let _: fn(f32, f32, f32) -> f32 = libm::$fun;
|
||||||
|
let mut $fun = File::create(concat!("bin/output/musl.", stringify!($fun)))?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
for (x0, x1, x2) in shared::F32F32F32.iter() {
|
||||||
|
$(
|
||||||
|
let y = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $fun(_: f32, _: f32, _: f32) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fun(*x0, *x1, *x2)
|
||||||
|
};
|
||||||
|
|
||||||
|
$fun.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f32i32 {
|
||||||
|
($($fun:ident,)+) => {{
|
||||||
|
$(
|
||||||
|
// check type signature
|
||||||
|
let _: fn(f32, i32) -> f32 = libm::$fun;
|
||||||
|
let mut $fun = File::create(concat!("bin/output/musl.", stringify!($fun)))?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
for (x0, x1) in shared::F32I32.iter() {
|
||||||
|
$(
|
||||||
|
let y = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $fun(_: f32, _: i32) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fun(*x0, *x1 as i32)
|
||||||
|
};
|
||||||
|
|
||||||
|
$fun.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f64 {
|
||||||
|
($($fun:ident,)+) => {{
|
||||||
|
$(
|
||||||
|
// check type signature
|
||||||
|
let _: fn(f64) -> f64 = libm::$fun;
|
||||||
|
let mut $fun = File::create(concat!("bin/output/musl.", stringify!($fun)))?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
for x in shared::F64.iter() {
|
||||||
|
$(
|
||||||
|
let y = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $fun(_: f64) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fun(*x)
|
||||||
|
};
|
||||||
|
|
||||||
|
$fun.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f64f64 {
|
||||||
|
($($fun:ident,)+) => {{
|
||||||
|
$(
|
||||||
|
// check type signature
|
||||||
|
let _: fn(f64, f64) -> f64 = libm::$fun;
|
||||||
|
let mut $fun = File::create(concat!("bin/output/musl.", stringify!($fun)))?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
for (x0, x1) in shared::F64F64.iter() {
|
||||||
|
$(
|
||||||
|
let y = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $fun(_: f64, _: f64) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fun(*x0, *x1)
|
||||||
|
};
|
||||||
|
|
||||||
|
$fun.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f64f64f64 {
|
||||||
|
($($fun:ident,)+) => {{
|
||||||
|
$(
|
||||||
|
// check type signature
|
||||||
|
let _: fn(f64, f64, f64) -> f64 = libm::$fun;
|
||||||
|
let mut $fun = File::create(concat!("bin/output/musl.", stringify!($fun)))?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
for (x0, x1, x2) in shared::F64F64F64.iter() {
|
||||||
|
$(
|
||||||
|
let y = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $fun(_: f64, _: f64, _: f64) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fun(*x0, *x1, *x2)
|
||||||
|
};
|
||||||
|
|
||||||
|
$fun.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f64i32 {
|
||||||
|
($($fun:ident,)+) => {{
|
||||||
|
$(
|
||||||
|
// check type signature
|
||||||
|
let _: fn(f64, i32) -> f64 = libm::$fun;
|
||||||
|
let mut $fun = File::create(concat!("bin/output/musl.", stringify!($fun)))?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
for (x0, x1) in shared::F64I32.iter() {
|
||||||
|
$(
|
||||||
|
let y = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $fun(_: f64, _: i32) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fun(*x0, *x1 as i32)
|
||||||
|
};
|
||||||
|
|
||||||
|
$fun.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
extern crate libm;
|
||||||
|
extern crate shared;
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
f32! {
|
||||||
|
acosf,
|
||||||
|
asinf,
|
||||||
|
atanf,
|
||||||
|
cbrtf,
|
||||||
|
ceilf,
|
||||||
|
cosf,
|
||||||
|
coshf,
|
||||||
|
exp2f,
|
||||||
|
expf,
|
||||||
|
expm1f,
|
||||||
|
fabsf,
|
||||||
|
floorf,
|
||||||
|
log10f,
|
||||||
|
log1pf,
|
||||||
|
log2f,
|
||||||
|
logf,
|
||||||
|
roundf,
|
||||||
|
sinf,
|
||||||
|
sinhf,
|
||||||
|
sqrtf,
|
||||||
|
tanf,
|
||||||
|
tanhf,
|
||||||
|
truncf,
|
||||||
|
}
|
||||||
|
|
||||||
|
f32f32! {
|
||||||
|
atan2f,
|
||||||
|
fdimf,
|
||||||
|
fmodf,
|
||||||
|
hypotf,
|
||||||
|
powf,
|
||||||
|
}
|
||||||
|
|
||||||
|
f32i32! {
|
||||||
|
scalbnf,
|
||||||
|
}
|
||||||
|
|
||||||
|
f32f32f32! {
|
||||||
|
fmaf,
|
||||||
|
}
|
||||||
|
|
||||||
|
f64! {
|
||||||
|
acos,
|
||||||
|
asin,
|
||||||
|
atan,
|
||||||
|
cbrt,
|
||||||
|
ceil,
|
||||||
|
cos,
|
||||||
|
cosh,
|
||||||
|
exp,
|
||||||
|
exp2,
|
||||||
|
expm1,
|
||||||
|
fabs,
|
||||||
|
floor,
|
||||||
|
log,
|
||||||
|
log10,
|
||||||
|
log1p,
|
||||||
|
log2,
|
||||||
|
round,
|
||||||
|
sin,
|
||||||
|
sinh,
|
||||||
|
sqrt,
|
||||||
|
tan,
|
||||||
|
tanh,
|
||||||
|
trunc,
|
||||||
|
}
|
||||||
|
|
||||||
|
f64f64! {
|
||||||
|
atan2,
|
||||||
|
fdim,
|
||||||
|
fmod,
|
||||||
|
hypot,
|
||||||
|
pow,
|
||||||
|
}
|
||||||
|
|
||||||
|
f64i32! {
|
||||||
|
scalbn,
|
||||||
|
}
|
||||||
|
|
||||||
|
f64f64f64! {
|
||||||
|
fma,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "newlib-generator"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
shared = { path = "../shared" }
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
macro_rules! f32 {
|
||||||
|
($($fun:ident,)+) => {
|
||||||
|
$(
|
||||||
|
let fun = stringify!($fun);
|
||||||
|
|
||||||
|
fs::create_dir_all("math/src")?;
|
||||||
|
|
||||||
|
let main = format!("
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate qemu_arm_rt as rt;
|
||||||
|
|
||||||
|
use core::u32;
|
||||||
|
|
||||||
|
use rt::{{io, process}};
|
||||||
|
|
||||||
|
entry!(main);
|
||||||
|
|
||||||
|
fn main() {{
|
||||||
|
run().unwrap_or_else(|e| {{
|
||||||
|
eprintln!(\"error: {{}}\", e);
|
||||||
|
process::exit(1);
|
||||||
|
}})
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn run() -> Result<(), usize> {{
|
||||||
|
#[link(name = \"m\")]
|
||||||
|
extern \"C\" {{
|
||||||
|
fn {0}(_: f32) -> f32;
|
||||||
|
}}
|
||||||
|
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
while let Ok(()) = io::Stdin.read_exact(&mut buf) {{
|
||||||
|
let x = f32::from_bits(u32::from_bytes(buf));
|
||||||
|
let y = unsafe {{ {0}(x) }};
|
||||||
|
|
||||||
|
io::Stdout.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
}}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn __errno() -> *mut i32 {{
|
||||||
|
static mut ERRNO: i32 = 0;
|
||||||
|
unsafe {{ &mut ERRNO }}
|
||||||
|
}}
|
||||||
|
", fun);
|
||||||
|
|
||||||
|
File::create("math/src/main.rs")?.write_all(main.as_bytes())?;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
Command::new("cross")
|
||||||
|
.args(&["build", "--target", "thumbv7em-none-eabi", "--release"])
|
||||||
|
.current_dir("math")
|
||||||
|
.status()?
|
||||||
|
.success()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut qemu = Command::new("qemu-arm")
|
||||||
|
.arg("math/target/thumbv7em-none-eabi/release/math")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
qemu.stdin.as_mut().take().unwrap().write_all(F32)?;
|
||||||
|
|
||||||
|
let output = qemu.wait_with_output()?;
|
||||||
|
|
||||||
|
File::create(concat!("bin/output/newlib.", stringify!($fun)))?
|
||||||
|
.write_all(&output.stdout)?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f32f32 {
|
||||||
|
($($fun:ident,)+) => {
|
||||||
|
$(
|
||||||
|
let fun = stringify!($fun);
|
||||||
|
|
||||||
|
fs::create_dir_all("math/src")?;
|
||||||
|
|
||||||
|
let main = format!("
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate qemu_arm_rt as rt;
|
||||||
|
|
||||||
|
use core::u32;
|
||||||
|
|
||||||
|
use rt::{{io, process}};
|
||||||
|
|
||||||
|
entry!(main);
|
||||||
|
|
||||||
|
fn main() {{
|
||||||
|
run().unwrap_or_else(|e| {{
|
||||||
|
eprintln!(\"error: {{}}\", e);
|
||||||
|
process::exit(1);
|
||||||
|
}})
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn run() -> Result<(), usize> {{
|
||||||
|
#[link(name = \"m\")]
|
||||||
|
extern \"C\" {{
|
||||||
|
fn {0}(_: f32, _: f32) -> f32;
|
||||||
|
}}
|
||||||
|
|
||||||
|
let mut chunk = [0; 8];
|
||||||
|
while let Ok(()) = io::Stdin.read_exact(&mut chunk) {{
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
buf.copy_from_slice(&chunk[..4]);
|
||||||
|
let x0 = f32::from_bits(u32::from_bytes(buf));
|
||||||
|
|
||||||
|
buf.copy_from_slice(&chunk[4..]);
|
||||||
|
let x1 = f32::from_bits(u32::from_bytes(buf));
|
||||||
|
|
||||||
|
let y = unsafe {{ {0}(x0, x1) }};
|
||||||
|
|
||||||
|
io::Stdout.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
}}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn __errno() -> *mut i32 {{
|
||||||
|
static mut ERRNO: i32 = 0;
|
||||||
|
unsafe {{ &mut ERRNO }}
|
||||||
|
}}
|
||||||
|
", fun);
|
||||||
|
|
||||||
|
File::create("math/src/main.rs")?.write_all(main.as_bytes())?;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
Command::new("cross")
|
||||||
|
.args(&["build", "--target", "thumbv7em-none-eabi", "--release"])
|
||||||
|
.current_dir("math")
|
||||||
|
.status()?
|
||||||
|
.success()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut qemu = Command::new("qemu-arm")
|
||||||
|
.arg("math/target/thumbv7em-none-eabi/release/math")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
qemu.stdin.as_mut().take().unwrap().write_all(F32)?;
|
||||||
|
|
||||||
|
let output = qemu.wait_with_output()?;
|
||||||
|
|
||||||
|
File::create(concat!("bin/output/newlib.", stringify!($fun)))?
|
||||||
|
.write_all(&output.stdout)?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! f32f32f32 {
|
||||||
|
($($fun:ident,)+) => {
|
||||||
|
$(
|
||||||
|
let fun = stringify!($fun);
|
||||||
|
|
||||||
|
fs::create_dir_all("math/src")?;
|
||||||
|
|
||||||
|
let main = format!("
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate qemu_arm_rt as rt;
|
||||||
|
|
||||||
|
use core::u32;
|
||||||
|
|
||||||
|
use rt::{{io, process}};
|
||||||
|
|
||||||
|
entry!(main);
|
||||||
|
|
||||||
|
fn main() {{
|
||||||
|
run().unwrap_or_else(|e| {{
|
||||||
|
eprintln!(\"error: {{}}\", e);
|
||||||
|
process::exit(1);
|
||||||
|
}})
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn run() -> Result<(), usize> {{
|
||||||
|
#[link(name = \"m\")]
|
||||||
|
extern \"C\" {{
|
||||||
|
fn {0}(_: f32, _: f32, _: f32) -> f32;
|
||||||
|
}}
|
||||||
|
|
||||||
|
let mut chunk = [0; 12];
|
||||||
|
while let Ok(()) = io::Stdin.read_exact(&mut chunk) {{
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
buf.copy_from_slice(&chunk[..4]);
|
||||||
|
let x0 = f32::from_bits(u32::from_bytes(buf));
|
||||||
|
|
||||||
|
buf.copy_from_slice(&chunk[4..8]);
|
||||||
|
let x1 = f32::from_bits(u32::from_bytes(buf));
|
||||||
|
|
||||||
|
buf.copy_from_slice(&chunk[8..]);
|
||||||
|
let x2 = f32::from_bits(u32::from_bytes(buf));
|
||||||
|
|
||||||
|
let y = unsafe {{ {0}(x0, x1, x2) }};
|
||||||
|
|
||||||
|
io::Stdout.write_all(&y.to_bits().to_bytes())?;
|
||||||
|
}}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn __errno() -> *mut i32 {{
|
||||||
|
static mut ERRNO: i32 = 0;
|
||||||
|
unsafe {{ &mut ERRNO }}
|
||||||
|
}}
|
||||||
|
", fun);
|
||||||
|
|
||||||
|
File::create("math/src/main.rs")?.write_all(main.as_bytes())?;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
Command::new("cross")
|
||||||
|
.args(&["build", "--target", "thumbv7em-none-eabi", "--release"])
|
||||||
|
.current_dir("math")
|
||||||
|
.status()?
|
||||||
|
.success()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut qemu = Command::new("qemu-arm")
|
||||||
|
.arg("math/target/thumbv7em-none-eabi/release/math")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
qemu.stdin.as_mut().take().unwrap().write_all(F32)?;
|
||||||
|
|
||||||
|
let output = qemu.wait_with_output()?;
|
||||||
|
|
||||||
|
File::create(concat!("bin/output/newlib.", stringify!($fun)))?
|
||||||
|
.write_all(&output.stdout)?;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
extern crate shared;
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
const F32: &[u8] = include_bytes!("../../bin/input/f32");
|
||||||
|
|
||||||
|
f32! {
|
||||||
|
asinf,
|
||||||
|
cbrtf,
|
||||||
|
cosf,
|
||||||
|
exp2f,
|
||||||
|
sinf,
|
||||||
|
tanf,
|
||||||
|
}
|
||||||
|
|
||||||
|
f32f32! {
|
||||||
|
hypotf,
|
||||||
|
}
|
||||||
|
|
||||||
|
f32f32f32! {
|
||||||
|
fmaf,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lazy_static = "1.0.2"
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
#![feature(exact_chunks)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref F32: Vec<f32> = {
|
||||||
|
let bytes = include_bytes!("../../bin/input/f32");
|
||||||
|
|
||||||
|
bytes
|
||||||
|
.exact_chunks(4)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
pub static ref F32F32: Vec<(f32, f32)> = {
|
||||||
|
let bytes = include_bytes!("../../bin/input/f32f32");
|
||||||
|
|
||||||
|
bytes
|
||||||
|
.exact_chunks(8)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut x0 = [0; 4];
|
||||||
|
let mut x1 = [0; 4];
|
||||||
|
x0.copy_from_slice(&chunk[..4]);
|
||||||
|
x1.copy_from_slice(&chunk[4..]);
|
||||||
|
|
||||||
|
(
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(x0))),
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(x1))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
pub static ref F32F32F32: Vec<(f32, f32, f32)> = {
|
||||||
|
let bytes = include_bytes!("../../bin/input/f32f32f32");
|
||||||
|
|
||||||
|
bytes
|
||||||
|
.exact_chunks(12)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut x0 = [0; 4];
|
||||||
|
let mut x1 = [0; 4];
|
||||||
|
let mut x2 = [0; 4];
|
||||||
|
x0.copy_from_slice(&chunk[..4]);
|
||||||
|
x1.copy_from_slice(&chunk[4..8]);
|
||||||
|
x2.copy_from_slice(&chunk[8..]);
|
||||||
|
|
||||||
|
(
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(x0))),
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(x1))),
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(x2))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
pub static ref F32I32: Vec<(f32, i32)> = {
|
||||||
|
let bytes = include_bytes!("../../bin/input/f32i16");
|
||||||
|
|
||||||
|
bytes
|
||||||
|
.exact_chunks(6)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut x0 = [0; 4];
|
||||||
|
let mut x1 = [0; 2];
|
||||||
|
x0.copy_from_slice(&chunk[..4]);
|
||||||
|
x1.copy_from_slice(&chunk[4..]);
|
||||||
|
|
||||||
|
(
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(x0))),
|
||||||
|
i16::from_le(i16::from_bytes(x1)) as i32,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
pub static ref F64: Vec<f64> = {
|
||||||
|
let bytes = include_bytes!("../../bin/input/f64");
|
||||||
|
|
||||||
|
bytes
|
||||||
|
.exact_chunks(8)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 8];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
pub static ref F64F64: Vec<(f64, f64)> = {
|
||||||
|
let bytes = include_bytes!("../../bin/input/f64f64");
|
||||||
|
|
||||||
|
bytes
|
||||||
|
.exact_chunks(16)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut x0 = [0; 8];
|
||||||
|
let mut x1 = [0; 8];
|
||||||
|
x0.copy_from_slice(&chunk[..8]);
|
||||||
|
x1.copy_from_slice(&chunk[8..]);
|
||||||
|
|
||||||
|
(
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(x0))),
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(x1))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
pub static ref F64F64F64: Vec<(f64, f64, f64)> = {
|
||||||
|
let bytes = include_bytes!("../../bin/input/f64f64f64");
|
||||||
|
|
||||||
|
bytes
|
||||||
|
.exact_chunks(24)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut x0 = [0; 8];
|
||||||
|
let mut x1 = [0; 8];
|
||||||
|
let mut x2 = [0; 8];
|
||||||
|
x0.copy_from_slice(&chunk[..8]);
|
||||||
|
x1.copy_from_slice(&chunk[8..16]);
|
||||||
|
x2.copy_from_slice(&chunk[16..]);
|
||||||
|
|
||||||
|
(
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(x0))),
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(x1))),
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(x2))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
pub static ref F64I32: Vec<(f64, i32)> = {
|
||||||
|
let bytes = include_bytes!("../../bin/input/f64i16");
|
||||||
|
|
||||||
|
bytes
|
||||||
|
.exact_chunks(10)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut x0 = [0; 8];
|
||||||
|
let mut x1 = [0; 2];
|
||||||
|
x0.copy_from_slice(&chunk[..8]);
|
||||||
|
x1.copy_from_slice(&chunk[8..]);
|
||||||
|
|
||||||
|
(
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(x0))),
|
||||||
|
i16::from_le(i16::from_bytes(x1)) as i32,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! f32 {
|
||||||
|
($lib:expr, $($fun:ident),+) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $fun() {
|
||||||
|
let expected = include_bytes!(concat!("../bin/output/", $lib, ".", stringify!($fun)))
|
||||||
|
.exact_chunks(4)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (input, expected) in $crate::F32.iter().zip(&expected) {
|
||||||
|
if let Ok(output) = panic::catch_unwind(|| libm::$fun(*input)) {
|
||||||
|
if let Err(error) = libm::_eqf(output, *expected) {
|
||||||
|
panic!(
|
||||||
|
"INPUT: {:#x}, OUTPUT: {:#x}, EXPECTED: {:#x}, ERROR: {}",
|
||||||
|
input.to_bits(),
|
||||||
|
output.to_bits(),
|
||||||
|
expected.to_bits(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"INPUT: {:#x}, OUTPUT: PANIC!, EXPECTED: {:#x}",
|
||||||
|
input.to_bits(),
|
||||||
|
expected.to_bits()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! f32f32 {
|
||||||
|
($lib:expr, $($fun:ident),+) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $fun() {
|
||||||
|
let expected = include_bytes!(concat!("../bin/output/", $lib, ".", stringify!($fun)))
|
||||||
|
.exact_chunks(4)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for ((i0, i1), expected) in $crate::F32F32.iter().zip(&expected) {
|
||||||
|
if let Ok(output) = panic::catch_unwind(|| libm::$fun(*i0, *i1)) {
|
||||||
|
if let Err(error) = libm::_eqf(output, *expected) {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: {:#x}, EXPECTED: {:#x}, ERROR: {}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1.to_bits(),
|
||||||
|
output.to_bits(),
|
||||||
|
expected.to_bits(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: PANIC!, EXPECTED: {:#x}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1.to_bits(),
|
||||||
|
expected.to_bits()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! f32f32f32 {
|
||||||
|
($lib:expr, $($fun:ident),+) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $fun() {
|
||||||
|
let expected = include_bytes!(concat!("../bin/output/", $lib, ".", stringify!($fun)))
|
||||||
|
.exact_chunks(4)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for ((i0, i1, i2), expected) in $crate::F32F32F32.iter().zip(&expected) {
|
||||||
|
if let Ok(output) = panic::catch_unwind(|| libm::$fun(*i0, *i1, *i2)) {
|
||||||
|
if let Err(error) = libm::_eqf(output, *expected) {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}, {:#x}), OUTPUT: {:#x}, EXPECTED: {:#x}, ERROR: {}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1.to_bits(),
|
||||||
|
i2.to_bits(),
|
||||||
|
output.to_bits(),
|
||||||
|
expected.to_bits(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: PANIC!, EXPECTED: {:#x}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1.to_bits(),
|
||||||
|
expected.to_bits()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! f32i32 {
|
||||||
|
($lib:expr, $($fun:ident),+) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $fun() {
|
||||||
|
let expected = include_bytes!(concat!("../bin/output/", $lib, ".", stringify!($fun)))
|
||||||
|
.exact_chunks(4)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f32::from_bits(u32::from_le(u32::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for ((i0, i1), expected) in $crate::F32I32.iter().zip(&expected) {
|
||||||
|
if let Ok(output) = panic::catch_unwind(|| libm::$fun(*i0, *i1)) {
|
||||||
|
if let Err(error) = libm::_eqf(output, *expected) {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: {:#x}, EXPECTED: {:#x}, ERROR: {}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1,
|
||||||
|
output.to_bits(),
|
||||||
|
expected.to_bits(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: PANIC!, EXPECTED: {:#x}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1,
|
||||||
|
expected.to_bits()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! f64 {
|
||||||
|
($lib:expr, $($fun:ident),+) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $fun() {
|
||||||
|
let expected = include_bytes!(concat!("../bin/output/", $lib, ".", stringify!($fun)))
|
||||||
|
.exact_chunks(8)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 8];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (input, expected) in shared::F64.iter().zip(&expected) {
|
||||||
|
if let Ok(output) = panic::catch_unwind(|| libm::$fun(*input)) {
|
||||||
|
if let Err(error) = libm::_eq(output, *expected) {
|
||||||
|
panic!(
|
||||||
|
"INPUT: {:#x}, OUTPUT: {:#x}, EXPECTED: {:#x}, ERROR: {}",
|
||||||
|
input.to_bits(),
|
||||||
|
output.to_bits(),
|
||||||
|
expected.to_bits(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"INPUT: {:#x}, OUTPUT: PANIC!, EXPECTED: {:#x}",
|
||||||
|
input.to_bits(),
|
||||||
|
expected.to_bits()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! f64f64 {
|
||||||
|
($lib:expr, $($fun:ident),+) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $fun() {
|
||||||
|
let expected = include_bytes!(concat!("../bin/output/", $lib, ".", stringify!($fun)))
|
||||||
|
.exact_chunks(8)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 8];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for ((i0, i1), expected) in shared::F64F64.iter().zip(&expected) {
|
||||||
|
if let Ok(output) = panic::catch_unwind(|| libm::$fun(*i0, *i1)) {
|
||||||
|
if let Err(error) = libm::_eq(output, *expected) {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: {:#x}, EXPECTED: {:#x}, ERROR: {}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1.to_bits(),
|
||||||
|
output.to_bits(),
|
||||||
|
expected.to_bits(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: PANIC!, EXPECTED: {:#x}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1.to_bits(),
|
||||||
|
expected.to_bits()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! f64f64f64 {
|
||||||
|
($lib:expr, $($fun:ident),+) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $fun() {
|
||||||
|
let expected = include_bytes!(concat!("../bin/output/", $lib, ".", stringify!($fun)))
|
||||||
|
.exact_chunks(8)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 8];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for ((i0, i1, i2), expected) in shared::F64F64F64.iter().zip(&expected) {
|
||||||
|
if let Ok(output) = panic::catch_unwind(|| libm::$fun(*i0, *i1, *i2)) {
|
||||||
|
if let Err(error) = libm::_eq(output, *expected) {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}, {:#x}), OUTPUT: {:#x}, EXPECTED: {:#x}, ERROR: {}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1.to_bits(),
|
||||||
|
i2.to_bits(),
|
||||||
|
output.to_bits(),
|
||||||
|
expected.to_bits(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: PANIC!, EXPECTED: {:#x}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1.to_bits(),
|
||||||
|
expected.to_bits()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! f64i32 {
|
||||||
|
($lib:expr, $($fun:ident),+) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $fun() {
|
||||||
|
let expected = include_bytes!(concat!("../bin/output/", $lib, ".", stringify!($fun)))
|
||||||
|
.exact_chunks(8)
|
||||||
|
.map(|chunk| {
|
||||||
|
let mut buf = [0; 8];
|
||||||
|
buf.copy_from_slice(chunk);
|
||||||
|
f64::from_bits(u64::from_le(u64::from_bytes(buf)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for ((i0, i1), expected) in shared::F64I32.iter().zip(&expected) {
|
||||||
|
if let Ok(output) = panic::catch_unwind(|| libm::$fun(*i0, *i1)) {
|
||||||
|
if let Err(error) = libm::_eq(output, *expected) {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: {:#x}, EXPECTED: {:#x}, ERROR: {}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1,
|
||||||
|
output.to_bits(),
|
||||||
|
expected.to_bits(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"INPUT: ({:#x}, {:#x}), OUTPUT: PANIC!, EXPECTED: {:#x}",
|
||||||
|
i0.to_bits(),
|
||||||
|
i1,
|
||||||
|
expected.to_bits()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-4
@@ -21,14 +21,34 @@ pub use math::*;
|
|||||||
/// Approximate equality with 1 ULP of tolerance
|
/// Approximate equality with 1 ULP of tolerance
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn _eqf(a: u32, b: u32) -> bool {
|
pub fn _eqf(a: f32, b: f32) -> Result<(), u32> {
|
||||||
(a as i32).wrapping_sub(b as i32).abs() <= 1
|
if a.is_nan() && b.is_nan() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let err = (a.to_bits() as i32).wrapping_sub(b.to_bits() as i32).abs();
|
||||||
|
|
||||||
|
if err <= 1 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn _eq(a: u64, b: u64) -> bool {
|
pub fn _eq(a: f64, b: f64) -> Result<(), u64> {
|
||||||
(a as i64).wrapping_sub(b as i64).abs() <= 1
|
if a.is_nan() && b.is_nan() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let err = (a.to_bits() as i64).wrapping_sub(b.to_bits() as i64).abs();
|
||||||
|
|
||||||
|
if err <= 1 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Math support for `f32`
|
/// Math support for `f32`
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
# `test-generator`
|
|
||||||
|
|
||||||
This is a tool to generate test cases for the `libm` crate.
|
|
||||||
|
|
||||||
The generator randomly creates inputs for each math function, then proceeds to compute the
|
|
||||||
expected output for the given function by running the MUSL *C implementation* of the function and
|
|
||||||
finally it packs the test cases as a Cargo test file. For this reason, this generator **must**
|
|
||||||
always be compiled for the `x86_64-unknown-linux-musl` target.
|
|
||||||
@@ -1,788 +0,0 @@
|
|||||||
// 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,
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user