Skip to content

Commit 4bf9a16

Browse files
andishgarpitrou
andauthored
GH-48408: [C++] Enable ULP-based float comparison (#49290)
### Rationale for this change Enable ULP-based floating-point comparison. ### What changes are included in this PR? Add `arrow::EqualOptions::use_ulp_distance` and `arrow::EqualOptions::ulp_distance`. ### Are these changes tested? Yes. ### Are there any user-facing changes? Yes. The ULP-based comparison method is enabled via `arrow::EqualOptions::use_ulp_distance` and `arrow::EqualOptions::ulp_distance`. * GitHub Issue: #48408 Lead-authored-by: arash andishgar <arashandishgar1@gmail.com> Co-authored-by: Antoine Pitrou <antoine@python.org> Signed-off-by: Sutou Kouhei <kou@clear-code.com>
1 parent 6513474 commit 4bf9a16

22 files changed

Lines changed: 720 additions & 417 deletions

c_glib/arrow-glib/basic-array.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ garrow_equal_options_get_property(GObject *object,
279279
g_value_set_boolean(value, priv->options.nans_equal());
280280
break;
281281
case PROP_ABSOLUTE_TOLERANCE:
282-
g_value_set_double(value, priv->options.atol());
282+
g_value_set_double(value,
283+
priv->options.atol().has_value() ? *priv->options.atol() : 0.0);
283284
break;
284285
default:
285286
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -348,7 +349,7 @@ garrow_equal_options_class_init(GArrowEqualOptionsClass *klass)
348349
"of floating-point values",
349350
-G_MAXDOUBLE,
350351
G_MAXDOUBLE,
351-
options.atol(),
352+
options.atol().has_value() ? *options.atol() : 0.0,
352353
static_cast<GParamFlags>(G_PARAM_READWRITE));
353354
g_object_class_install_property(gobject_class, PROP_ABSOLUTE_TOLERANCE, spec);
354355
}

cpp/src/arrow/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ set(ARROW_UTIL_SRCS
576576
util/time.cc
577577
util/tracing.cc
578578
util/trie.cc
579+
util/ulp_distance.cc
579580
util/union_util.cc
580581
util/unreachable.cc
581582
util/uri.cc

cpp/src/arrow/array/array_test.cc

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2199,11 +2199,19 @@ void CheckFloatApproxEqualsWithAtol() {
21992199
auto options = EqualOptions::Defaults().atol(0.2);
22002200

22012201
ASSERT_FALSE(a->Equals(b));
2202+
ASSERT_TRUE(a->Equals(b, options));
2203+
ARROW_SUPPRESS_DEPRECATION_WARNING
22022204
ASSERT_TRUE(a->Equals(b, options.use_atol(true)));
2205+
ASSERT_FALSE(a->Equals(b, options.use_atol(false)));
2206+
ARROW_UNSUPPRESS_DEPRECATION_WARNING
22032207
ASSERT_TRUE(a->ApproxEquals(b, options));
22042208

2205-
ASSERT_FALSE(a->RangeEquals(0, 1, 0, b, options));
2209+
ASSERT_FALSE(a->RangeEquals(0, 1, 0, b));
2210+
ASSERT_TRUE(a->RangeEquals(0, 1, 0, b, options));
2211+
ARROW_SUPPRESS_DEPRECATION_WARNING
22062212
ASSERT_TRUE(a->RangeEquals(0, 1, 0, b, options.use_atol(true)));
2213+
ASSERT_FALSE(a->RangeEquals(0, 1, 0, b, options.use_atol(false)));
2214+
ARROW_UNSUPPRESS_DEPRECATION_WARNING
22072215
ASSERT_TRUE(ArrayRangeApproxEquals(*a, *b, 0, 1, 0, options));
22082216
}
22092217

@@ -2413,6 +2421,42 @@ void CheckFloatingZeroEquality() {
24132421
}
24142422
}
24152423

2424+
template <typename TYPE>
2425+
void CheckFloatApproxEqualsWithUlpDistance() {
2426+
using CType =
2427+
std::conditional_t<is_half_float_type<TYPE>::value, Float16, typename TYPE::c_type>;
2428+
auto type = TypeTraits<TYPE>::type_singleton();
2429+
std::vector<CType> a, b;
2430+
a = {CType(NAN), CType(+0.0), CType(INFINITY)};
2431+
b = {CType(NAN), CType(-0.0), CType(INFINITY)};
2432+
if constexpr (is_half_float_type<TYPE>::value) {
2433+
a.push_back(Float16(1.00097656));
2434+
b.push_back(Float16(0.999511719f));
2435+
} else if constexpr (std::is_same_v<TYPE, DoubleType>) {
2436+
a.push_back(CType(0.9999999999999999));
2437+
b.push_back(CType(1.0000000000000002));
2438+
} else if constexpr (std::is_same_v<TYPE, FloatType>) {
2439+
a.push_back(CType(1.0000001f));
2440+
b.push_back(CType(0.99999994f));
2441+
}
2442+
2443+
std::shared_ptr<Array> array_a, array_b;
2444+
ArrayFromVector<TYPE>(type, a, &array_a);
2445+
ArrayFromVector<TYPE>(type, b, &array_b);
2446+
auto options = EqualOptions::Defaults().ulp_distance(2);
2447+
2448+
// Check with NaN
2449+
ASSERT_FALSE(array_a->Equals(array_b, options));
2450+
ASSERT_TRUE(array_a->Equals(array_b, options.nans_equal(true)));
2451+
2452+
// Check With Signed Zero
2453+
ASSERT_FALSE(
2454+
array_a->Equals(array_b, options.nans_equal(true).signed_zeros_equal(false)));
2455+
2456+
// Check with Ulp Distance
2457+
ASSERT_FALSE(array_a->Equals(array_b, options.nans_equal(true).ulp_distance(1)));
2458+
}
2459+
24162460
TEST(TestPrimitiveAdHoc, FloatingApproxEquals) {
24172461
CheckApproxEquals<FloatType>();
24182462
CheckApproxEquals<DoubleType>();
@@ -2446,6 +2490,12 @@ TEST(TestPrimitiveAdHoc, FloatingZeroEquality) {
24462490
CheckFloatingZeroEquality<HalfFloatType>();
24472491
}
24482492

2493+
TEST(TestPrimitiveAdHoc, FloatingUlpDistanceEquality) {
2494+
CheckFloatApproxEqualsWithUlpDistance<HalfFloatType>();
2495+
CheckFloatApproxEqualsWithUlpDistance<FloatType>();
2496+
CheckFloatApproxEqualsWithUlpDistance<DoubleType>();
2497+
}
2498+
24492499
// ----------------------------------------------------------------------
24502500
// FixedSizeBinary tests
24512501

cpp/src/arrow/array/statistics_test.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,12 @@ TEST_F(TestArrayStatisticsEqualityDoubleValue, NaN) {
255255
TEST_F(TestArrayStatisticsEqualityDoubleValue, ApproximateEquals) {
256256
statistics1_.max = 0.5001f;
257257
statistics2_.max = 0.5;
258-
ASSERT_FALSE(statistics1_.Equals(statistics2_, options_.atol(1e-3)));
258+
ASSERT_FALSE(statistics1_.Equals(statistics2_, options_));
259+
ASSERT_TRUE(statistics1_.Equals(statistics2_, options_.atol(1e-3)));
260+
ARROW_SUPPRESS_DEPRECATION_WARNING
259261
ASSERT_TRUE(statistics1_.Equals(statistics2_, options_.atol(1e-3).use_atol(true)));
262+
ASSERT_FALSE(statistics1_.Equals(statistics2_, options_.atol(1e-3).use_atol(false)));
263+
ARROW_UNSUPPRESS_DEPRECATION_WARNING
260264
}
261265

262266
} // namespace arrow

cpp/src/arrow/chunked_array.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ bool ChunkedArray::Equals(const std::shared_ptr<ChunkedArray>& other,
161161

162162
bool ChunkedArray::ApproxEquals(const ChunkedArray& other,
163163
const EqualOptions& equal_options) const {
164-
return Equals(other, equal_options.use_atol(true));
164+
auto resolved_options = equal_options;
165+
if (!resolved_options.atol()) {
166+
resolved_options = resolved_options.atol(kDefaultAbsoluteTolerance);
167+
}
168+
return Equals(other, resolved_options);
165169
}
166170

167171
Result<std::shared_ptr<Scalar>> ChunkedArray::GetScalar(int64_t index) const {

cpp/src/arrow/chunked_array.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ class ARROW_EXPORT ChunkedArray {
166166
bool Equals(const std::shared_ptr<ChunkedArray>& other,
167167
const EqualOptions& opts = EqualOptions::Defaults()) const;
168168
/// \brief Determine if two chunked arrays approximately equal
169+
///
170+
/// If the absolute tolerance (atol) is not specified in \ref arrow::EqualOptions,
171+
/// 'arrow::kDefaultAbsoluteTolerance' is used.
172+
///
169173
bool ApproxEquals(const ChunkedArray& other,
170174
const EqualOptions& = EqualOptions::Defaults()) const;
171175

cpp/src/arrow/chunked_array_test.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,11 @@ TEST_F(TestChunkedArray, ApproxEquals) {
202202
auto options = EqualOptions::Defaults().atol(1e-3);
203203

204204
ASSERT_FALSE(chunked_array_1->Equals(chunked_array_2));
205+
ASSERT_TRUE(chunked_array_1->Equals(chunked_array_2, options));
206+
ARROW_SUPPRESS_DEPRECATION_WARNING
205207
ASSERT_TRUE(chunked_array_1->Equals(chunked_array_2, options.use_atol(true)));
208+
ASSERT_FALSE(chunked_array_1->Equals(chunked_array_2, options.use_atol(false)));
209+
ARROW_UNSUPPRESS_DEPRECATION_WARNING
206210
ASSERT_TRUE(chunked_array_1->ApproxEquals(*chunked_array_2, options));
207211
}
208212

0 commit comments

Comments
 (0)