round() vs. rint()/nearbyint() with fast-math

Hello,

Sometime over the last few months, I implemented in the PowerPC backend a fast-math-only optimization which lowers ISD::FRINT/FNEARBYINT in terms of the frin instruction (when supported). As one of my users has pointed out to me, frin actually implements the semantics of round() [it ties away from zero] instead of implementing nearbyint() [which ties to even]. This user has additionally pointed out that LLVM on x86 does not lower round() in the same way that it lowers rint() in fast-math mode.

LLVM does not currently have special lowering handling for round(), and I'll propose a patch to add that, but the larger question is this: should fast-math change the tie-breaking behavior of rint/nearbyint/round, etc. and, if so, should we make a specific effort to have all backends provide the same guarantee (or lack of a guarantee) in this regard?

Thanks again,
Hal

LLVM does not currently have special lowering handling for round(), and

I'll propose a patch to add that, but the larger question is this: should
fast-math change the tie-breaking behavior of

rint/nearbyint/round, etc. and, if so, should we make a specific effort to

have all backends provide the same guarantee (or lack of a guarantee) in
this regard?

I don't know, primarily because I've never really been involved in anything
where I've cared about using exotic rounding modes. But in general I'm of
the opinion that -fast-math is the "nuclear option" that's allowed to do
lots of things which may well invoke backend specific behaviour. (That's
also why I think that most FP transformations shouldn't be "only" guarded by
fast-math but a more precise option.)

Cheers,
Dave

The functions rint and round and standard libm functions commonly used to
round floating point values to integers. Both round to the nearest integer,
but break ties differently -- rint uses IEEE tie breaking (towards even),
round uses mathematical tie breaking (away from zero).

The question here is: Is this optimization worthwhile, or would it surprise
too many people? Depending on this, it should either be disallowed, or
possibly implemented for other back-ends as well.

-erik

After some consideration, I have come to the conclusion that this
optimization (changing rint to round) is not worthwhile. There are some
floating point operations that can provide an exact result, and not
obtaining this exact result is surprising. For example, I would expect that
adding/multiplying two small integers gives the exact result, or that
fmin/fmax give the correct result if no nans are involved, or that
comparisons yield the correct answer (again in the absence of nans,
denormalized numbers etc.).

The case here -- rint(0.5) -- involves an input that can be represented
exactly, and an output that can be represented exactly (0.0). Neither nans,
infinities, nor denormalized numbers are involved. In this case I do expect
the correct answer, even with full floating point operations that ignore
nans, infinities, denormalized numbers, or that re-associate etc.

-erik

PS:

I think that

rint(x) = x + copysign(M,x) - copysign(M,x)

where M is a magic number, and where the addition and subtraction cannot be
optimized. I believe M=2^52. This should work fine at least for "reasonably
small" numbers.

> LLVM does not currently have special lowering handling for round(),
> and
I'll propose a patch to add that, but the larger question is this:
should
fast-math change the tie-breaking behavior of
> rint/nearbyint/round, etc. and, if so, should we make a specific
> effort to
have all backends provide the same guarantee (or lack of a guarantee)
in
this regard?

I don't know, primarily because I've never really been involved in
anything
where I've cared about using exotic rounding modes. But in general
I'm of
the opinion that -fast-math is the "nuclear option" that's allowed to
do
lots of things which may well invoke backend specific behaviour.
(That's
also why I think that most FP transformations shouldn't be "only"
guarded by
fast-math but a more precise option.)

The functions rint and round and standard libm functions commonly
used to round floating point values to integers. Both round to the
nearest integer, but break ties differently -- rint uses IEEE tie
breaking (towards even), round uses mathematical tie breaking (away
from zero).

The question here is: Is this optimization worthwhile, or would it
surprise too many people? Depending on this, it should either be
disallowed, or possibly implemented for other back-ends as well.

After some consideration, I have come to the conclusion that this
optimization (changing rint to round) is not worthwhile. There are
some floating point operations that can provide an exact result, and
not obtaining this exact result is surprising. For example, I would
expect that adding/multiplying two small integers gives the exact
result, or that fmin/fmax give the correct result if no nans are
involved, or that comparisons yield the correct answer (again in the
absence of nans, denormalized numbers etc.).

The case here -- rint(0.5) -- involves an input that can be
represented exactly, and an output that can be represented exactly
(0.0). Neither nans, infinities, nor denormalized numbers are
involved. In this case I do expect the correct answer, even with
full floating point operations that ignore nans, infinities,
denormalized numbers, or that re-associate etc.

I've been thinking about this for some time as well, and I've come to the same conclusion. I'll be updating the PPC backend accordingly in the near future. frin should really map to round() and not rint(), and we should leave it at that.

Thanks again,
Hal

>
>
>
>
>
>
>
>
>
>
>
>
>
> > LLVM does not currently have special lowering handling for
> > round(),
> > and
> I'll propose a patch to add that, but the larger question is this:
> should
> fast-math change the tie-breaking behavior of
> > rint/nearbyint/round, etc. and, if so, should we make a specific
> > effort to
> have all backends provide the same guarantee (or lack of a
> guarantee)
> in
> this regard?
>
> I don't know, primarily because I've never really been involved in
> anything
> where I've cared about using exotic rounding modes. But in general
> I'm of
> the opinion that -fast-math is the "nuclear option" that's allowed
> to
> do
> lots of things which may well invoke backend specific behaviour.
> (That's
> also why I think that most FP transformations shouldn't be "only"
> guarded by
> fast-math but a more precise option.)
>
>
> The functions rint and round and standard libm functions commonly
> used to round floating point values to integers. Both round to the
> nearest integer, but break ties differently -- rint uses IEEE tie
> breaking (towards even), round uses mathematical tie breaking (away
> from zero).
>
>
> The question here is: Is this optimization worthwhile, or would it
> surprise too many people? Depending on this, it should either be
> disallowed, or possibly implemented for other back-ends as well.
>
>
> After some consideration, I have come to the conclusion that this
> optimization (changing rint to round) is not worthwhile. There are
> some floating point operations that can provide an exact result,
> and
> not obtaining this exact result is surprising. For example, I would
> expect that adding/multiplying two small integers gives the exact
> result, or that fmin/fmax give the correct result if no nans are
> involved, or that comparisons yield the correct answer (again in
> the
> absence of nans, denormalized numbers etc.).
>
>
> The case here -- rint(0.5) -- involves an input that can be
> represented exactly, and an output that can be represented exactly
> (0.0). Neither nans, infinities, nor denormalized numbers are
> involved. In this case I do expect the correct answer, even with
> full floating point operations that ignore nans, infinities,
> denormalized numbers, or that re-associate etc.

I've been thinking about this for some time as well, and I've come to
the same conclusion. I'll be updating the PPC backend accordingly in
the near future. frin should really map to round() and not rint(),
and we should leave it at that.

The PowerPC backend has been modified in r187960 to map round() -> frin (and remove the nearbyint() and rint() fast-math mappings).

Thanks again,
Hal