|
19 | 19 |
|
20 | 20 | #include "iceberg/literal.h" |
21 | 21 |
|
| 22 | +#include <cmath> |
| 23 | +#include <concepts> |
22 | 24 | #include <sstream> |
23 | 25 |
|
24 | 26 | #include "iceberg/exception.h" |
@@ -60,11 +62,11 @@ PrimitiveLiteral PrimitiveLiteral::Binary(std::vector<uint8_t> value) { |
60 | 62 | } |
61 | 63 |
|
62 | 64 | PrimitiveLiteral PrimitiveLiteral::BelowMinLiteral(std::shared_ptr<PrimitiveType> type) { |
63 | | - return PrimitiveLiteral(PrimitiveLiteralValue{BelowMin{}}, std::move(type)); |
| 65 | + return {PrimitiveLiteralValue{BelowMin{}}, std::move(type)}; |
64 | 66 | } |
65 | 67 |
|
66 | 68 | PrimitiveLiteral PrimitiveLiteral::AboveMaxLiteral(std::shared_ptr<PrimitiveType> type) { |
67 | | - return PrimitiveLiteral(PrimitiveLiteralValue{AboveMax{}}, std::move(type)); |
| 69 | + return {PrimitiveLiteralValue{AboveMax{}}, std::move(type)}; |
68 | 70 | } |
69 | 71 |
|
70 | 72 | Result<PrimitiveLiteral> PrimitiveLiteral::Deserialize(std::span<const uint8_t> data) { |
@@ -171,6 +173,30 @@ Result<PrimitiveLiteral> PrimitiveLiteral::CastFromFloat(TypeId target_type_id) |
171 | 173 | } |
172 | 174 | } |
173 | 175 |
|
| 176 | +// Template function for floating point comparison following Iceberg rules: |
| 177 | +// -NaN < NaN, but all NaN values (qNaN, sNaN) are treated as equivalent within their sign |
| 178 | +template <std::floating_point T> |
| 179 | +std::partial_ordering iceberg_float_compare(T lhs, T rhs) { |
| 180 | + bool lhs_is_nan = std::isnan(lhs); |
| 181 | + bool rhs_is_nan = std::isnan(rhs); |
| 182 | + |
| 183 | + // If both are NaN, check their signs |
| 184 | + if (lhs_is_nan && rhs_is_nan) { |
| 185 | + bool lhs_is_negative = std::signbit(lhs); |
| 186 | + bool rhs_is_negative = std::signbit(rhs); |
| 187 | + |
| 188 | + if (lhs_is_negative == rhs_is_negative) { |
| 189 | + // Same sign NaN values are equivalent (no qNaN vs sNaN distinction) |
| 190 | + return std::partial_ordering::equivalent; |
| 191 | + } |
| 192 | + // -NaN < NaN |
| 193 | + return lhs_is_negative ? std::partial_ordering::less : std::partial_ordering::greater; |
| 194 | + } |
| 195 | + |
| 196 | + // For non-NaN values, use standard strong ordering |
| 197 | + return std::strong_order(lhs, rhs); |
| 198 | +} |
| 199 | + |
174 | 200 | // Three-way comparison operator |
175 | 201 | std::partial_ordering PrimitiveLiteral::operator<=>(const PrimitiveLiteral& other) const { |
176 | 202 | // If types are different, comparison is unordered |
@@ -208,14 +234,14 @@ std::partial_ordering PrimitiveLiteral::operator<=>(const PrimitiveLiteral& othe |
208 | 234 | auto this_val = std::get<float>(value_); |
209 | 235 | auto other_val = std::get<float>(other.value_); |
210 | 236 | // Use strong_ordering for floating point as spec requests |
211 | | - return std::strong_order(this_val, other_val); |
| 237 | + return iceberg_float_compare(this_val, other_val); |
212 | 238 | } |
213 | 239 |
|
214 | 240 | case TypeId::kDouble: { |
215 | 241 | auto this_val = std::get<double>(value_); |
216 | 242 | auto other_val = std::get<double>(other.value_); |
217 | 243 | // Use strong_ordering for floating point as spec requests |
218 | | - return std::strong_order(this_val, other_val); |
| 244 | + return iceberg_float_compare(this_val, other_val); |
219 | 245 | } |
220 | 246 |
|
221 | 247 | case TypeId::kString: { |
|
0 commit comments