OpenCL-Docs icon indicating copy to clipboard operation
OpenCL-Docs copied to clipboard

edge case behavior for fract

Open bashbaug opened this issue 2 years ago • 1 comments

From a discussion in the SPIR call on November 15th (see also internal SPIR-V issue 768):

OpenCL defines the following edge case behavior for the fract built-in function - see link. These requirements have been in place since OpenCL 1.0:

fract(x, iptr) shall not return a value greater than or equal to 1.0, and shall not return a value less than 0. fract(+0, iptr) returns +0 and +0 in iptr. fract(-0, iptr) returns -0 and -0 in iptr. fract(+∞, iptr) returns +0 and +∞ in iptr. fract(-∞, iptr) returns -0 and -∞ in iptr. fract(NaN, iptr) returns the NaN and NaN in iptr.

It doesn't look like all of these edge case conditions are tested in the CTS tests though, and for the conditions in bold, several implementations that I've tested are returning +0 instead of -0.

Does anyone recall why these edge cases are defined as they are? Is it to match the semantics of the C99 modf, which is specified to return values "each having the same type and sign as arg"?

bashbaug avatar Nov 15 '23 21:11 bashbaug

Is it to match the semantics of the C99 modf

This may or may not be relevant, but there are some important differences with C's modf that make matching the semantics potentially weird (although that doesn't mean that it isn't the reason!).

Writing modf(x) to mean the fractional part returned by the modf function (so ignoring the integer part), C has modf(x) == x for all x in (-1, 1), so it is natural that modf(-0) == -0. Similarly C has modf(x) == -0 for all negative integers and, because floating-point precision means that all very large values are integers, for all sufficiently large negative values. This suggests that modf(-Inf) == -0.

OpenCL C's fract has neither of these properties. -0 and -Inf are the only 2 values for which the function will return -0. fract(x) == +0 for all sufficiently large negative values of x, but this flips to -0 at -Inf. I would suggest that +0 is in some sense "more correct" for these results, but obviously the spec has clearly said otherwise for a long time.

One advantage to having fract(-0) == -0 is that we get floor(x) + fract(x) == x for -0, but the relation still does not hold for very small, non-zero negative values, for which floor(x) == -1.0, fract(x) ~= 1.0 and so there is not enough precision to recover x when adding the results together.

I don't really have a conclusion here or a result that I'm pushing for, but having worked through all this on the Vulkan side, I thought I'd share.

gnl21 avatar Nov 16 '23 18:11 gnl21