Skip to content

Instantly share code, notes, and snippets.

@ActuallyaDeviloper
Last active September 28, 2017 23:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ActuallyaDeviloper/2a1b84554b0d9e9ff8f14fa5c1b0bcce to your computer and use it in GitHub Desktop.
Save ActuallyaDeviloper/2a1b84554b0d9e9ff8f14fa5c1b0bcce to your computer and use it in GitHub Desktop.
Branchless f32 to integer tests
#![feature(test)]
#![feature(inclusive_range_syntax)]
extern crate test;
extern crate rand;
use test::*;
fn u16_cast(x: f32) -> u16 {
x as u16
}
fn u16_cast_clip(x: f32) -> u16 {
if u16::min_value() as f32 <= x && x < u16::max_value() as f32 {
x as u16
} else if x.is_nan() {
0
} else if x < 0.0 {
u16::min_value()
} else {
u16::max_value()
}
}
fn u16_cast_clip2(x: f32) -> u16 {
let min = u16::min_value() as f32;
let max = u16::max_value() as f32;
let y = if min < x { x } else { min }; // We are good to go with NaN, it's already handled with this.
let y = if max < y { max } else { y };
y as u16
}
fn u16_array_cast(arr: [f32; 128]) -> [u16; 128] {
let mut out: [u16; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = u16_cast(arr[i]);
}
out
}
fn u16_array_cast_clip(arr: [f32; 128]) -> [u16; 128] {
let mut out: [u16; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = u16_cast_clip(arr[i]);
}
out
}
fn u16_array_cast_clip2(arr: [f32; 128]) -> [u16; 128] {
let mut out: [u16; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = u16_cast_clip2(arr[i]);
}
out
}
#[bench]
fn u16_bench_cast(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(u16_cast(black_box(x)));
});
}
#[bench]
fn u16_bench_cast_clip(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(u16_cast_clip(black_box(x)));
});
}
#[bench]
fn u16_bench_cast_clip2(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(u16_cast_clip2(black_box(x)));
});
}
#[bench]
fn u16_bench_rng_cast(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(u16_cast(rng.next_f32() * 100000.0f32 - 50000.0f32));
});
}
#[bench]
fn u16_bench_rng_cast_clip(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(u16_cast_clip(rng.next_f32() * 100000.0f32 - 50000.0f32));
});
}
#[bench]
fn u16_bench_rng_cast_clip2(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(u16_cast_clip2(rng.next_f32() * 100000.0f32 - 50000.0f32));
});
}
#[bench]
fn u16_bench_array_cast(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| u16_array_cast(black_box(x)));
}
#[bench]
fn u16_bench_array_cast_clip(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| u16_array_cast_clip(black_box(x)));
}
#[bench]
fn u16_bench_array_cast_clip2(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| u16_array_cast_clip2(black_box(x)));
}
#[test]
fn u16_clip_test() {
for i in 0..=u32::max_value() {
let flt = unsafe { std::mem::transmute::<_, f32>(i) };
let result = u16_cast_clip(flt);
let result2 = u16_cast_clip2(flt);
if (result != result2) {
panic!("Error at {}", flt);
}
}
}
fn i16_cast(x: f32) -> i16 {
x as i16
}
fn i16_cast_clip(x: f32) -> i16 {
if i16::min_value() as f32 <= x && x < i16::max_value() as f32 {
x as i16
} else if x.is_nan() {
0
} else if x < 0.0 {
i16::min_value()
} else {
i16::max_value()
}
}
fn i16_cast_clip2(x: f32) -> i16 {
let min = i16::min_value() as f32;
let max = i16::max_value() as f32;
let y = if min > x { min } else { x };
let y = if max < y { max } else { y };
if x.is_nan() { 0 } else { y as i16 } // Special case for returning 0 in case of NaN.
}
fn i16_array_cast(arr: [f32; 128]) -> [i16; 128] {
let mut out: [i16; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = i16_cast(arr[i]);
}
out
}
fn i16_array_cast_clip(arr: [f32; 128]) -> [i16; 128] {
let mut out: [i16; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = i16_cast_clip(arr[i]);
}
out
}
fn i16_array_cast_clip2(arr: [f32; 128]) -> [i16; 128] {
let mut out: [i16; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = i16_cast_clip2(arr[i]);
}
out
}
#[bench]
fn i16_bench_cast(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(i16_cast(black_box(x)));
});
}
#[bench]
fn i16_bench_cast_clip(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(i16_cast_clip(black_box(x)));
});
}
#[bench]
fn i16_bench_cast_clip2(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(i16_cast_clip2(black_box(x)));
});
}
#[bench]
fn i16_bench_rng_cast(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(i16_cast(rng.next_f32() * 100000.0f32 - 50000.0f32));
});
}
#[bench]
fn i16_bench_rng_cast_clip(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(i16_cast_clip(rng.next_f32() * 100000.0f32 - 50000.0f32));
});
}
#[bench]
fn i16_bench_rng_cast_clip2(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(i16_cast_clip2(rng.next_f32() * 100000.0f32 - 50000.0f32));
});
}
#[bench]
fn i16_bench_array_cast(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| i16_array_cast(black_box(x)));
}
#[bench]
fn i16_bench_array_cast_clip(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| i16_array_cast_clip(black_box(x)));
}
#[bench]
fn i16_bench_array_cast_clip2(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| i16_array_cast_clip2(black_box(x)));
}
#[test]
fn i16_clip_test() {
for i in 0..=u32::max_value() {
let flt = unsafe { std::mem::transmute::<_, f32>(i) };
let result = i16_cast_clip(flt);
let result2 = i16_cast_clip2(flt);
if (result != result2) {
panic!("Error at {}", flt);
}
}
}
fn i32_cast(x: f32) -> i32 {
x as i32
}
fn i32_cast_clip(x: f32) -> i32 {
if i32::min_value() as f32 <= x && x < i32::max_value() as f32 {
x as i32
} else if x.is_nan() {
0
} else if x < 0.0 {
i32::min_value()
} else {
i32::max_value()
}
}
fn i32_cast_clip2(x: f32) -> i32 {
// i32::max_value() rounds upwards to an out of range value.
// By doing an i32 decrement, it's value is reduced so that it fits inside the range of i32 again.
let min = i32::min_value() as f32;
let max = unsafe { std::mem::transmute::<_, f32>(
std::mem::transmute::<_, i32>(i32::max_value() as f32) - 1) };
let y = if min > x { min } else { x };
let y = if max < y { max } else { y };
let z = if x.is_nan() { 0 } else { y as i32 }; // Special case for returning 0 in case of NaN.
// f32 can't represent i32::max_value().
// So to get a proper clamp that returns i32::max_value(), this fixup step is needed
if max < x { i32::max_value() } else { z }
}
fn i32_array_cast(arr: [f32; 128]) -> [i32; 128] {
let mut out: [i32; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = i32_cast(arr[i]);
}
out
}
fn i32_array_cast_clip(arr: [f32; 128]) -> [i32; 128] {
let mut out: [i32; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = i32_cast_clip(arr[i]);
}
out
}
fn i32_array_cast_clip2(arr: [f32; 128]) -> [i32; 128] {
let mut out: [i32; 128] = unsafe { std::mem::uninitialized() };
for i in 0..128 {
out[i] = i32_cast_clip2(arr[i]);
}
out
}
#[bench]
fn i32_bench_cast(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(i32_cast(black_box(x)));
});
}
#[bench]
fn i32_bench_cast_clip(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(i32_cast_clip(black_box(x)));
});
}
#[bench]
fn i32_bench_cast_clip2(b: &mut Bencher) {
let x = 0f32;
b.iter(|| for _ in 0..128 {
black_box(i32_cast_clip2(black_box(x)));
});
}
#[bench]
fn i32_bench_rng_cast(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(i32_cast(rng.next_f32() * 10000000000.0f32 - 5000000000.0f32));
});
}
#[bench]
fn i32_bench_rng_cast_clip(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(i32_cast_clip(rng.next_f32() * 10000000000.0f32 - 5000000000.0f32));
});
}
#[bench]
fn i32_bench_rng_cast_clip2(b: &mut Bencher) {
use rand::Rng;
let mut rng = rand::thread_rng();
b.iter(|| for _ in 0..128 {
black_box(i32_cast_clip2(rng.next_f32() * 10000000000.0f32 - 5000000000.0f32));
});
}
#[bench]
fn i32_bench_array_cast(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| i32_array_cast(black_box(x)));
}
#[bench]
fn i32_bench_array_cast_clip(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| i32_array_cast_clip(black_box(x)));
}
#[bench]
fn i32_bench_array_cast_clip2(b: &mut Bencher) {
let x = [0f32; 128];
b.iter(|| i32_array_cast_clip2(black_box(x)));
}
#[test]
fn i32_clip_test() {
for i in 0..=u32::max_value() {
let flt = unsafe { std::mem::transmute::<_, f32>(i) };
let result = i32_cast_clip(flt);
let result2 = i32_cast_clip2(flt);
if (result != result2) {
panic!("Error at {}", flt);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment