diff --git a/cpp/src/arrow/array/array_test.cc b/cpp/src/arrow/array/array_test.cc index e5a27d18d00a..3c6bf6255640 100644 --- a/cpp/src/arrow/array/array_test.cc +++ b/cpp/src/arrow/array/array_test.cc @@ -38,6 +38,7 @@ #include "arrow/array/builder_binary.h" #include "arrow/array/builder_decimal.h" #include "arrow/array/builder_dict.h" +#include "arrow/array/builder_primitive.h" #include "arrow/array/builder_run_end.h" #include "arrow/array/builder_time.h" #include "arrow/array/data.h" @@ -60,6 +61,7 @@ #include "arrow/util/bitmap_builders.h" #include "arrow/util/checked_cast.h" #include "arrow/util/decimal.h" +#include "arrow/util/float16.h" #include "arrow/util/key_value_metadata.h" #include "arrow/util/macros.h" #include "arrow/util/range.h" @@ -72,6 +74,7 @@ namespace arrow { using internal::checked_cast; using internal::checked_pointer_cast; +using util::Float16; class TestArray : public ::testing::Test { public: @@ -4075,4 +4078,72 @@ TYPED_TEST(TestPrimitiveArray, IndexOperator) { } } +class TestHalfFloatBuilder : public ::testing::Test { + public: + void VerifyValue(HalfFloatBuilder& builder, int64_t index, float expected) { + ASSERT_EQ(builder.GetValue(index), Float16(expected).bits()); + ASSERT_EQ(builder.GetValue(index), Float16(expected)); + ASSERT_EQ(builder.GetValue(index), Float16(expected).bits()); + ASSERT_EQ(builder[index], Float16(expected).bits()); + } +}; + +TEST_F(TestHalfFloatBuilder, TestAppend) { + HalfFloatBuilder builder; + ASSERT_OK(builder.Append(Float16(0.0f))); + ASSERT_OK(builder.Append(Float16(1.0f).bits())); + ASSERT_OK(builder.AppendNull()); + ASSERT_OK(builder.Reserve(3)); + builder.UnsafeAppend(Float16(3.0f)); + builder.UnsafeAppend(Float16(4.0f).bits()); + builder.UnsafeAppend(uint16_t{15872}); // 1.5f + + VerifyValue(builder, 0, 0.0f); + VerifyValue(builder, 1, 1.0f); + VerifyValue(builder, 3, 3.0f); + VerifyValue(builder, 4, 4.0f); + VerifyValue(builder, 5, 1.5f); +} + +TEST_F(TestHalfFloatBuilder, TestBulkAppend) { + HalfFloatBuilder builder; + + ASSERT_OK(builder.AppendValues(5, Float16(1.5))); + uint16_t val = Float16(2.0f).bits(); + ASSERT_OK(builder.AppendValues({val, val, val, val}, {0, 1, 0, 1})); + ASSERT_EQ(builder.length(), 9); + for (int i = 0; i < 5; i++) { + VerifyValue(builder, i, 1.5f); + } + ASSERT_OK_AND_ASSIGN(auto array, builder.Finish()); + ASSERT_EQ(array->null_count(), 2); + ASSERT_EQ(array->length(), 9); + auto comp = ArrayFromJSON(float16(), "[1.5,1.5,1.5,1.5,1.5,null,2,null,2]"); + ASSERT_TRUE(array->Equals(*comp)); + + std::vector vals = {Float16(2.5), Float16(3.5)}; + std::vector is_valid = {true, true}; + std::vector bitmap = {1, 1}; + ASSERT_OK(builder.AppendValues(vals)); + ASSERT_OK(builder.AppendValues(vals, is_valid)); + ASSERT_OK(builder.AppendValues(vals.data(), vals.size(), is_valid)); + ASSERT_OK(builder.AppendValues(vals.data(), vals.size())); + ASSERT_OK(builder.AppendValues(vals.data(), vals.size(), bitmap.data(), 0)); + + for (int i = 0; i < 5; i++) { + VerifyValue(builder, (2 * i), 2.5); + VerifyValue(builder, (2 * i) + 1, 3.5); + } +} + +TEST_F(TestHalfFloatBuilder, TestReinterpretCast) { + std::vector vf{Float16(1.0f), Float16(2.0f), Float16(3.0f)}; + Float16* fdata = vf.data(); + uint16_t* udata = reinterpret_cast(fdata); + + ASSERT_EQ(udata[0], vf[0].bits()); + ASSERT_EQ(udata[1], vf[1].bits()); + ASSERT_EQ(udata[2], vf[2].bits()); +} + } // namespace arrow diff --git a/cpp/src/arrow/array/builder_primitive.h b/cpp/src/arrow/array/builder_primitive.h index 8ee3c260d368..dc560a018d44 100644 --- a/cpp/src/arrow/array/builder_primitive.h +++ b/cpp/src/arrow/array/builder_primitive.h @@ -26,6 +26,7 @@ #include "arrow/result.h" #include "arrow/type.h" #include "arrow/type_traits.h" +#include "arrow/util/float16.h" namespace arrow { @@ -196,7 +197,7 @@ class NumericBuilder /// \brief Append a sequence of elements in one shot /// \param[in] values a contiguous C array of values /// \param[in] length the number of values to append - /// \param[in] is_valid an std::vector indicating valid (1) or null + /// \param[in] is_valid a std::vector indicating valid (1) or null /// (0). Equal in length to values /// \return Status Status AppendValues(const value_type* values, int64_t length, @@ -364,7 +365,6 @@ using Int16Builder = NumericBuilder; using Int32Builder = NumericBuilder; using Int64Builder = NumericBuilder; -using HalfFloatBuilder = NumericBuilder; using FloatBuilder = NumericBuilder; using DoubleBuilder = NumericBuilder; @@ -384,6 +384,101 @@ using DurationBuilder = NumericBuilder; /// @} +class ARROW_EXPORT HalfFloatBuilder : public NumericBuilder { + public: + using BaseClass = NumericBuilder; + using Float16 = arrow::util::Float16; + + using BaseClass::Append; + using BaseClass::AppendValues; + using BaseClass::BaseClass; + using BaseClass::GetValue; + using BaseClass::UnsafeAppend; + + /// Scalar append a arrow::util::Float16 + Status Append(const Float16 val) { return Append(val.bits()); } + + /// Scalar append a arrow::util::Float16, without checking for capacity + void UnsafeAppend(const Float16 val) { UnsafeAppend(val.bits()); } + + /// \brief Append a sequence of elements in one shot + /// \param[in] values a contiguous array of arrow::util::Float16 + /// \param[in] length the number of values to append + /// \param[in] valid_bytes an optional sequence of bytes where non-zero + /// indicates a valid (non-null) value + /// \return Status + Status AppendValues(const Float16* values, int64_t length, + const uint8_t* valid_bytes = NULLPTR) { + return BaseClass::AppendValues(reinterpret_cast(values), length, + valid_bytes); + } + + /// \brief Append a sequence of elements in one shot + /// \param[in] values a contiguous array of arrow::util::Float16 + /// \param[in] length the number of values to append + /// \param[in] bitmap a validity bitmap to copy (may be null) + /// \param[in] bitmap_offset an offset into the validity bitmap + /// \return Status + Status AppendValues(const Float16* values, int64_t length, const uint8_t* bitmap, + int64_t bitmap_offset) { + return BaseClass::AppendValues(reinterpret_cast(values), length, + bitmap, bitmap_offset); + } + + /// \brief Append a sequence of elements in one shot + /// \param[in] values a contiguous array of arrow::util::Float16 + /// \param[in] length the number of values to append + /// \param[in] is_valid a std::vector indicating valid (1) or null + /// (0). Equal in length to values + /// \return Status + Status AppendValues(const Float16* values, int64_t length, + const std::vector& is_valid) { + return BaseClass::AppendValues(reinterpret_cast(values), length, + is_valid); + } + + /// \brief Append a sequence of elements in one shot + /// \param[in] values a std::vector + /// \param[in] is_valid a std::vector indicating valid (1) or null + /// (0). Equal in length to values + /// \return Status + Status AppendValues(const std::vector& values, + const std::vector& is_valid) { + return AppendValues(values.data(), static_cast(values.size()), is_valid); + } + + /// \brief Append a sequence of elements in one shot + /// \param[in] values a std::vector + /// \return Status + Status AppendValues(const std::vector& values) { + return AppendValues(values.data(), static_cast(values.size())); + } + + /// \brief Append one value many times in one shot + /// \param[in] length the number of values to append + /// \param[in] value a arrow::util::Float16 + Status AppendValues(int64_t length, Float16 value) { + RETURN_NOT_OK(Reserve(length)); + data_builder_.UnsafeAppend(length, value.bits()); + ArrayBuilder::UnsafeSetNotNull(length); + return Status::OK(); + } + + /// \brief Get the value at a certain index + /// \param[in] index the zero-based index + /// @tparam arrow::util::Float16 or value_type (uint16_t) + template + T GetValue(int64_t index) const { + static_assert(std::is_same_v || + std::is_same_v); + if constexpr (std::is_same_v) { + return BaseClass::GetValue(index); + } else { + return Float16::FromBits(BaseClass::GetValue(index)); + } + } +}; + class ARROW_EXPORT BooleanBuilder : public ArrayBuilder, public internal::ArrayBuilderExtraOps { diff --git a/cpp/src/arrow/type_fwd.h b/cpp/src/arrow/type_fwd.h index 5a2fbde0232d..dc290cd327ae 100644 --- a/cpp/src/arrow/type_fwd.h +++ b/cpp/src/arrow/type_fwd.h @@ -242,12 +242,17 @@ _NUMERIC_TYPE_DECL(UInt8) _NUMERIC_TYPE_DECL(UInt16) _NUMERIC_TYPE_DECL(UInt32) _NUMERIC_TYPE_DECL(UInt64) -_NUMERIC_TYPE_DECL(HalfFloat) _NUMERIC_TYPE_DECL(Float) _NUMERIC_TYPE_DECL(Double) #undef _NUMERIC_TYPE_DECL +class HalfFloatType; +using HalfFloatArray = NumericArray; +class HalfFloatBuilder; +struct HalfFloatScalar; +using HalfFloatTensor = NumericTensor; + enum class DateUnit : char { DAY = 0, MILLI = 1 }; class DateType; diff --git a/cpp/src/arrow/util/float16.h b/cpp/src/arrow/util/float16.h index feead50b1a4e..12b6aac364e1 100644 --- a/cpp/src/arrow/util/float16.h +++ b/cpp/src/arrow/util/float16.h @@ -179,6 +179,7 @@ class ARROW_EXPORT Float16 { }; static_assert(std::is_trivial_v); +static_assert(std::is_standard_layout_v); static_assert(sizeof(Float16) == sizeof(uint16_t)); } // namespace util diff --git a/docs/source/cpp/api/builder.rst b/docs/source/cpp/api/builder.rst index 1342ba2655f5..ee3576ec79e8 100644 --- a/docs/source/cpp/api/builder.rst +++ b/docs/source/cpp/api/builder.rst @@ -34,6 +34,9 @@ Primitive .. doxygenclass:: arrow::BooleanBuilder :members: +.. doxygenclass:: arrow::HalfFloatBuilder + :members: + .. doxygengroup:: numeric-builders :content-only: :members: