-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ER] isqrt() function for integral values #89273
Comments
@rustbot label: +Libs-small. |
Error: Label Libs-small can only be set by Rust team members Please let |
1 question I have is what should be the return value of I think a working implementation would be a good way to push this further. However, I am just some random contributor and a team member (particularly a library team member) may have a better idea of what to do. |
In Rust we prefer to return Option (or sometimes Result) for partial functions like sqrt (I don't agree with the design of the recently added integral log of the stdlib: "When the number is negative, zero, or if the base is not at least 2; it panics in debug mode and the return value is 0 in release mode."). A funny API for this function could be: const fn u8::isqrt() -> u8; // A nibble.
const fn u16::isqrt() -> u8;
const fn u32::isqrt() -> u16;
const fn u64::isqrt() -> u32;
const fn u128::isqrt() -> u64;
const fn i8::isqrt() -> Option<u8>; // A nibble.
const fn i16::isqrt() -> Option<u8>;
const fn i32::isqrt() -> Option<u16>;
const fn i64::isqrt() -> Option<u32>;
const fn i128::isqrt() -> Option<u64>; (Unfortunately Rust type system doesn't have ranged integral values yet, so the function for u8 needs to return another u8). But having Option only for the signed ones seems iffy. So an alternative is to use Result<Ty, !> for the unsigned types. |
Regarding returning smaller integers than That said, I'm not sure whether I like returning the smallest possible integer type. For one, as you said,
I don't think the signed and unsigned API really needs to be symmetric here. I would much prefer |
If u32::isqrt returns a u16, and you need a u32 again, you can write u32::from(x.isqrt()) without hard casts and without unwraps. While to go from a u32 result to a u16 you need some kind of hard cast (or try_from). This is one reason why returning types smaller than the input type is nicer. For usize::isqrt I think it's OK to return another usize. |
I'm not sure if this has been discussed before but I think the name should be |
I named those isqrt because 5.isqrt() doesn't return the square root, it returns the integer truncation of the square root. In Python std lib they have chosen the "isqrt" name. But if for Rust stdlib there's consensus to avoid the leading "i", then it's OK. |
"Integer square root" (abbreviated as isqrt) is an established term in math with a precise definition: https://en.wikipedia.org/wiki/Integer_square_root However, I'm also fine with just |
I think it's OK for u8::isqrt to return an u8 plus an assume(result <= 16): const fn u8::isqrt() -> u8 {
// ...
unsafe { assume(result <= 16);
result
} Once Rust gains proper well implemented ergonomic ranged integers, that assume() should be removed. |
Once we have That said, |
For ranged integers now I've settled to like a syntax like u16[0 ..= N] similar to slicing. It's natural for a Rust programmer and it's compact. Regarding the type round-trip, I'm content with using a safe looking "from":
Rust doesn't have a typeof, otherwise you could write:
There are situations where you want to use the smaller type returned by the isqrt. Using a smaller type avoids you some casts. |
Add "integer square root" method to integer primitive types For every suffix `N` among `8`, `16`, `32`, `64`, `128` and `size`, this PR adds the methods ```rust const fn uN::isqrt() -> uN; const fn iN::isqrt() -> iN; const fn iN::checked_isqrt() -> Option<iN>; ``` to compute the [integer square root](https://en.wikipedia.org/wiki/Integer_square_root), addressing issue rust-lang#89273. The implementation is based on the [base 2 digit-by-digit algorithm](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)) on Wikipedia, which after some benchmarking has proved to be faster than both binary search and Heron's/Newton's method. I haven't had the time to understand and port [this code](http://atoms.alife.co.uk/sqrt/SquareRoot.java) based on lookup tables instead, but I'm not sure whether it's worth complicating such a function this much for relatively little benefit.
Add "integer square root" method to integer primitive types For every suffix `N` among `8`, `16`, `32`, `64`, `128` and `size`, this PR adds the methods ```rust const fn uN::isqrt() -> uN; const fn iN::isqrt() -> iN; const fn iN::checked_isqrt() -> Option<iN>; ``` to compute the [integer square root](https://en.wikipedia.org/wiki/Integer_square_root), addressing issue rust-lang#89273. The implementation is based on the [base 2 digit-by-digit algorithm](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)) on Wikipedia, which after some benchmarking has proved to be faster than both binary search and Heron's/Newton's method. I haven't had the time to understand and port [this code](http://atoms.alife.co.uk/sqrt/SquareRoot.java) based on lookup tables instead, but I'm not sure whether it's worth complicating such a function this much for relatively little benefit.
I think it's more correct, according to the return type. They mean:
Thus, if we name it |
As in Python 3:
https://docs.python.org/3/library/math.html#math.isqrt
To the Rust stdlib it could be added a isqrt() function for all u*/i*/usize/isize, that returns the floor of the square root of the nonnegative integral value n, that is the greatest integer a such that a*a <= n.
This in Python shows that the usual trick to pass through f64 isn't precise enough for u128:
The text was updated successfully, but these errors were encountered: