Skip to content

Commit 25c375f

Browse files
committed
better support for u256 and i256
1 parent bbd3e9e commit 25c375f

File tree

4 files changed

+169
-58
lines changed

4 files changed

+169
-58
lines changed

libm-test/tests/u256.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,13 @@ fn mp_u256_add() {
111111
let y = random_u256(&mut rng);
112112
assign_bigint(&mut bx, x);
113113
assign_bigint(&mut by, y);
114-
let actual = x + y;
114+
let actual = if u256::MAX - x >= y {
115+
x + y
116+
} else {
117+
// otherwise (u256::MAX - x) < y, so the wrapped result is
118+
// (x + y) - (u256::MAX + 1) == y - (u256::MAX - x) - 1
119+
y - (u256::MAX - x) - 1_u128.widen()
120+
};
115121
bx += &by;
116122
check_one(|| hexu(x), || Some(hexu(y)), actual, &mut bx);
117123
}

libm/src/math/support/big.rs

Lines changed: 79 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ const U128_LO_MASK: u128 = u64::MAX as u128;
1111

1212
/// A 256-bit unsigned integer represented as two 128-bit native-endian limbs.
1313
#[allow(non_camel_case_types)]
14-
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
14+
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
1515
pub struct u256 {
16-
pub lo: u128,
1716
pub hi: u128,
17+
pub lo: u128,
1818
}
1919

