Description
This is experimental code, it could be compiled with one or the other implementation of the integer square root:
#![feature(cfg_overflow_checks, stmt_expr_attributes)]
#![allow(unused_macros)]
#[cfg(overflow_checks)]
macro_rules! fto { ($e:expr, $t:ident) => (($e) as $t) }
#[cfg(not(overflow_checks))]
macro_rules! fto { ($e:expr, $t:ident) => (#[allow(unused_unsafe)] unsafe { $e.to_int_unchecked::<$t>() }) }
macro_rules! isqrt { ($e:expr, $t:ident) => ( fto!{(($e) as f64).sqrt(), $t} ) }
fn main() {
let mut tot: u64 = 0;
for x in (1_u64 .. 1_000_000_000).step_by(3) {
tot += isqrt!(x, u64); // Version A. 1.04 seconds.
//tot += x.isqrt(); // Version B. 4.94 seconds.
}
println!("{tot}");
}
As you see the floating-point based isqrt implementation is almost five times faster on my PC (using rustc 1.87.0-nightly). The std lib isqrt has upside of being const, so I can use it for const generics calculations and similar situations. And when I need to compute only one or few isqrt, this performance difference isn't important, and I use the std one. But when I need to compute a lot of isqrt, I consider faster alternatives.
While I don't propose to replace the std library isqrt with the code I've shown here, I suggest std lib implementers to express an opinion regarding the usage of floating point sqrt+ int cast in some vetted and safe cases.