| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // This Source Code Form is subject to the terms of the Mozilla | |
| // Public License v. 2.0. If a copy of the MPL was not distributed | |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| // SPDX-FileCopyrightText: The Eigen Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| // isinf/isnan/isfinite of every bfloat16 encoding, scalar, packet and through the public array predicates. The | |
| // inputs are built from raw bits and the expectations from the exponent and mantissa fields in integer arithmetic, | |
| // so nothing here can fold under -ffast-math: bfloat16_classification_fastmath compiles this file with that flag, | |
| // under which a classification of the widened float folds to "not NaN, not inf, finite". | |
| #include "main.h" | |
| namespace { | |
| using Eigen::numext::uint16_t; | |
| struct Classification { | |
| bool is_inf, is_nan, is_finite; | |
| }; | |
| // An exponent field of all ones is an infinity with a zero mantissa and a NaN otherwise. | |
| Classification classify(uint16_t bits) { | |
| const bool exponent_all_ones = (bits & 0x7f80) == 0x7f80; | |
| const bool mantissa_zero = (bits & 0x007f) == 0; | |
| return {exponent_all_ones && mantissa_zero, exponent_all_ones && !mantissa_zero, !exponent_all_ones}; | |
| } | |
| bfloat16 from_bits(int bits) { return numext::bit_cast<bfloat16>(static_cast<uint16_t>(bits)); } | |
| uint16_t bits_of(bfloat16 x) { return numext::bit_cast<uint16_t>(x); } | |
| void bfloat16_scalar_classification() { | |
| for (int bits = 0; bits <= 0xffff; ++bits) { | |
| const Classification expected = classify(static_cast<uint16_t>(bits)); | |
| const bfloat16 x = from_bits(bits); | |
| VERIFY_IS_EQUAL((numext::isinf)(x), expected.is_inf); | |
| VERIFY_IS_EQUAL((numext::isnan)(x), expected.is_nan); | |
| VERIFY_IS_EQUAL((numext::isfinite)(x), expected.is_finite); | |
| } | |
| } | |
| // A vectorized packet must return a full-bit mask per lane: all ones where the predicate holds, all zeros elsewhere. | |
| template <typename Packet, bool Vectorized = internal::unpacket_traits<Packet>::vectorizable> | |
| struct packet_classification { | |
| static void run() { | |
| constexpr int PacketSize = internal::unpacket_traits<Packet>::size; | |
| EIGEN_ALIGN_MAX bfloat16 in[PacketSize], is_inf[PacketSize], is_nan[PacketSize], is_finite[PacketSize]; | |
| for (int start = 0; start <= 0xffff; start += PacketSize) { | |
| for (int lane = 0; lane < PacketSize; ++lane) in[lane] = from_bits(start + lane); | |
| const Packet a = internal::pload<Packet>(in); | |
| internal::pstore(is_inf, internal::pisinf(a)); | |
| internal::pstore(is_nan, internal::pisnan(a)); | |
| internal::pstore(is_finite, internal::pisfinite(a)); | |
| for (int lane = 0; lane < PacketSize; ++lane) { | |
| const Classification expected = classify(static_cast<uint16_t>(start + lane)); | |
| VERIFY_IS_EQUAL(bits_of(is_inf[lane]), expected.is_inf ? 0xffff : 0); | |
| VERIFY_IS_EQUAL(bits_of(is_nan[lane]), expected.is_nan ? 0xffff : 0); | |
| VERIFY_IS_EQUAL(bits_of(is_finite[lane]), expected.is_finite ? 0xffff : 0); | |
| } | |
| } | |
| } | |
| }; | |
| template <typename Packet> | |
| struct packet_classification<Packet, false> { | |
| static void run() {} | |
| }; | |
| void bfloat16_packet_classification() { | |
| using Packet = internal::packet_traits<bfloat16>::type; | |
| using HalfPacket = internal::unpacket_traits<Packet>::half; | |
| packet_classification<Packet>::run(); | |
| if (!std::is_same<Packet, HalfPacket>::value) packet_classification<HalfPacket>::run(); | |
| } | |
| using ArrayType = Array<bfloat16, Dynamic, 1>; | |
| // The bool predicates go through numext::is*; the typed ones through pis* on whole packets and numext::is* on the | |
| // scalar tail, where a lane is the all-ones mask or Scalar(1), so only its truth value is checked. isFiniteTyped() | |
| // is the public typed predicate; the isinf/isnan functors have no public entry point and are applied directly. | |
| // (allFinite() is not checked: under __FINITE_MATH_ONLY__ it is true by definition, see all_finite_impl.) | |
| void check_array_classification(const ArrayType& x) { | |
| const Array<bool, Dynamic, 1> is_inf = x.isInf(), is_nan = x.isNaN(), is_finite = x.isFinite(); | |
| const ArrayType typed_inf = x.unaryExpr(internal::scalar_isinf_op<bfloat16, true>()), | |
| typed_nan = x.unaryExpr(internal::scalar_isnan_op<bfloat16, true>()), | |
| typed_finite = x.isFiniteTyped(); | |
| for (Index i = 0; i < x.size(); ++i) { | |
| const Classification expected = classify(bits_of(x[i])); | |
| VERIFY_IS_EQUAL(is_inf[i], expected.is_inf); | |
| VERIFY_IS_EQUAL(is_nan[i], expected.is_nan); | |
| VERIFY_IS_EQUAL(is_finite[i], expected.is_finite); | |
| VERIFY_IS_EQUAL(bits_of(typed_inf[i]) != 0, expected.is_inf); | |
| VERIFY_IS_EQUAL(bits_of(typed_nan[i]) != 0, expected.is_nan); | |
| VERIFY_IS_EQUAL(bits_of(typed_finite[i]) != 0, expected.is_finite); | |
| } | |
| } | |
| void bfloat16_array_classification() { | |
| ArrayType all(0x10000); | |
| for (Index i = 0; i < all.size(); ++i) all[i] = from_bits(int(i)); | |
| check_array_classification(all); | |
| // Lengths that are not a multiple of the packet size leave a scalar tail; over these lengths it crosses the | |
| // largest finite value, the infinity and the first NaNs of either sign. | |
| for (Index len = 1; len <= 33; ++len) { | |
| check_array_classification(all.segment(0x7f70, len)); | |
| check_array_classification(all.segment(0xff70, len)); | |
| } | |
| } | |
| } // namespace | |
| EIGEN_DECLARE_TEST(bfloat16_classification) { | |
| CALL_SUBTEST(bfloat16_scalar_classification()); | |
| CALL_SUBTEST(bfloat16_packet_classification()); | |
| CALL_SUBTEST(bfloat16_array_classification()); | |
| } |