Skip to content

Instantly share code, notes, and snippets.

@m1el
Last active April 20, 2021 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save m1el/873de570c1af6131f707a850108c6ced to your computer and use it in GitHub Desktop.
Save m1el/873de570c1af6131f707a850108c6ced to your computer and use it in GitHub Desktop.
[package]
name = "round"
version = "0.1.0"
authors = ["Igor null <m1el.2027@gmail.com>"]
[dependencies]
libm = "0.1.2"
[[bin]]
name = "round-bench"
path = "round.rs"
std::f32::round: 47.4554037s
round via builtin trunc: 39.655634s
round reimplementation: 11.1431439s
round reimplementation unwrapped: 6.3648848s
libm::roundf: 8.363816s
std::f32::trunc: 37.1020037s
trunc reimplementation: 7.6747364s
libm::truncf: 10.562295s
/*
The MIT License
Copyright (c) 2015 Igor Null <m1el.2027@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
extern crate libm;
const F32_EXP_BIAS: u32 = 0x7f;
const F32_EXP_MASK: u32 = 0xff << 23;
const F32_SIGN_MASK: u32 = 1<<31;
const F32_MANTISSA_BITS: u32 = 23;
fn copysign_f32(x: f32, y: f32) -> f32 {
let iy = y.to_bits();
let ix = (x.to_bits() & !(1<<31)) | (iy & (1<<31));
f32::from_bits(ix)
}
fn trunc_f32(x: f32) -> f32 {
let mut ix = x.to_bits();
let exp = (ix & F32_EXP_MASK) >> F32_MANTISSA_BITS;
if exp >= F32_EXP_BIAS + F32_MANTISSA_BITS {
return x;
}
if exp < F32_EXP_BIAS {
return x * 0.0;
}
let shift = F32_EXP_BIAS + F32_MANTISSA_BITS - exp;
ix &= (!0) << shift;
f32::from_bits(ix)
}
fn round_f32(x: f32) -> f32 {
const ROUND_STEP_F32: f32 = 0.5 - 0.25 * std::f32::EPSILON;
if x.is_nan() { return x; }
trunc_f32(x + copysign_f32(ROUND_STEP_F32, x))
}
fn round_f32_unwrapped(x: f32) -> f32 {
const F32_ROUND_STEP: f32 = 0.5 - 0.25 * std::f32::EPSILON;
let mut ix = x.to_bits();
let mut exp = (ix & F32_EXP_MASK) >> F32_MANTISSA_BITS;
if exp >= F32_EXP_BIAS + F32_MANTISSA_BITS {
return x;
}
let step = f32::from_bits(F32_ROUND_STEP.to_bits() | (ix & F32_SIGN_MASK));
ix = (x + step).to_bits();
exp = (ix & F32_EXP_MASK) >> F32_MANTISSA_BITS;
if exp < F32_EXP_BIAS {
return x * 0.0;
}
let shift = F32_EXP_BIAS + F32_MANTISSA_BITS - exp;
ix &= (!0) << shift;
f32::from_bits(ix)
}
fn round_f32_std_trunc(x: f32) -> f32 {
const ROUND_STEP_F32: f32 = 0.5 - 0.25 * std::f32::EPSILON;
if x.is_nan() { return x; }
(x+copysign_f32(ROUND_STEP_F32, x)).trunc()
}
fn main() {
/*
let mut failed = 0;
for i in 0..=u32::max_value() {
if i & 0xffffff == 0 { println!("{}", i); }
let x = f32::from_bits(i);
//if x.is_nan() { continue }
let r_std = x.round();
let r_hoc = round_f32_unwrapped(x);
if r_std.to_bits() != r_hoc.to_bits()
{
println!("round for {} {} fails", x, i);
println!("{}, {}", r_std, r_hoc);
println!("{}, {}", r_std.to_bits(), r_hoc.to_bits());
failed += 1;
if failed > 10 { break }
}
}
*/
use std::time::Instant;
//let mut failed = 0usize;
let start = Instant::now();
let mut sum = 0.0;
for i in 0..=u32::max_value() {
let x = f32::from_bits(i);
sum += x.round();
}
println!("std::f32::round: {:?} {}", start.elapsed(), sum);
let start = Instant::now();
let mut sum = 0.0;
for i in 0..=u32::max_value() {
let x = f32::from_bits(i);
sum += round_f32_std_trunc(x);
}
println!("round via builtin trunc: {:?} {}", start.elapsed(), sum);
let start = Instant::now();
let mut sum = 0.0;
for i in 0..=u32::max_value() {
let x = f32::from_bits(i);
sum += round_f32(x);
}
println!("round reimplementation: {:?} {}", start.elapsed(), sum);
let start = Instant::now();
let mut sum = 0.0;
for i in 0..=u32::max_value() {
let x = f32::from_bits(i);
sum += round_f32_unwrapped(x);
}
println!("round reimplementation unwrapped: {:?} {}", start.elapsed(), sum);
let start = Instant::now();
let mut sum = 0.0;
for i in 0..=u32::max_value() {
let x = f32::from_bits(i);
sum += libm::roundf(x);
}
println!("libm::roundf: {:?} {}", start.elapsed(), sum);
let start = Instant::now();
let mut sum = 0.0;
for i in 0..=u32::max_value() {
let x = f32::from_bits(i);
sum += x.trunc();
}
println!("std::f32::trunc: {:?} {}", start.elapsed(), sum);
let start = Instant::now();
let mut sum = 0.0;
for i in 0..=u32::max_value() {
let x = f32::from_bits(i);
sum += trunc_f32(x);
}
println!("trunc reimplementation: {:?} {}", start.elapsed(), sum);
let start = Instant::now();
let mut sum = 0.0;
for i in 0..=u32::max_value() {
let x = f32::from_bits(i);
sum += libm::truncf(x);
}
println!("libm::truncf: {:?} {}", start.elapsed(), sum);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment