Skip to content

Commit cdc2f08

Browse files
committed
Expanded power of two opt
1 parent 3c0b2d3 commit cdc2f08

File tree

2 files changed

+229
-121
lines changed

2 files changed

+229
-121
lines changed

library/core/src/num/int_macros.rs

+129-62
Original file line numberDiff line numberDiff line change
@@ -901,26 +901,46 @@ macro_rules! int_impl {
901901
#[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]
902902
#[must_use = "this returns the result of the operation, \
903903
without modifying the original"]
904+
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
904905
#[inline]
905906
pub const fn checked_pow(self, mut exp: u32) -> Option<Self> {
906-
if exp == 0 {
907-
return Some(1);
908-
}
909-
let mut base = self;
910-
let mut acc: Self = 1;
907+
// SAFETY: This path has the same behavior as the other.
908+
if unsafe { intrinsics::is_val_statically_known(self) }
909+
&& self.unsigned_abs().is_power_of_two()
910+
{
911+
// SAFETY: We just checked this is a power of two. and above zero.
912+
let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 };
913+
let num_shl = power_used.saturating_mul(exp);
914+
let res = try_opt!((1 as Self).checked_shl(num_shl));
915+
916+
let sign = self.is_negative() && exp & 1 != 0;
917+
if !sign && res == Self::MIN {
918+
None
919+
} else if sign {
920+
Some(res.wrapping_neg())
921+
} else {
922+
Some(res)
923+
}
924+
} else {
925+
if exp == 0 {
926+
return Some(1);
927+
}
928+
let mut base = self;
929+
let mut acc: Self = 1;
911930

912-
while exp > 1 {
913-
if (exp & 1) == 1 {
914-
acc = try_opt!(acc.checked_mul(base));
931+
while exp > 1 {
932+
if (exp & 1) == 1 {
933+
acc = try_opt!(acc.checked_mul(base));
934+
}
935+
exp /= 2;
936+
base = try_opt!(base.checked_mul(base));
915937
}
916-
exp /= 2;
917-
base = try_opt!(base.checked_mul(base));
938+
// since exp!=0, finally the exp must be 1.
939+
// Deal with the final bit of the exponent separately, since
940+
// squaring the base afterwards is not necessary and may cause a
941+
// needless overflow.
942+
acc.checked_mul(base)
918943
}
919-
// since exp!=0, finally the exp must be 1.
920-
// Deal with the final bit of the exponent separately, since
921-
// squaring the base afterwards is not necessary and may cause a
922-
// needless overflow.
923-
acc.checked_mul(base)
924944
}
925945

926946
/// Returns the square root of the number, rounded down.
@@ -1537,27 +1557,48 @@ macro_rules! int_impl {
15371557
#[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]
15381558
#[must_use = "this returns the result of the operation, \
15391559
without modifying the original"]
1560+
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
15401561
#[inline]
15411562
pub const fn wrapping_pow(self, mut exp: u32) -> Self {
1542-
if exp == 0 {
1543-
return 1;
1544-
}
1545-
let mut base = self;
1546-
let mut acc: Self = 1;
1563+
// SAFETY: This path has the same behavior as the other.
1564+
if unsafe { intrinsics::is_val_statically_known(self) }
1565+
&& self.unsigned_abs().is_power_of_two()
1566+
{
1567+
// SAFETY: We just checked this is a power of two. and above zero.
1568+
let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 };
1569+
let num_shl = power_used.saturating_mul(exp);
1570+
let res = match (1 as Self).checked_shl(num_shl) {
1571+
Some(x) => x,
1572+
None => 0
1573+
};
15471574

1548-
while exp > 1 {
1549-
if (exp & 1) == 1 {
1550-
acc = acc.wrapping_mul(base);
1575+
let sign = self.is_negative() && exp & 1 != 0;
1576+
if sign {
1577+
res.wrapping_neg()
1578+
} else {
1579+
res
15511580
}
1552-
exp /= 2;
1553-
base = base.wrapping_mul(base);
1554-
}
1581+
} else {
1582+
if exp == 0 {
1583+
return 1;
1584+
}
1585+
let mut base = self;
1586+
let mut acc: Self = 1;
15551587

1556-
// since exp!=0, finally the exp must be 1.
1557-
// Deal with the final bit of the exponent separately, since
1558-
// squaring the base afterwards is not necessary and may cause a
1559-
// needless overflow.
1560-
acc.wrapping_mul(base)
1588+
while exp > 1 {
1589+
if (exp & 1) == 1 {
1590+
acc = acc.wrapping_mul(base);
1591+
}
1592+
exp /= 2;
1593+
base = base.wrapping_mul(base);
1594+
}
1595+
1596+
// since exp!=0, finally the exp must be 1.
1597+
// Deal with the final bit of the exponent separately, since
1598+
// squaring the base afterwards is not necessary and may cause a
1599+
// needless overflow.
1600+
acc.wrapping_mul(base)
1601+
}
15611602
}
15621603

15631604
/// Calculates `self` + `rhs`
@@ -2039,36 +2080,58 @@ macro_rules! int_impl {
20392080
#[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]
20402081
#[must_use = "this returns the result of the operation, \
20412082
without modifying the original"]
2083+
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
20422084
#[inline]
20432085
pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) {
2044-
if exp == 0 {
2045-
return (1,false);
2046-
}
2047-
let mut base = self;
2048-
let mut acc: Self = 1;
2049-
let mut overflown = false;
2050-
// Scratch space for storing results of overflowing_mul.
2051-
let mut r;
2052-
2053-
while exp > 1 {
2054-
if (exp & 1) == 1 {
2055-
r = acc.overflowing_mul(base);
2056-
acc = r.0;
2086+
// SAFETY: This path has the same behavior as the other.
2087+
if unsafe { intrinsics::is_val_statically_known(self) }
2088+
&& self.unsigned_abs().is_power_of_two()
2089+
{
2090+
// SAFETY: We just checked this is a power of two. and above zero.
2091+
let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 };
2092+
let num_shl = power_used.saturating_mul(exp);
2093+
let res = match (1 as Self).checked_shl(num_shl) {
2094+
Some(x) => x,
2095+
None => 0
2096+
};
2097+
2098+
let sign = self.is_negative() && exp & 1 != 0;
2099+
let overflow = res == 0 || (sign && res == Self::MIN);
2100+
if sign {
2101+
(res.wrapping_neg(), overflow)
2102+
} else {
2103+
(res, overflow)
2104+
}
2105+
} else {
2106+
if exp == 0 {
2107+
return (1,false);
2108+
}
2109+
let mut base = self;
2110+
let mut acc: Self = 1;
2111+
let mut overflown = false;
2112+
// Scratch space for storing results of overflowing_mul.
2113+
let mut r;
2114+
2115+
while exp > 1 {
2116+
if (exp & 1) == 1 {
2117+
r = acc.overflowing_mul(base);
2118+
acc = r.0;
2119+
overflown |= r.1;
2120+
}
2121+
exp /= 2;
2122+
r = base.overflowing_mul(base);
2123+
base = r.0;
20572124
overflown |= r.1;
20582125
}
2059-
exp /= 2;
2060-
r = base.overflowing_mul(base);
2061-
base = r.0;
2062-
overflown |= r.1;
2063-
}
20642126

2065-
// since exp!=0, finally the exp must be 1.
2066-
// Deal with the final bit of the exponent separately, since
2067-
// squaring the base afterwards is not necessary and may cause a
2068-
// needless overflow.
2069-
r = acc.overflowing_mul(base);
2070-
r.1 |= overflown;
2071-
r
2127+
// since exp!=0, finally the exp must be 1.
2128+
// Deal with the final bit of the exponent separately, since
2129+
// squaring the base afterwards is not necessary and may cause a
2130+
// needless overflow.
2131+
r = acc.overflowing_mul(base);
2132+
r.1 |= overflown;
2133+
r
2134+
}
20722135
}
20732136

20742137
/// Raises self to the power of `exp`, using exponentiation by squaring.
@@ -2086,30 +2149,34 @@ macro_rules! int_impl {
20862149
#[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]
20872150
#[must_use = "this returns the result of the operation, \
20882151
without modifying the original"]
2152+
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
20892153
#[inline]
20902154
#[rustc_inherit_overflow_checks]
20912155
#[track_caller] // Hides the hackish overflow check for powers of two.
2092-
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
20932156
pub const fn pow(self, mut exp: u32) -> Self {
20942157
// SAFETY: This path has the same behavior as the other.
20952158
if unsafe { intrinsics::is_val_statically_known(self) }
2096-
&& self > 0
2097-
&& (self & (self - 1) == 0)
2159+
&& self.unsigned_abs().is_power_of_two()
20982160
{
20992161
// SAFETY: We just checked this is a power of two. and above zero.
2100-
let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 };
2162+
let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 };
21012163
let num_shl = power_used.saturating_mul(exp);
21022164
let res = match (1 as Self).checked_shl(num_shl) {
21032165
Some(x) => x,
21042166
None => 0
21052167
};
21062168

2169+
let sign = self.is_negative() && exp & 1 != 0;
21072170
#[allow(arithmetic_overflow)]
2108-
if res <= 0 {
2171+
if res == 0 || (!sign && res == Self::MIN) {
21092172
// So it panics.
21102173
_ = Self::MAX * Self::MAX;
21112174
}
2112-
res
2175+
if sign {
2176+
res.wrapping_neg()
2177+
} else {
2178+
res
2179+
}
21132180
} else {
21142181
if exp == 0 {
21152182
return 1;

0 commit comments

Comments
 (0)