| 1320 | } |
| 1321 | |
| 1322 | XlaOp NextAfter(XlaOp from, XlaOp to) { |
| 1323 | auto builder = from.builder(); |
| 1324 | return builder->ReportErrorOrReturn([&]() -> StatusOr<XlaOp> { |
| 1325 | TF_ASSIGN_OR_RETURN(auto shape, builder->GetShape(from)); |
| 1326 | int bitwidth = primitive_util::BitWidth(shape.element_type()); |
| 1327 | auto int_type = primitive_util::UnsignedIntegralTypeForBitWidth(bitwidth); |
| 1328 | auto from_as_int = BitcastConvertType(from, int_type); |
| 1329 | auto to_as_int = BitcastConvertType(to, int_type); |
| 1330 | |
| 1331 | // The result is NaN if either "from" or "to" are NaN. |
| 1332 | auto from_is_nan = Ne(from, from); |
| 1333 | auto to_is_nan = Ne(to, to); |
| 1334 | auto nan_input = Or(from_is_nan, to_is_nan); |
| 1335 | auto result_for_nan = |
| 1336 | Broadcast(ScalarLike(from, std::numeric_limits<double>::quiet_NaN()), |
| 1337 | shape.dimensions()); |
| 1338 | result_for_nan = BitcastConvertType(result_for_nan, int_type); |
| 1339 | |
| 1340 | // The sign bit is the MSB. |
| 1341 | const int64 sign_mask = int64{1} << (bitwidth - 1); |
| 1342 | // Discard the sign bit to make the result non-negative. |
| 1343 | auto from_abs = And(from_as_int, ScalarLike(from_as_int, ~sign_mask)); |
| 1344 | auto to_abs = And(to_as_int, ScalarLike(to_as_int, ~sign_mask)); |
| 1345 | |
| 1346 | // When both "from" and "to" are equal, the result is "to". |
| 1347 | // N.B. It would not make a difference if we chose the result to be "from". |
| 1348 | auto from_and_to_are_equal = Eq(from_as_int, to_as_int); |
| 1349 | auto result_for_equal = to_as_int; |
| 1350 | |
| 1351 | // When both "from" and "to" are both 0, the result is "to". This ensures we |
| 1352 | // get a zero signed like "to". |
| 1353 | auto from_is_zero = Eq(from_abs, ZerosLike(from_abs)); |
| 1354 | auto to_is_zero = Eq(to_abs, ZerosLike(to_abs)); |
| 1355 | auto result_for_both_zero = to_as_int; |
| 1356 | |
| 1357 | auto from_sign = And(from_as_int, ScalarLike(from_as_int, sign_mask)); |
| 1358 | auto to_sign = And(to_as_int, ScalarLike(to_as_int, sign_mask)); |
| 1359 | |
| 1360 | // If from == 0 && to != 0, we need to return the smallest subnormal number |
| 1361 | // signed like "to". |
| 1362 | auto result_for_from_zero_to_non_zero = |
| 1363 | Or(to_sign, ScalarLike(from_as_int, 1)); |
| 1364 | |
| 1365 | // If the sign of "from" and "to" disagree: |
| 1366 | // - we need to make the magnitude of "from" smaller so that it is closer to |
| 1367 | // zero. |
| 1368 | // |
| 1369 | // Otherwise the signs agree: |
| 1370 | // - "from" with a magnitude larger than "to" means we need to make the |
| 1371 | // magnitude smaller. |
| 1372 | // - "from" with a magnitude smaller than "to" means we need to make the |
| 1373 | // magnitude larger. |
| 1374 | // - "from" with the same magnitude and sign as "to" has already been |
| 1375 | // handled. |
| 1376 | auto signs_disagree = Ne(from_sign, to_sign); |
| 1377 | auto from_magnitude_larger_than_to = Gt(from_abs, to_abs); |
| 1378 | auto result_has_smaller_magnitude = |
| 1379 | Or(from_magnitude_larger_than_to, signs_disagree); |
no test coverage detected