-
-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from henninglive/i128
Add support for 128-bit integers
- Loading branch information
Showing
7 changed files
with
319 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,17 @@ | ||
sudo: false | ||
|
||
language: rust | ||
|
||
rust: | ||
- nightly | ||
matrix: | ||
include: | ||
- rust: stable | ||
- rust: beta | ||
- rust: nightly | ||
env: | ||
- FEATURES="i128" | ||
- BUILD_BENCH="true" | ||
|
||
script: | ||
- cargo build --verbose --features "$FEATURES" | ||
- cargo test --verbose --features "$FEATURES" | ||
- if [ "$BUILD_BENCH" == "true" ]; then cargo bench --verbose --no-run --features "$FEATURES"; fi | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
// Copyright 2009-2016 compiler-builtins Developers | ||
// | ||
// The compiler-builtins crate is dual licensed under both the University of | ||
// Illinois "BSD-Like" license and the MIT license. As a user of this code you may | ||
// choose to use it under either license. As a contributor, you agree to allow | ||
// your code to be used under both. | ||
// | ||
// Full text of the relevant licenses is found here: | ||
// /~https://github.com/rust-lang-nursery/compiler-builtins/blob/master/LICENSE.TXT | ||
// | ||
// | ||
// | ||
// The following code is based on Rust’s [compiler-builtins crate] | ||
// (/~https://github.com/rust-lang-nursery/compiler-builtins) which | ||
// provides runtime functions for the Rust programs. The Rust | ||
// compiler will automatically link your programs against this crate. | ||
// | ||
// We copied the implementation of '__udivmodti4()' which is an intrinsic | ||
// implementing division with remainder for architectures without 128-bit integer support. | ||
// We have done this two reasons, to work around [bad optimization by LLVM] | ||
// (/~https://github.com/rust-lang/rust/issues/44545) and to allow function | ||
// inlining which doesn’t happen with the intrinsic. | ||
|
||
const BITS: u32 = 128; | ||
const BITS_HALF: u32 = 64; | ||
|
||
trait LargeInt { | ||
fn low(self) -> u64; | ||
fn high(self) -> u64; | ||
fn from_parts(low: u64, high: u64) -> Self; | ||
} | ||
|
||
trait Int { | ||
fn aborting_div(self, other: Self) -> Self; | ||
fn aborting_rem(self, other: Self) -> Self; | ||
} | ||
|
||
impl LargeInt for u128 { | ||
fn low(self) -> u64 { | ||
self as u64 | ||
} | ||
|
||
fn high(self) -> u64 { | ||
(self >> 64) as u64 | ||
} | ||
|
||
fn from_parts(low: u64, high: u64) -> u128 { | ||
low as u128 | ((high as u128) << 64) | ||
} | ||
} | ||
|
||
impl Int for u64 { | ||
fn aborting_div(self, other: u64) -> u64 { | ||
<u64>::checked_div(self, other).unwrap() | ||
} | ||
|
||
fn aborting_rem(self, other: u64) -> u64 { | ||
<u64>::checked_rem(self, other).unwrap() | ||
} | ||
} | ||
|
||
pub fn udivmodti4(n: u128, d: u128, rem: Option<&mut u128>) -> u128 { | ||
// NOTE X is unknown, K != 0 | ||
if n.high() == 0 { | ||
if d.high() == 0 { | ||
// 0 X | ||
// --- | ||
// 0 X | ||
|
||
if let Some(rem) = rem { | ||
*rem = <u128>::from(n.low().aborting_rem(d.low())); | ||
} | ||
return <u128>::from(n.low().aborting_div(d.low())) | ||
} else { | ||
// 0 X | ||
// --- | ||
// K X | ||
if let Some(rem) = rem { | ||
*rem = n; | ||
} | ||
return 0; | ||
}; | ||
} | ||
|
||
let mut sr; | ||
let mut q; | ||
let mut r; | ||
|
||
if d.low() == 0 { | ||
if d.high() == 0 { | ||
// K X | ||
// --- | ||
// 0 0 | ||
// NOTE This should be unreachable in safe Rust because the program will panic before | ||
// this intrinsic is called | ||
unreachable!(); | ||
} | ||
|
||
if n.low() == 0 { | ||
// K 0 | ||
// --- | ||
// K 0 | ||
if let Some(rem) = rem { | ||
*rem = <u128>::from_parts(0, n.high().aborting_rem(d.high())); | ||
} | ||
return <u128>::from(n.high().aborting_div(d.high())) | ||
} | ||
|
||
// K K | ||
// --- | ||
// K 0 | ||
|
||
if d.high().is_power_of_two() { | ||
if let Some(rem) = rem { | ||
*rem = <u128>::from_parts(n.low(), n.high() & (d.high() - 1)); | ||
} | ||
return <u128>::from(n.high() >> d.high().trailing_zeros()); | ||
} | ||
|
||
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); | ||
|
||
// D > N | ||
if sr > BITS_HALF - 2 { | ||
if let Some(rem) = rem { | ||
*rem = n; | ||
} | ||
return 0; | ||
} | ||
|
||
sr += 1; | ||
|
||
// 1 <= sr <= BITS_HALF - 1 | ||
q = n << (BITS - sr); | ||
r = n >> sr; | ||
} else if d.high() == 0 { | ||
// K X | ||
// --- | ||
// 0 K | ||
if d.low().is_power_of_two() { | ||
if let Some(rem) = rem { | ||
*rem = <u128>::from(n.low() & (d.low() - 1)); | ||
} | ||
|
||
if d.low() == 1 { | ||
return n; | ||
} else { | ||
let sr = d.low().trailing_zeros(); | ||
return n >> sr; | ||
}; | ||
} | ||
|
||
sr = 1 + BITS_HALF + d.low().leading_zeros() - n.high().leading_zeros(); | ||
|
||
// 2 <= sr <= u64::BITS - 1 | ||
q = n << (BITS - sr); | ||
r = n >> sr; | ||
} else { | ||
// K X | ||
// --- | ||
// K K | ||
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); | ||
|
||
// D > N | ||
if sr > BITS_HALF - 1 { | ||
if let Some(rem) = rem { | ||
*rem = n; | ||
} | ||
return 0; | ||
} | ||
|
||
sr += 1; | ||
|
||
// 1 <= sr <= BITS_HALF | ||
q = n << (BITS - sr); | ||
r = n >> sr; | ||
} | ||
|
||
// Not a special case | ||
// q and r are initialized with | ||
// q = n << (u64::BITS - sr) | ||
// r = n >> sr | ||
// 1 <= sr <= u64::BITS - 1 | ||
let mut carry = 0; | ||
|
||
// Don't use a range because they may generate references to memcpy in unoptimized code | ||
let mut i = 0; | ||
while i < sr { | ||
i += 1; | ||
|
||
// r:q = ((r:q) << 1) | carry | ||
r = (r << 1) | (q >> (BITS - 1)); | ||
q = (q << 1) | carry as u128; | ||
|
||
// carry = 0 | ||
// if r >= d { | ||
// r -= d; | ||
// carry = 1; | ||
// } | ||
let s = (d.wrapping_sub(r).wrapping_sub(1)) as i128 >> (BITS - 1); | ||
carry = (s & 1) as u64; | ||
r -= d & s as u128; | ||
} | ||
|
||
if let Some(rem) = rem { | ||
*rem = r; | ||
} | ||
(q << 1) | carry as u128 | ||
} | ||
|
||
#[cfg(test)] | ||
#[test] | ||
fn test_udivmodti4() { | ||
let primes = [ | ||
3, 7, 31, 73, 127, 179, 233, 283, 353, | ||
419, 467, 547, 607, 661, 739, 811, 877, 947, | ||
]; | ||
|
||
for (i, d) in (0..128).cycle().zip(primes.iter().cycle()).take(1_000) { | ||
let n = 1u128 << i; | ||
let mut rem = 0; | ||
let q = udivmodti4(n, *d, Some(&mut rem)); | ||
assert_eq!(q, n / d); | ||
assert_eq!(rem, n % d); | ||
} | ||
} |
Oops, something went wrong.