2020
impl u256 {
@@ -28,17 +28,17 @@ impl u256 {
2828
pub fn signed(self) -> i256 {
2929
i256 {
3030
lo: self.lo,
31-
hi: self.hi,
31+
hi: self.hi as i128,
3232
}
3333
}
3434
}
3535

3636
/// A 256-bit signed integer represented as two 128-bit native-endian limbs.
3737
#[allow(non_camel_case_types)]
38-
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
38+
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
3939
pub struct i256 {
40+
pub hi: i128,
4041
pub lo: u128,
41-
pub hi: u128,
4242
}
4343

4444
impl i256 {
@@ -47,7 +47,7 @@ impl i256 {
4747
pub fn unsigned(self) -> u256 {
4848
u256 {
4949
lo: self.lo,
50-
hi: self.hi,
50+
hi: self.hi as u128,
5151
}
5252
}
5353
}
@@ -73,17 +73,17 @@ impl MinInt for i256 {
7373

7474
type Unsigned = u256;
7575

76-
const SIGNED: bool = false;
76+
const SIGNED: bool = true;
7777
const BITS: u32 = 256;
7878
const ZERO: Self = Self { lo: 0, hi: 0 };
7979
const ONE: Self = Self { lo: 1, hi: 0 };
8080
const MIN: Self = Self {
81-
lo: 0,
82-
hi: 1 << 127,
81+
lo: u128::MIN,
82+
hi: i128::MIN,
8383
};
8484
const MAX: Self = Self {
8585
lo: u128::MAX,
86-
hi: u128::MAX >> 1,
86+
hi: i128::MAX,
8787
};
8888
}
8989

@@ -109,60 +109,86 @@ macro_rules! impl_common {
109109
}
110110
}
111111

112-
impl ops::Shl<u32> for $ty {
112+
impl ops::Add<Self> for $ty {
113113
type Output = Self;
114114

115-
fn shl(self, _rhs: u32) -> Self::Output {
116-
unimplemented!("only used to meet trait bounds")
115+
fn add(self, rhs: Self) -> Self::Output {
116+
let (lo, carry) = self.lo.overflowing_add(rhs.lo);
117+
let (hi, of) = Int::carrying_add(self.hi, rhs.hi, carry);
118+
debug_assert!(!of, "attempt to add with overflow");
119+
Self { lo, hi }
117120
}
118121
}
119-
};
120-
}
121122

122-
impl_common!(i256);
123-
impl_common!(u256);
123+
impl ops::Sub<Self> for $ty {
124+
type Output = Self;
124125

125-
impl ops::Add<Self> for u256 {
126-
type Output = Self;
126+
fn sub(self, rhs: Self) -> Self::Output {
127+
let (lo, borrow) = self.lo.overflowing_sub(rhs.lo);
128+
let (hi, of) = Int::borrowing_sub(self.hi, rhs.hi, borrow);
129+
debug_assert!(!of, "attempt to subtract with overflow");
130+
Self { lo, hi }
131+
}
132+
}
127133

128-
fn add(self, rhs: Self) -> Self::Output {
129-
let (lo, carry) = self.lo.overflowing_add(rhs.lo);
130-
let hi = self.hi.wrapping_add(carry as u128).wrapping_add(rhs.hi);
134+
impl ops::Shl<u32> for $ty {
135+
type Output = Self;
131136

132-
Self { lo, hi }
133-
}
134-
}
137+
fn shl(mut self, rhs: u32) -> Self::Output {
138+
debug_assert!(rhs < Self::BITS, "attempt to shift left with overflow");
135139

136-
impl ops::Shr<u32> for u256 {
137-
type Output = Self;
140+
let half_bits = Self::BITS / 2;
141+
let low_mask = half_bits - 1;
142+
let s = rhs & low_mask;
138143

139-
fn shr(mut self, rhs: u32) -> Self::Output {
140-
debug_assert!(rhs < Self::BITS, "attempted to shift right with overflow");
141-
if rhs >= Self::BITS {
142-
return Self::ZERO;
143-
}
144+
let lo = self.lo;
145+
let hi = self.hi;
144146

145-
if rhs == 0 {
146-
return self;
147-
}
147+
self.lo = lo << s;
148148

149-
if rhs < 128 {
150-
self.lo >>= rhs;
151-
self.lo |= self.hi << (128 - rhs);
152-
} else {
153-
self.lo = self.hi >> (rhs - 128);
149+
if rhs & half_bits == 0 {
150+
self.hi = (lo >> (low_mask ^ s) >> 1) as _;
151+
self.hi |= hi << s;
152+
} else {
153+
self.hi = self.lo as _;
154+
self.lo = 0;
155+
}
156+
self
157+
}
154158
}
155159

156-
if rhs < 128 {
157-
self.hi >>= rhs;
158-
} else {
159-
self.hi = 0;
160-
}
160+
impl ops::Shr<u32> for $ty {
161+
type Output = Self;
161162

162-
self
163-
}
163+
fn shr(mut self, rhs: u32) -> Self::Output {
164+
debug_assert!(rhs < Self::BITS, "attempt to shift right with overflow");
165+
166+
let half_bits = Self::BITS / 2;
167+
let low_mask = half_bits - 1;
168+
let s = rhs & low_mask;
169+
170+
let lo = self.lo;
171+
let hi = self.hi;
172+
173+
self.hi = hi >> s;
174+
175+
#[allow(unused_comparisons)]
176+
if rhs & half_bits == 0 {
177+
self.lo = (hi << (low_mask ^ s) << 1) as _;
178+
self.lo |= lo >> s;
179+
} else {
180+
self.lo = self.hi as _;
181+
self.hi = if hi < 0 { !0 } else { 0 };
182+
}
183+
self
184+
}
185+
}
186+
};
164187
}
165188

189+
impl_common!(i256);
190+
impl_common!(u256);
191+
166192
impl HInt for u128 {
167193
type D = u256;
168194

@@ -200,19 +226,18 @@ impl HInt for u128 {
200226
}
201227

202228
fn widen_hi(self) -> Self::D {
203-
self.widen() << <Self as MinInt>::BITS
229+
u256 { lo: 0, hi: self }
204230
}
205231
}
206232

207233
impl HInt for i128 {
208234
type D = i256;
209235

210236
fn widen(self) -> Self::D {
211-
let mut ret = self.unsigned().zero_widen().signed();
212-
if self.is_negative() {
213-
ret.hi = u128::MAX;
237+
i256 {
238+
lo: self as u128,
239+
hi: if self < 0 { -1 } else { 0 },
214240
}
215-
ret
216241
}
217242

218243
fn zero_widen(self) -> Self::D {
@@ -228,7 +253,7 @@ impl HInt for i128 {
228253
}
229254

230255
fn widen_hi(self) -> Self::D {
231-
self.widen() << <Self as MinInt>::BITS
256+
i256 { lo: 0, hi: self }
232257
}
233258
}
234259

@@ -252,6 +277,6 @@ impl DInt for i256 {
252277
}
253278

254279
fn hi(self) -> Self::H {
255-
self.hi as i128
280+
self.hi
256281
}
257282
}

libm/src/math/support/big/tests.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ fn widen_i128() {
3636
(LOHI_SPLIT as i128).widen(),
3737
i256 {
3838
lo: LOHI_SPLIT,
39-
hi: u128::MAX
39+
hi: -1,
4040
}
4141
);
4242
assert_eq!((-1i128).zero_widen().unsigned(), (u128::MAX).widen());
@@ -275,3 +275,64 @@ fn shr_u256_overflow() {
275275
assert_eq!(u256::MAX >> 257, u256::ZERO);
276276
assert_eq!(u256::MAX >> u32::MAX, u256::ZERO);
277277
}
278+
279+
#[test]
280+
fn u256_ord() {
281+
let _1 = u256::ONE;
282+
let _2 = _1 + _1;
283+
for x in u8::MIN..u8::MAX {
284+
let y = x + 1;
285+
let wx = (x as u128).widen_hi();
286+
let wy = (y as u128).widen_hi();
287+
assert!([wx, wx + _1, wx + _2, wy, wy + _1, wy + _2].is_sorted());
288+
}
289+
}
290+
#[test]
291+
fn i256_ord() {
292+
let _1 = i256::ONE;
293+
let _2 = _1 + _1;
294+
for x in i8::MIN..i8::MAX {
295+
let y = x + 1;
296+
let wx = (x as i128).widen_hi();
297+
let wy = (y as i128).widen_hi();
298+
assert!([wx, wx + _1, wx + _2, wy - _2, wy - _1, wy].is_sorted());
299+
}
300+
}
301+
302+
#[test]
303+
fn u256_shifts() {
304+
let _1 = u256::ONE;
305+
for k in 0..255 {
306+
let x = _1 << k;
307+
let x2 = _1 << (k + 1);
308+
assert!(x < x2);
309+
assert_eq!(x << 1, x2);
310+
assert_eq!(x + x, x2);
311+
assert_eq!(x >> k, _1);
312+
assert_eq!(x2 >> (k + 1), _1);
313+
}
314+
}
315+
#[test]
316+
fn i256_shifts() {
317+
let _1 = i256::ONE;
318+
for k in 0..254 {
319+
let x = _1 << k;
320+
let x2 = _1 << (k + 1);
321+
assert!(x < x2);
322+
assert_eq!(x << 1, x2);
323+
assert_eq!(x + x, x2);
324+
assert_eq!(x >> k, _1);
325+
assert_eq!(x2 >> (k + 1), _1);
326+
}
327+
328+
let min = _1 << 255;
329+
assert_eq!(min, i256::MIN);
330+
let mut x = min;
331+
for k in 0..255 {
332+
assert_eq!(x, min >> k);
333+
let y = x >> 1;
334+
assert_eq!(y + y, x);
335+
assert!(x < y);
336+
x = y;
337+
}
338+
}

libm/src/math/support/int_traits.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ pub trait Int:
3737
+ fmt::Display
3838
+ fmt::Binary
3939
+ fmt::LowerHex
40-
+ PartialEq
41-
+ PartialOrd
4240
+ ops::AddAssign
4341
+ ops::SubAssign
4442
+ ops::MulAssign
@@ -102,7 +100,10 @@ pub trait Int:
102100
fn rotate_left(self, other: u32) -> Self;
103101
fn overflowing_add(self, other: Self) -> (Self, bool);
104102
fn overflowing_sub(self, other: Self) -> (Self, bool);
103+
fn carrying_add(self, other: Self, carry: bool) -> (Self, bool);
104+
fn borrowing_sub(self, other: Self, borrow: bool) -> (Self, bool);
105105
fn leading_zeros(self) -> u32;
106+
fn trailing_zeros(self) -> u32;
106107
fn ilog2(self) -> u32;
107108
}
108109

@@ -168,12 +169,30 @@ macro_rules! int_impl_common {
168169
<Self>::leading_zeros(self)
169170
}
170171

172+
fn trailing_zeros(self) -> u32 {
173+
<Self>::trailing_zeros(self)
174+
}
175+
171176
fn ilog2(self) -> u32 {
172177
// On our older MSRV, this resolves to the trait method. Which won't actually work,
173178
// but this is only called behind other gates.
174179
#[allow(clippy::incompatible_msrv)]
175180
<Self>::ilog2(self)
176181
}
182+
183+
fn carrying_add(self, other: Self, carry: bool) -> (Self, bool) {
184+
let (ab, of1) = self.overflowing_add(other);
185+
let (abc, of2) = ab.overflowing_add(Self::from_bool(carry));
186+
// `of1 && of2` is possible with signed integers if a negative sum
187+
// overflows to `MAX` and adding the carry overflows again back to `MIN`
188+
(abc, of1 ^ of2)
189+
}
190+
191+
fn borrowing_sub(self, other: Self, borrow: bool) -> (Self, bool) {
192+
let (ab, of1) = self.overflowing_sub(other);
193+
let (abc, of2) = ab.overflowing_sub(Self::from_bool(borrow));
194+
(abc, of1 ^ of2)
195+
}
177196
};
178197
}
179198

0 commit comments

Comments
 (0)