I'm looking for a fast and maybe idiomatic way to split a floating point number into an integer and a fractional part. The integer part shall always be less or equal to the floating point number and the fractional part shall be within 0.0..1.0
(1.0
excluded).
This is typically needed for interpolating values of an array.
My ideas so far:
(Rust Playground)
use std::time::Instant;
#[inline(never)]
fn split_a(x: f64) -> (i64, f64) {
let int = x.floor() as i64;
(int, x - int as f64)
}
#[inline(never)]
fn split_b(x: f64) -> (i64, f64) {
let int = x.floor();
(int as i64, x - int)
}
#[inline(never)]
fn split_c(x: f64) -> (i64, f64) {
(x.floor() as i64, x.rem_euclid(1.0))
}
#[inline(never)]
fn split_d(x: f64) -> (i64, f64) {
let int = x as i64;
(int, x - int as f64)
}
#[inline(never)]
fn split_e(x: f64) -> (i64, f64) {
(x.trunc() as i64, x.fract())
}
#[inline(never)]
fn split_f(x: f64) -> (f64, f64) {
let int = x.floor();
(int, x - int)
}
#[inline(never)]
fn split_g(x: f64) -> (f64, f64) {
(x.trunc(), x.fract())
}
fn main() {
// trick the compiler into not pre-computing the results
let zero = Instant::now().elapsed().as_secs() as f64;
let test_values = [-1.0 + zero, -0.75 + zero, -0.5 + zero, -0.25 + zero, 0.0 + zero, 0.25 + zero, 0.5 + zero, 0.75 + zero, 1.0 + zero];
println!("split_a: ok");
for v in &test_values { print!("{:?} ", split_a(*v)); } println!();
println!("split_b: ok");
for v in &test_values { print!("{:?} ", split_b(*v)); } println!();
println!("split_c: ok but -0.0 as fract for negative input");
for v in &test_values { print!("{:?} ", split_c(*v)); } println!();
println!("split_d: ok for non-negative");
for v in &test_values { print!("{:?} ", split_d(*v)); } println!();
println!("split_e: ok for non-negative");
for v in &test_values { print!("{:?} ", split_e(*v)); } println!();
println!("split_f: ok (integer as f64)");
for v in &test_values { print!("{:?} ", split_f(*v)); } println!();
println!("split_g: ok for non-negative (integer as f64)");
for v in &test_values { print!("{:?} ", split_g(*v)); } println!();
}
output:
split_a: ok
(-1, 0.0) (-1, 0.25) (-1, 0.5) (-1, 0.75) (0, 0.0) (0, 0.25) (0, 0.5) (0, 0.75) (1, 0.0)
split_b: ok
(-1, 0.0) (-1, 0.25) (-1, 0.5) (-1, 0.75) (0, 0.0) (0, 0.25) (0, 0.5) (0, 0.75) (1, 0.0)
split_c: ok but -0.0 as fract for negative input
(-1, -0.0) (-1, 0.25) (-1, 0.5) (-1, 0.75) (0, 0.0) (0, 0.25) (0, 0.5) (0, 0.75) (1, 0.0)
split_d: ok for non-negative
(-1, 0.0) (0, -0.75) (0, -0.5) (0, -0.25) (0, 0.0) (0, 0.25) (0, 0.5) (0, 0.75) (1, 0.0)
split_e: ok for non-negative
(-1, 0.0) (0, -0.75) (0, -0.5) (0, -0.25) (0, 0.0) (0, 0.25) (0, 0.5) (0, 0.75) (1, 0.0)
split_f: ok (integer as f64)
(-1.0, 0.0) (-1.0, 0.25) (-1.0, 0.5) (-1.0, 0.75) (0.0, 0.0) (0.0, 0.25) (0.0, 0.5) (0.0, 0.75) (1.0, 0.0)
split_g: ok for non-negative (integer as f64)
(-1.0, 0.0) (-0.0, -0.75) (-0.0, -0.5) (-0.0, -0.25) (0.0, 0.0) (0.0, 0.25) (0.0, 0.5) (0.0, 0.75) (1.0, 0.0)
So far I like split_b
most. Any other ideas?