Skip to content

Commit 1509b46

Browse files
authored
[libclc] Improve nextafter behaviour around zero (llvm#127469)
This commit improves the behaviour of (__clc_)nextafter around zero. Specifically, the nextafter value of very small negative numbers in the positive direction is now negative zero. Previously we'd return positive zero. This behaviour is not required as far as OpenCL is concerned: at least, the CTS isn't testing for it. However, this change does bring our implementation into bit-equivalence with (libstdc++'s implementation of) std::nextafter, tested on all possible values of 32-bit float towards both positive and negative INFINITY. Furthermore, since the implementation of libclc's floating-point 'rtp' and 'rtz' conversions use __clc_nextafter, the previous behaviour was resulting in CTS validation issues. For example, when converting float -0x1.000002p-25 to half, rounding towards zero or positive infinity, nextafter was returning +0.0, whereas the correct conversion requires us to return -0.0. We could work around this issue in the conversion functions, but since the change to nextafter is small enough and the behaviour around zero matches libstdc++, the fix feels at home there. This commit also converts several variables to unsigned types to avoid undefined behaviour surrounding signed underflow on the subtractions. It also converts some variables to be kept in floating-point types, using fabs to get the absolute value rather than by bit-hacking.
1 parent 7781e10 commit 1509b46

File tree

1 file changed

+23
-13
lines changed

1 file changed

+23
-13
lines changed

libclc/clc/lib/generic/math/clc_nextafter.cl

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <clc/clcmacro.h>
22
#include <clc/internal/clc.h>
3+
#include <clc/math/clc_fabs.h>
34
#include <clc/relational/clc_isnan.h>
45

56
// This file provides OpenCL C implementations of __clc_nextafter for
@@ -12,21 +13,30 @@
1213
FLOAT_TYPE y) { \
1314
const UINT_TYPE sign_bit = (UINT_TYPE)1 \
1415
<< (sizeof(INT_TYPE_SCALAR) * 8 - 1); \
15-
const UINT_TYPE sign_bit_mask = sign_bit - (UINT_TYPE)1; \
16-
INT_TYPE ix = CLC_AS_TYPE(INT_TYPE)(x); \
17-
UINT_TYPE ax = CLC_AS_TYPE(UINT_TYPE)(ix) & sign_bit_mask; \
18-
INT_TYPE mx = CLC_AS_TYPE(INT_TYPE)(sign_bit) - ix; \
19-
mx = CLC_AS_TYPE(INT_TYPE)(ix) < (INT_TYPE)0 ? mx : ix; \
20-
INT_TYPE iy = CLC_AS_TYPE(INT_TYPE)(y); \
21-
UINT_TYPE ay = CLC_AS_TYPE(UINT_TYPE)(iy) & sign_bit_mask; \
22-
INT_TYPE my = CLC_AS_TYPE(INT_TYPE)(sign_bit) - iy; \
23-
my = iy < (INT_TYPE)0 ? my : iy; \
16+
UINT_TYPE ix = CLC_AS_TYPE(UINT_TYPE)(x); \
17+
FLOAT_TYPE absx = __clc_fabs(x); \
18+
UINT_TYPE mxu = sign_bit - ix; \
19+
INT_TYPE mx = CLC_AS_TYPE(INT_TYPE)(mxu); \
20+
mx = CLC_AS_TYPE(INT_TYPE)(ix) < (INT_TYPE)0 ? mx \
21+
: CLC_AS_TYPE(INT_TYPE)(ix); \
22+
UINT_TYPE iy = CLC_AS_TYPE(UINT_TYPE)(y); \
23+
FLOAT_TYPE absy = __clc_fabs(y); \
24+
UINT_TYPE myu = sign_bit - iy; \
25+
INT_TYPE my = CLC_AS_TYPE(INT_TYPE)(myu); \
26+
my = CLC_AS_TYPE(INT_TYPE)(iy) < (INT_TYPE)0 ? my \
27+
: CLC_AS_TYPE(INT_TYPE)(iy); \
2428
INT_TYPE t = mx + (mx < my ? (INT_TYPE)1 : (INT_TYPE)-1); \
25-
INT_TYPE r = CLC_AS_TYPE(INT_TYPE)(sign_bit) - t; \
26-
r = t < (INT_TYPE)0 ? r : t; \
29+
UINT_TYPE r = sign_bit - CLC_AS_TYPE(UINT_TYPE)(t); \
30+
r = (t < (INT_TYPE)0 || (t == (INT_TYPE)0 && mx < my)) \
31+
? r \
32+
: CLC_AS_TYPE(UINT_TYPE)(t); \
2733
r = __clc_isnan(x) ? ix : r; \
28-
r = __clc_isnan(y) ? CLC_AS_TYPE(INT_TYPE)(iy) : r; \
29-
r = ((ax | ay) == (UINT_TYPE)0 || ix == iy) ? iy : r; \
34+
r = __clc_isnan(y) ? iy : r; \
35+
r = ((CLC_AS_TYPE(UINT_TYPE)(absx) | CLC_AS_TYPE(UINT_TYPE)(absy)) == \
36+
(UINT_TYPE)0 || \
37+
ix == iy) \
38+
? iy \
39+
: r; \
3040
return CLC_AS_TYPE(FLOAT_TYPE)(r); \
3141
}
3242

0 commit comments

Comments
 (0)