]> git.lizzy.rs Git - rust.git/commitdiff
Add bit twiddling
authorPyry Kontio <pyry.kontio@drasa.eu>
Mon, 25 May 2020 19:50:53 +0000 (04:50 +0900)
committerPyry Kontio <pyry.kontio@drasa.eu>
Mon, 25 May 2020 20:07:00 +0000 (05:07 +0900)
src/libcore/num/f32.rs
src/libcore/num/f64.rs

index 36b70587385cb18015af607b6176cb2efa42765b..538cca712ca95ee4cddc5019dd0f42afd3c79742 100644 (file)
@@ -857,7 +857,7 @@ pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
         let mut left = self.to_bits() as i32;
         let mut right = other.to_bits() as i32;
 
-        // In case of negatives, flip all the bits except the sign
+        // In case of negatives, flip all the bits expect the sign
         // to achieve a similar layout as two's complement integers
         //
         // Why does this work? IEEE 754 floats consist of three fields:
@@ -872,13 +872,15 @@ pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
         // To easily compare the floats as signed integers, we need to
         // flip the exponent and mantissa bits in case of negative numbers.
         // We effectively convert the numbers to "two's complement" form.
-        if left < 0 {
-            // i32::MAX corresponds the bit pattern of "all ones except for the sign bit"
-            left ^= i32::MAX
-        };
-        if right < 0 {
-            right ^= i32::MAX
-        };
+        //
+        // To do the flipping, we construct a mask and XOR against it.
+        // We branchlessly calculate an "all-ones expect for the sign bit"
+        // mask from negative-signed values: right shifting sign-extends
+        // the integer, so we "fill" the mask with sign bits, and then
+        // convert to unsigned to push one more zero bit.
+        // On positive values, the mask is all zeros, so it's a no-op.
+        left ^= (((left >> 31) as u32) >> 1) as i32;
+        right ^= (((right >> 31) as u32) >> 1) as i32;
 
         left.cmp(&right)
     }
index 61711df3c65ef1150a69e85dc4fd1344145d07e1..b3ebceb77c2e5e05240930b3549e40d52c19dace 100644 (file)
@@ -886,13 +886,15 @@ pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
         // To easily compare the floats as signed integers, we need to
         // flip the exponent and mantissa bits in case of negative numbers.
         // We effectively convert the numbers to "two's complement" form.
-        if left < 0 {
-            // i64::MAX corresponds the bit pattern of "all ones expect for the sign bit"
-            left ^= i64::MAX
-        };
-        if right < 0 {
-            right ^= i64::MAX
-        };
+        //
+        // To do the flipping, we construct a mask and XOR against it.
+        // We branchlessly calculate an "all-ones expect for the sign bit"
+        // mask from negative-signed values: right shifting sign-extends
+        // the integer, so we "fill" the mask with sign bits, and then
+        // convert to unsigned to push one more zero bit.
+        // On positive values, the mask is all zeros, so it's a no-op.
+        left ^= (((left >> 63) as u64) >> 1) as i64;
+        right ^= (((right >> 63) as u64) >> 1) as i64;
 
         left.cmp(&right)
     }