Skip to content

Commit 24a93b6

Browse files
authored
Rework specialization for uint256 (#321)
Use `if constexpr` instead of function overloading to specialize implementations of some operators for uint256.
1 parent 5a64a5f commit 24a93b6

File tree

1 file changed

+86
-92
lines changed

1 file changed

+86
-92
lines changed

include/intx/intx.hpp

+86-92
Original file line numberDiff line numberDiff line change
@@ -984,22 +984,22 @@ struct uint
984984

985985
using uint256 = uint<256>;
986986

987-
inline constexpr bool operator<(const uint256& x, const uint256& y) noexcept
988-
{
989-
auto xp = uint128{x[2], x[3]};
990-
auto yp = uint128{y[2], y[3]};
991-
if (xp == yp)
992-
{
993-
xp = uint128{x[0], x[1]};
994-
yp = uint128{y[0], y[1]};
995-
}
996-
return xp < yp;
997-
}
998-
999987
template <unsigned N>
1000988
inline constexpr bool operator<(const uint<N>& x, const uint<N>& y) noexcept
1001989
{
1002-
return subc(x, y).carry;
990+
if constexpr (N == 256)
991+
{
992+
auto xp = uint128{x[2], x[3]};
993+
auto yp = uint128{y[2], y[3]};
994+
if (xp == yp)
995+
{
996+
xp = uint128{x[0], x[1]};
997+
yp = uint128{y[0], y[1]};
998+
}
999+
return xp < yp;
1000+
}
1001+
else
1002+
return subc(x, y).carry;
10031003
}
10041004

10051005
template <unsigned N, typename T>
@@ -1092,109 +1092,103 @@ inline constexpr bool slt(const uint<N>& x, const uint<N>& y) noexcept
10921092
return ((x_neg ^ y_neg) != 0) ? x_neg : x < y;
10931093
}
10941094

1095-
1096-
inline constexpr uint256 operator<<(const uint256& x, uint64_t shift) noexcept
1097-
{
1098-
if (INTX_UNLIKELY(shift >= uint256::num_bits))
1099-
return 0;
1100-
1101-
constexpr auto num_bits = uint256::num_bits;
1102-
constexpr auto half_bits = num_bits / 2;
1103-
1104-
const auto xlo = uint128{x[0], x[1]};
1105-
1106-
if (shift < half_bits)
1107-
{
1108-
const auto lo = xlo << shift;
1109-
1110-
const auto xhi = uint128{x[2], x[3]};
1111-
1112-
// Find the part moved from lo to hi.
1113-
// The shift right here can be invalid:
1114-
// for shift == 0 => rshift == half_bits.
1115-
// Split it into 2 valid shifts by (rshift - 1) and 1.
1116-
const auto rshift = half_bits - shift;
1117-
const auto lo_overflow = (xlo >> (rshift - 1)) >> 1;
1118-
const auto hi = (xhi << shift) | lo_overflow;
1119-
return {lo[0], lo[1], hi[0], hi[1]};
1120-
}
1121-
1122-
const auto hi = xlo << (shift - half_bits);
1123-
return {0, 0, hi[0], hi[1]};
1124-
}
1125-
11261095
template <unsigned N>
11271096
inline constexpr uint<N> operator<<(const uint<N>& x, uint64_t shift) noexcept
11281097
{
1129-
if (INTX_UNLIKELY(shift >= uint<N>::num_bits))
1098+
if (shift >= uint<N>::num_bits) [[unlikely]]
11301099
return 0;
11311100

1132-
constexpr auto word_bits = sizeof(uint64_t) * 8;
1133-
1134-
const auto s = shift % word_bits;
1135-
const auto skip = static_cast<size_t>(shift / word_bits);
1136-
1137-
uint<N> r;
1138-
uint64_t carry = 0;
1139-
for (size_t i = 0; i < (uint<N>::num_words - skip); ++i)
1101+
if constexpr (N == 256)
11401102
{
1141-
r[i + skip] = (x[i] << s) | carry;
1142-
carry = (x[i] >> (word_bits - s - 1)) >> 1;
1143-
}
1144-
return r;
1145-
}
1103+
constexpr auto half_bits = uint<N>::num_bits / 2;
11461104

1105+
const auto xlo = uint128{x[0], x[1]};
11471106

1148-
inline constexpr uint256 operator>>(const uint256& x, uint64_t shift) noexcept
1149-
{
1150-
if (INTX_UNLIKELY(shift >= uint256::num_bits))
1151-
return 0;
1152-
1153-
constexpr auto num_bits = uint256::num_bits;
1154-
constexpr auto half_bits = num_bits / 2;
1155-
1156-
const auto xhi = uint128{x[2], x[3]};
1107+
if (shift < half_bits)
1108+
{
1109+
const auto lo = xlo << shift;
1110+
1111+
const auto xhi = uint128{x[2], x[3]};
1112+
1113+
// Find the part moved from lo to hi.
1114+
// The shift right here can be invalid:
1115+
// for shift == 0 => rshift == half_bits.
1116+
// Split it into 2 valid shifts by (rshift - 1) and 1.
1117+
const auto rshift = half_bits - shift;
1118+
const auto lo_overflow = (xlo >> (rshift - 1)) >> 1;
1119+
const auto hi = (xhi << shift) | lo_overflow;
1120+
return {lo[0], lo[1], hi[0], hi[1]};
1121+
}
11571122

1158-
if (shift < half_bits)
1123+
const auto hi = xlo << (shift - half_bits);
1124+
return {0, 0, hi[0], hi[1]};
1125+
}
1126+
else
11591127
{
1160-
const auto hi = xhi >> shift;
1128+
constexpr auto word_bits = sizeof(uint64_t) * 8;
11611129

1162-
const auto xlo = uint128{x[0], x[1]};
1130+
const auto s = shift % word_bits;
1131+
const auto skip = static_cast<size_t>(shift / word_bits);
11631132

1164-
// Find the part moved from hi to lo.
1165-
// The shift left here can be invalid:
1166-
// for shift == 0 => lshift == half_bits.
1167-
// Split it into 2 valid shifts by (lshift - 1) and 1.
1168-
const auto lshift = half_bits - shift;
1169-
const auto hi_overflow = (xhi << (lshift - 1)) << 1;
1170-
const auto lo = (xlo >> shift) | hi_overflow;
1171-
return {lo[0], lo[1], hi[0], hi[1]};
1133+
uint<N> r;
1134+
uint64_t carry = 0;
1135+
for (size_t i = 0; i < (uint<N>::num_words - skip); ++i)
1136+
{
1137+
r[i + skip] = (x[i] << s) | carry;
1138+
carry = (x[i] >> (word_bits - s - 1)) >> 1;
1139+
}
1140+
return r;
11721141
}
1173-
1174-
const auto lo = xhi >> (shift - half_bits);
1175-
return {lo[0], lo[1], 0, 0};
11761142
}
11771143

11781144
template <unsigned N>
11791145
inline constexpr uint<N> operator>>(const uint<N>& x, uint64_t shift) noexcept
11801146
{
1181-
if (INTX_UNLIKELY(shift >= uint<N>::num_bits))
1147+
if (shift >= uint<N>::num_bits) [[unlikely]]
11821148
return 0;
11831149

1184-
constexpr auto num_words = uint<N>::num_words;
1185-
constexpr auto word_bits = sizeof(uint64_t) * 8;
1150+
if constexpr (N == 256)
1151+
{
1152+
constexpr auto half_bits = uint<N>::num_bits / 2;
11861153

1187-
const auto s = shift % word_bits;
1188-
const auto skip = static_cast<size_t>(shift / word_bits);
1154+
const auto xhi = uint128{x[2], x[3]};
11891155

1190-
uint<N> r;
1191-
uint64_t carry = 0;
1192-
for (size_t i = 0; i < (num_words - skip); ++i)
1156+
if (shift < half_bits)
1157+
{
1158+
const auto hi = xhi >> shift;
1159+
1160+
const auto xlo = uint128{x[0], x[1]};
1161+
1162+
// Find the part moved from hi to lo.
1163+
// The shift left here can be invalid:
1164+
// for shift == 0 => lshift == half_bits.
1165+
// Split it into 2 valid shifts by (lshift - 1) and 1.
1166+
const auto lshift = half_bits - shift;
1167+
const auto hi_overflow = (xhi << (lshift - 1)) << 1;
1168+
const auto lo = (xlo >> shift) | hi_overflow;
1169+
return {lo[0], lo[1], hi[0], hi[1]};
1170+
}
1171+
1172+
const auto lo = xhi >> (shift - half_bits);
1173+
return {lo[0], lo[1], 0, 0};
1174+
}
1175+
else
11931176
{
1194-
r[num_words - 1 - i - skip] = (x[num_words - 1 - i] >> s) | carry;
1195-
carry = (x[num_words - 1 - i] << (word_bits - s - 1)) << 1;
1177+
constexpr auto num_words = uint<N>::num_words;
1178+
constexpr auto word_bits = sizeof(uint64_t) * 8;
1179+
1180+
const auto s = shift % word_bits;
1181+
const auto skip = static_cast<size_t>(shift / word_bits);
1182+
1183+
uint<N> r;
1184+
uint64_t carry = 0;
1185+
for (size_t i = 0; i < (num_words - skip); ++i)
1186+
{
1187+
r[num_words - 1 - i - skip] = (x[num_words - 1 - i] >> s) | carry;
1188+
carry = (x[num_words - 1 - i] << (word_bits - s - 1)) << 1;
1189+
}
1190+
return r;
11961191
}
1197-
return r;
11981192
}
11991193

12001194
template <unsigned N>

0 commit comments

Comments
 (0)