I'm afraid I don't know Rust, but I wrote the following for Julia (based on a similar sequence for ties away from zero by Arch Robinson), which you should be able to adapt:
y = floor(x)
ifelse(x==y, y, copysign(floor(2*x-y),x))
A quick explanation of what is going on:
floor
finds the nearest integer less than or equal to x
.
- If
y==x
, then x
is an integer, so no rounding is necessary: note that this captures all cases where the absolute value of x
is greater than 253.
floor(2*x-y)
will give the desired answer: 2*x
is exact, and we don't have to worry about overflow due to step 2. The subtraction will be exact for all cases except -0.25 < x < 0
, which will clearly give the right answer anyway.
- The
copysign
is there just to ensure the zero has the correct sign. If you're not bothered by such things, you could leave it off.
Steps 2 & 3 could be replaced by a simple branch:
x-y < 0.5 ? y : y+1.0
but if I recall correctly, avoiding the branch here made the code more vectorisation friendly (which is also the reason for using ifelse
instead of an if
block).