Copilot commented on code in PR #1042:
URL: https://github.com/apache/iceberg-cpp/pull/1042#discussion_r4313116328


##########
src/iceberg/util/truncate_util.h:
##########
@@ -82,12 +83,20 @@ class ICEBERG_EXPORT TruncateUtils {
 
   /// \brief Truncate an integer v, either int32_t or int64_t, to v - (v % W).
   ///
-  /// The remainder, v % W, must be positive. For languages where % can 
produce negative
-  /// values, the correct truncate function is: v - (((v % W) + W) % W)
+  /// W must be positive. Since C++ can produce a negative remainder, add W 
only
+  /// when v % W is negative to normalize it to [0, W). This addition cannot 
overflow.
+  /// The final subtraction uses unsigned arithmetic to avoid signed-overflow 
undefined
+  /// behavior when the truncated value is below T's minimum. Conversion back 
to T
+  /// preserves two's-complement wraparound (defined since C++20).

Review Comment:
   The statement “preserves two's-complement wraparound (defined since C++20)” 
is inaccurate: the standard unsigned-to-signed conversion for out-of-range 
values is implementation-defined (and the language still does not universally 
mandate two’s-complement). Please reword this comment to reflect the actual 
guarantee (or lack thereof), or explicitly state/verify the project’s 
assumption (and/or switch to a defined mechanism like `std::bit_cast` if you 
want bit-preserving behavior).



##########
src/iceberg/util/truncate_util.h:
##########
@@ -82,12 +83,20 @@ class ICEBERG_EXPORT TruncateUtils {
 
   /// \brief Truncate an integer v, either int32_t or int64_t, to v - (v % W).
   ///
-  /// The remainder, v % W, must be positive. For languages where % can 
produce negative
-  /// values, the correct truncate function is: v - (((v % W) + W) % W)
+  /// W must be positive. Since C++ can produce a negative remainder, add W 
only
+  /// when v % W is negative to normalize it to [0, W). This addition cannot 
overflow.
+  /// The final subtraction uses unsigned arithmetic to avoid signed-overflow 
undefined
+  /// behavior when the truncated value is below T's minimum. Conversion back 
to T
+  /// preserves two's-complement wraparound (defined since C++20).
   template <typename T>
     requires std::is_same_v<T, int32_t> || std::is_same_v<T, int64_t>
   static inline T TruncateInteger(T v, int32_t W) {
-    return v - (((v % W) + W) % W);
+    using U = std::make_unsigned_t<T>;
+    T remainder = v % W;
+    if (remainder < 0) {
+      remainder += W;
+    }
+    return static_cast<T>(static_cast<U>(v) - static_cast<U>(remainder));

Review Comment:
   `static_cast<T>(unsigned_value)` is implementation-defined when the unsigned 
result is not representable in `T` (which is exactly what happens for cases 
like `min - remainder`). If the goal is deterministic two’s-complement 
wraparound, consider converting back to `T` in a fully specified way (e.g., 
compute `U truncated = ...;` and use `std::bit_cast<T>(truncated)`), or enforce 
the required behavior with an explicit compile-time assumption/guard (and 
document it). This avoids relying on implementation-defined unsigned-to-signed 
narrowing.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to