Skip to content

Commit 563c682

Browse files
committed
[stdlib] Further optimize heterogeneous binary integer comparison
1 parent 9d7b1c5 commit 563c682

File tree

1 file changed

+36
-48
lines changed

1 file changed

+36
-48
lines changed

stdlib/public/core/Integers.swift

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,32 +1658,26 @@ extension BinaryInteger {
16581658
public static func == <
16591659
Other: BinaryInteger
16601660
>(lhs: Self, rhs: Other) -> Bool {
1661-
let rhs_ = Self(truncatingIfNeeded: rhs)
1662-
// Is `rhs` representable as a value of `Self` type? In other words, does
1663-
// the bit pattern conversion above preserve the value of `rhs`?
1664-
//
1665-
// To find out the answer, we see if the value roundtrips by bit pattern
1666-
// conversion back to `Self` [1], and we also check that the original bit
1667-
// pattern conversion doesn't change the sign [2].
1668-
if Other(truncatingIfNeeded: rhs_) == rhs /* [1] */
1669-
&& (rhs < (0 as Other)) == (rhs_ < (0 as Self)) /* [2] */ {
1670-
return lhs == rhs_
1661+
// Use bit pattern conversion to widen the comparand with smaller bit width.
1662+
if Self.isSigned == Other.isSigned {
1663+
return lhs.bitWidth >= rhs.bitWidth ?
1664+
lhs == Self(truncatingIfNeeded: rhs) :
1665+
Other(truncatingIfNeeded: lhs) == rhs
16711666
}
1672-
1673-
let lhs_ = Other(truncatingIfNeeded: lhs)
1674-
// Is `lhs` representable as a value of `Other` type?
1675-
if Self(truncatingIfNeeded: lhs_) == lhs
1676-
&& (lhs < (0 as Self)) == (lhs_ < (0 as Other)) {
1677-
return lhs_ == rhs
1667+
// If `Self` is signed but `Other` is unsigned, then we have to
1668+
// be a little more careful about widening, since:
1669+
// (1) a fixed-width signed type can't represent the largest values of
1670+
// a fixed-width unsigned type of equal bit width; and
1671+
// (2) an unsigned type (obviously) can't represent a negative value.
1672+
if Self.isSigned {
1673+
return lhs.bitWidth > rhs.bitWidth ? // (1)
1674+
lhs == Self(truncatingIfNeeded: rhs) :
1675+
lhs < (0 as Self) ? false : Other(truncatingIfNeeded: lhs) == rhs // (2)
16781676
}
1679-
1680-
// If we're here, then either:
1681-
// - `Self` is signed and fixed-width, `Other` is unsigned,
1682-
// `lhs` is negative, and `rhs` is greater than `Self.max`; or
1683-
// - `Other` is signed and fixed-width, `Self` is unsigned,
1684-
// `rhs` is negative, and `lhs` is greater than `Other.max`.
1685-
// Thus, `lhs != rhs`.
1686-
return false
1677+
// Analogous reasoning applies if `Other` is signed but `Self` is not.
1678+
return lhs.bitWidth < rhs.bitWidth ?
1679+
Other(truncatingIfNeeded: lhs) == rhs :
1680+
rhs < (0 as Other) ? false : lhs == Self(truncatingIfNeeded: rhs)
16871681
}
16881682

16891683
/// Returns a Boolean value indicating whether the two given values are not
@@ -1723,32 +1717,26 @@ extension BinaryInteger {
17231717
/// - rhs: Another integer to compare.
17241718
@_transparent
17251719
public static func < <Other: BinaryInteger>(lhs: Self, rhs: Other) -> Bool {
1726-
let rhs_ = Self(truncatingIfNeeded: rhs)
1727-
// Is `rhs` representable as a value of `Self` type? In other words, does
1728-
// the bitcast operation above preserve the value of `rhs`?
1729-
//
1730-
// To find out the answer, we see if the value roundtrips by bitcasting back
1731-
// to `Self` [1], and we also check that bitcasting doesn't change the sign
1732-
// [2].
1733-
if Other(truncatingIfNeeded: rhs_) == rhs /* [1] */
1734-
&& (rhs < (0 as Other)) == (rhs_ < (0 as Self)) /* [2] */ {
1735-
return lhs < rhs_
1720+
// Use bit pattern conversion to widen the comparand with smaller bit width.
1721+
if Self.isSigned == Other.isSigned {
1722+
return lhs.bitWidth >= rhs.bitWidth ?
1723+
lhs < Self(truncatingIfNeeded: rhs) :
1724+
Other(truncatingIfNeeded: lhs) < rhs
17361725
}
1737-
1738-
let lhs_ = Other(truncatingIfNeeded: lhs)
1739-
// Is `lhs` representable as a value of `Other` type?
1740-
if Self(truncatingIfNeeded: lhs_) == lhs
1741-
&& (lhs < (0 as Self)) == (lhs_ < (0 as Other)) {
1742-
return lhs_ < rhs
1726+
// If `Self` is signed but `Other` is unsigned, then we have to
1727+
// be a little more careful about widening, since:
1728+
// (1) a fixed-width signed type can't represent the largest values of
1729+
// a fixed-width unsigned type of equal bit width; and
1730+
// (2) an unsigned type (obviously) can't represent a negative value.
1731+
if Self.isSigned {
1732+
return lhs.bitWidth > rhs.bitWidth ? // (1)
1733+
lhs < Self(truncatingIfNeeded: rhs) :
1734+
lhs < (0 as Self) ? true : Other(truncatingIfNeeded: lhs) < rhs // (2)
17431735
}
1744-
1745-
// If we're here, then either:
1746-
// - `Self` is signed and fixed-width, `Other` is unsigned,
1747-
// `lhs` is negative, and `rhs` is greater than `Self.max`; or
1748-
// - `Other` is signed and fixed-width, `Self` is unsigned,
1749-
// `rhs` is negative, and `lhs` is greater than `Other.max`.
1750-
// Thus, `lhs < rhs` if and only if `Self.isSigned`.
1751-
return Self.isSigned
1736+
// Analogous reasoning applies if `Other` is signed but `Self` is not.
1737+
return lhs.bitWidth < rhs.bitWidth ?
1738+
Other(truncatingIfNeeded: lhs) < rhs :
1739+
rhs < (0 as Other) ? false : lhs < Self(truncatingIfNeeded: rhs)
17521740
}
17531741

17541742
/// Returns a Boolean value indicating whether the value of the first

0 commit comments

Comments
 (0)