Skip to content

Commit 6d70c0f

Browse files
committed
Work on bigint
Try splitting part of 'Int' into 'MinInt' so we don't need to implement everything on u256/i256 Add addsub test Add mul/div/rem tests Add cmp test Remove 32-bit div implementation formatting updates disable div tests for now Bigint updates Big update Fix widen mul wrapping add disable duplicate symbols in builtins Apply temporary unord fix from @beetrees #593 tests add lowerhex display errors by ref tests fix-test Update big tests Fix core calls Disable widen_mul for signed Test adding symbols in build.rs Add a feature to compile intrinsics that are missing on the system for testing update Disable f128 tests on platforms without system support add missing build.rs file pull cas file from master testgs print more div values Add a benchmark Work on fixing bit widths Update benchmark
1 parent fcda955 commit 6d70c0f

23 files changed

+952
-194
lines changed

build.rs

-13
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ fn main() {
4444
println!("cargo:rustc-cfg=feature=\"mem-unaligned\"");
4545
}
4646

47-
// NOTE we are going to assume that llvm-target, what determines our codegen option, matches the
4847
// target triple. This is usually correct for our built-in targets but can break in presence of
4948
// custom targets, which can have arbitrary names.
5049
let llvm_target = target.split('-').collect::<Vec<_>>();
@@ -478,10 +477,6 @@ mod c {
478477
("__floatunsitf", "floatunsitf.c"),
479478
("__trunctfdf2", "trunctfdf2.c"),
480479
("__trunctfsf2", "trunctfsf2.c"),
481-
("__addtf3", "addtf3.c"),
482-
("__multf3", "multf3.c"),
483-
("__subtf3", "subtf3.c"),
484-
("__divtf3", "divtf3.c"),
485480
("__powitf2", "powitf2.c"),
486481
("__fe_getround", "fp_mode.c"),
487482
("__fe_raise_inexact", "fp_mode.c"),
@@ -500,15 +495,11 @@ mod c {
500495
sources.extend(&[
501496
("__extenddftf2", "extenddftf2.c"),
502497
("__netf2", "comparetf2.c"),
503-
("__addtf3", "addtf3.c"),
504-
("__multf3", "multf3.c"),
505-
("__subtf3", "subtf3.c"),
506498
("__fixtfsi", "fixtfsi.c"),
507499
("__floatsitf", "floatsitf.c"),
508500
("__fixunstfsi", "fixunstfsi.c"),
509501
("__floatunsitf", "floatunsitf.c"),
510502
("__fe_getround", "fp_mode.c"),
511-
("__divtf3", "divtf3.c"),
512503
("__trunctfdf2", "trunctfdf2.c"),
513504
("__trunctfsf2", "trunctfsf2.c"),
514505
]);
@@ -518,15 +509,11 @@ mod c {
518509
sources.extend(&[
519510
("__extenddftf2", "extenddftf2.c"),
520511
("__netf2", "comparetf2.c"),
521-
("__addtf3", "addtf3.c"),
522-
("__multf3", "multf3.c"),
523-
("__subtf3", "subtf3.c"),
524512
("__fixtfsi", "fixtfsi.c"),
525513
("__floatsitf", "floatsitf.c"),
526514
("__fixunstfsi", "fixunstfsi.c"),
527515
("__floatunsitf", "floatunsitf.c"),
528516
("__fe_getround", "fp_mode.c"),
529-
("__divtf3", "divtf3.c"),
530517
("__trunctfdf2", "trunctfdf2.c"),
531518
("__trunctfsf2", "trunctfsf2.c"),
532519
]);

src/float/add.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::float::Float;
2-
use crate::int::{CastInto, Int};
2+
use crate::int::{CastInto, Int, MinInt};
33

44
/// Returns `a + b`
55
fn add<F: Float>(a: F, b: F) -> F
@@ -57,17 +57,17 @@ where
5757
}
5858

5959
// zero + anything = anything
60-
if a_abs == Int::ZERO {
60+
if a_abs == MinInt::ZERO {
6161
// but we need to get the sign right for zero + zero
62-
if b_abs == Int::ZERO {
62+
if b_abs == MinInt::ZERO {
6363
return F::from_repr(a.repr() & b.repr());
6464
} else {
6565
return b;
6666
}
6767
}
6868

6969
// anything + zero = anything
70-
if b_abs == Int::ZERO {
70+
if b_abs == MinInt::ZERO {
7171
return a;
7272
}
7373
}
@@ -113,10 +113,10 @@ where
113113
// Shift the significand of b by the difference in exponents, with a sticky
114114
// bottom bit to get rounding correct.
115115
let align = a_exponent.wrapping_sub(b_exponent).cast();
116-
if align != Int::ZERO {
116+
if align != MinInt::ZERO {
117117
if align < bits {
118118
let sticky =
119-
F::Int::from_bool(b_significand << bits.wrapping_sub(align).cast() != Int::ZERO);
119+
F::Int::from_bool(b_significand << bits.wrapping_sub(align).cast() != MinInt::ZERO);
120120
b_significand = (b_significand >> align.cast()) | sticky;
121121
} else {
122122
b_significand = one; // sticky; b is known to be non-zero.
@@ -125,8 +125,8 @@ where
125125
if subtraction {
126126
a_significand = a_significand.wrapping_sub(b_significand);
127127
// If a == -b, return +zero.
128-
if a_significand == Int::ZERO {
129-
return F::from_repr(Int::ZERO);
128+
if a_significand == MinInt::ZERO {
129+
return F::from_repr(MinInt::ZERO);
130130
}
131131

132132
// If partial cancellation occured, we need to left-shift the result
@@ -143,8 +143,8 @@ where
143143

144144
// If the addition carried up, we need to right-shift the result and
145145
// adjust the exponent:
146-
if a_significand & implicit_bit << 4 != Int::ZERO {
147-
let sticky = F::Int::from_bool(a_significand & one != Int::ZERO);
146+
if a_significand & implicit_bit << 4 != MinInt::ZERO {
147+
let sticky = F::Int::from_bool(a_significand & one != MinInt::ZERO);
148148
a_significand = a_significand >> 1 | sticky;
149149
a_exponent += 1;
150150
}
@@ -160,7 +160,7 @@ where
160160
// need to shift the significand.
161161
let shift = (1 - a_exponent).cast();
162162
let sticky =
163-
F::Int::from_bool((a_significand << bits.wrapping_sub(shift).cast()) != Int::ZERO);
163+
F::Int::from_bool((a_significand << bits.wrapping_sub(shift).cast()) != MinInt::ZERO);
164164
a_significand = a_significand >> shift.cast() | sticky;
165165
a_exponent = 0;
166166
}

src/float/cmp.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![allow(unreachable_code)]
22

33
use crate::float::Float;
4-
use crate::int::Int;
4+
use crate::int::MinInt;
55

66
#[derive(Clone, Copy)]
77
enum Result {

src/float/div.rs

+60-35
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
#![allow(clippy::needless_return)]
44

55
use crate::float::Float;
6-
use crate::int::{CastInto, DInt, HInt, Int};
6+
use crate::int::{CastInto, DInt, HInt, Int, MinInt};
7+
8+
use super::HalfRep;
79

810
fn div32<F: Float>(a: F, b: F) -> F
911
where
@@ -37,6 +39,11 @@ where
3739
let quiet_bit = implicit_bit >> 1;
3840
let qnan_rep = exponent_mask | quiet_bit;
3941

42+
// #[inline(always)]
43+
// fn negate<T: Int>(a: T) -> T {
44+
// T::wrapping_neg(a.signe)
45+
// }
46+
4047
#[inline(always)]
4148
fn negate_u32(a: u32) -> u32 {
4249
(<i32>::wrapping_neg(a as i32)) as u32
@@ -459,10 +466,14 @@ where
459466
i32: CastInto<F::Int>,
460467
F::Int: CastInto<i32>,
461468
u64: CastInto<F::Int>,
469+
u64: CastInto<HalfRep<F>>,
470+
F::Int: CastInto<HalfRep<F>>,
471+
F::Int: From<HalfRep<F>>,
472+
F::Int: From<u8>,
462473
F::Int: CastInto<u64>,
463474
i64: CastInto<F::Int>,
464475
F::Int: CastInto<i64>,
465-
F::Int: HInt,
476+
F::Int: HInt + DInt,
466477
{
467478
const NUMBER_OF_HALF_ITERATIONS: usize = 3;
468479
const NUMBER_OF_FULL_ITERATIONS: usize = 1;
@@ -471,7 +482,7 @@ where
471482
let one = F::Int::ONE;
472483
let zero = F::Int::ZERO;
473484
let hw = F::BITS / 2;
474-
let lo_mask = u64::MAX >> hw;
485+
let lo_mask = F::Int::MAX >> hw;
475486

476487
let significand_bits = F::SIGNIFICAND_BITS;
477488
let max_exponent = F::EXPONENT_MAX;
@@ -616,21 +627,23 @@ where
616627

617628
let mut x_uq0 = if NUMBER_OF_HALF_ITERATIONS > 0 {
618629
// Starting with (n-1) half-width iterations
619-
let b_uq1_hw: u32 =
620-
(CastInto::<u64>::cast(b_significand) >> (significand_bits + 1 - hw)) as u32;
630+
let b_uq1_hw: HalfRep<F> = CastInto::<HalfRep<F>>::cast(
631+
CastInto::<u64>::cast(b_significand) >> (significand_bits + 1 - hw),
632+
);
621633

622634
// C is (3/4 + 1/sqrt(2)) - 1 truncated to W0 fractional bits as UQ0.HW
623635
// with W0 being either 16 or 32 and W0 <= HW.
624636
// That is, C is the aforementioned 3/4 + 1/sqrt(2) constant (from which
625637
// b/2 is subtracted to obtain x0) wrapped to [0, 1) range.
626638

627639
// HW is at least 32. Shifting into the highest bits if needed.
628-
let c_hw = (0x7504F333_u64 as u32).wrapping_shl(hw.wrapping_sub(32));
640+
let c_hw = (CastInto::<HalfRep<F>>::cast(0x7504F333_u64)).wrapping_shl(hw.wrapping_sub(32));
629641

630642
// b >= 1, thus an upper bound for 3/4 + 1/sqrt(2) - b/2 is about 0.9572,
631643
// so x0 fits to UQ0.HW without wrapping.
632-
let x_uq0_hw: u32 = {
633-
let mut x_uq0_hw: u32 = c_hw.wrapping_sub(b_uq1_hw /* exact b_hw/2 as UQ0.HW */);
644+
let x_uq0_hw: HalfRep<F> = {
645+
let mut x_uq0_hw: HalfRep<F> =
646+
c_hw.wrapping_sub(b_uq1_hw /* exact b_hw/2 as UQ0.HW */);
634647
// dbg!(x_uq0_hw);
635648
// An e_0 error is comprised of errors due to
636649
// * x0 being an inherently imprecise first approximation of 1/b_hw
@@ -661,8 +674,9 @@ where
661674
// no overflow occurred earlier: ((rep_t)x_UQ0_hw * b_UQ1_hw >> HW) is
662675
// expected to be strictly positive because b_UQ1_hw has its highest bit set
663676
// and x_UQ0_hw should be rather large (it converges to 1/2 < 1/b_hw <= 1).
664-
let corr_uq1_hw: u32 =
665-
0.wrapping_sub(((x_uq0_hw as u64).wrapping_mul(b_uq1_hw as u64)) >> hw) as u32;
677+
let corr_uq1_hw: HalfRep<F> = CastInto::<HalfRep<F>>::cast(zero.wrapping_sub(
678+
((F::Int::from(x_uq0_hw)).wrapping_mul(F::Int::from(b_uq1_hw))) >> hw,
679+
));
666680
// dbg!(corr_uq1_hw);
667681

668682
// Now, we should multiply UQ0.HW and UQ1.(HW-1) numbers, naturally
@@ -677,7 +691,9 @@ where
677691
// The fact corr_UQ1_hw was virtually round up (due to result of
678692
// multiplication being **first** truncated, then negated - to improve
679693
// error estimations) can increase x_UQ0_hw by up to 2*Ulp of x_UQ0_hw.
680-
x_uq0_hw = ((x_uq0_hw as u64).wrapping_mul(corr_uq1_hw as u64) >> (hw - 1)) as u32;
694+
x_uq0_hw = ((F::Int::from(x_uq0_hw)).wrapping_mul(F::Int::from(corr_uq1_hw))
695+
>> (hw - 1))
696+
.cast();
681697
// dbg!(x_uq0_hw);
682698
// Now, either no overflow occurred or x_UQ0_hw is 0 or 1 in its half_rep_t
683699
// representation. In the latter case, x_UQ0_hw will be either 0 or 1 after
@@ -707,7 +723,7 @@ where
707723
// be not below that value (see g(x) above), so it is safe to decrement just
708724
// once after the final iteration. On the other hand, an effective value of
709725
// divisor changes after this point (from b_hw to b), so adjust here.
710-
x_uq0_hw.wrapping_sub(1_u32)
726+
x_uq0_hw.wrapping_sub(HalfRep::<F>::ONE)
711727
};
712728

713729
// Error estimations for full-precision iterations are calculated just
@@ -717,7 +733,7 @@ where
717733
// Simulating operations on a twice_rep_t to perform a single final full-width
718734
// iteration. Using ad-hoc multiplication implementations to take advantage
719735
// of particular structure of operands.
720-
let blo: u64 = (CastInto::<u64>::cast(b_uq1)) & lo_mask;
736+
let blo: F::Int = b_uq1 & lo_mask;
721737
// x_UQ0 = x_UQ0_hw * 2^HW - 1
722738
// x_UQ0 * b_UQ1 = (x_UQ0_hw * 2^HW) * (b_UQ1_hw * 2^HW + blo) - b_UQ1
723739
//
@@ -726,19 +742,20 @@ where
726742
// + [ x_UQ0_hw * blo ]
727743
// - [ b_UQ1 ]
728744
// = [ result ][.... discarded ...]
729-
let corr_uq1 = negate_u64(
730-
(x_uq0_hw as u64) * (b_uq1_hw as u64) + (((x_uq0_hw as u64) * (blo)) >> hw) - 1,
731-
); // account for *possible* carry
732-
let lo_corr = corr_uq1 & lo_mask;
733-
let hi_corr = corr_uq1 >> hw;
745+
let corr_uq1: F::Int = (F::Int::from(x_uq0_hw) * F::Int::from(b_uq1_hw)
746+
+ ((F::Int::from(x_uq0_hw) * blo) >> hw))
747+
.wrapping_sub(one)
748+
.wrapping_neg(); // account for *possible* carry
749+
let lo_corr: F::Int = corr_uq1 & lo_mask;
750+
let hi_corr: F::Int = corr_uq1 >> hw;
734751
// x_UQ0 * corr_UQ1 = (x_UQ0_hw * 2^HW) * (hi_corr * 2^HW + lo_corr) - corr_UQ1
735-
let mut x_uq0: <F as Float>::Int = ((((x_uq0_hw as u64) * hi_corr) << 1)
736-
.wrapping_add(((x_uq0_hw as u64) * lo_corr) >> (hw - 1))
737-
.wrapping_sub(2))
738-
.cast(); // 1 to account for the highest bit of corr_UQ1 can be 1
739-
// 1 to account for possible carry
740-
// Just like the case of half-width iterations but with possibility
741-
// of overflowing by one extra Ulp of x_UQ0.
752+
let mut x_uq0: F::Int = ((F::Int::from(x_uq0_hw) * hi_corr) << 1)
753+
.wrapping_add((F::Int::from(x_uq0_hw) * lo_corr) >> (hw - 1))
754+
.wrapping_sub(F::Int::from(2u8));
755+
// 1 to account for the highest bit of corr_UQ1 can be 1
756+
// 1 to account for possible carry
757+
// Just like the case of half-width iterations but with possibility
758+
// of overflowing by one extra Ulp of x_UQ0.
742759
x_uq0 -= one;
743760
// ... and then traditional fixup by 2 should work
744761

@@ -755,8 +772,8 @@ where
755772
x_uq0
756773
} else {
757774
// C is (3/4 + 1/sqrt(2)) - 1 truncated to 64 fractional bits as UQ0.n
758-
let c: <F as Float>::Int = (0x7504F333 << (F::BITS - 32)).cast();
759-
let x_uq0: <F as Float>::Int = c.wrapping_sub(b_uq1);
775+
let c: F::Int = (0x7504F333 << (F::BITS - 32)).cast();
776+
let x_uq0: F::Int = c.wrapping_sub(b_uq1);
760777
// E_0 <= 3/4 - 1/sqrt(2) + 2 * 2^-64
761778
x_uq0
762779
};
@@ -799,14 +816,27 @@ where
799816

800817
// Add 2 to U_N due to final decrement.
801818

802-
let reciprocal_precision: <F as Float>::Int = 220.cast();
819+
let reciprocal_precision: F::Int = if F::BITS == 32
820+
&& NUMBER_OF_HALF_ITERATIONS == 2
821+
&& NUMBER_OF_FULL_ITERATIONS == 1
822+
{
823+
74.cast()
824+
} else if F::BITS == 32 && NUMBER_OF_HALF_ITERATIONS == 0 && NUMBER_OF_FULL_ITERATIONS == 3 {
825+
10.cast()
826+
} else if F::BITS == 64 && NUMBER_OF_HALF_ITERATIONS == 3 && NUMBER_OF_FULL_ITERATIONS == 1 {
827+
220.cast()
828+
} else if F::BITS == 128 && NUMBER_OF_HALF_ITERATIONS == 4 && NUMBER_OF_FULL_ITERATIONS == 1 {
829+
13922.cast()
830+
} else {
831+
panic!("invalid iterations for the specified bits");
832+
};
803833

804834
// Suppose 1/b - P * 2^-W < x < 1/b + P * 2^-W
805835
let x_uq0 = x_uq0 - reciprocal_precision;
806836
// Now 1/b - (2*P) * 2^-W < x < 1/b
807837
// FIXME Is x_UQ0 still >= 0.5?
808838

809-
let mut quotient: <F as Float>::Int = x_uq0.widen_mul(a_significand << 1).hi();
839+
let mut quotient: F::Int = x_uq0.widen_mul(a_significand << 1).hi();
810840
// Now, a/b - 4*P * 2^-W < q < a/b for q=<quotient_UQ1:dummy> in UQ1.(SB+1+W).
811841

812842
// quotient_UQ1 is in [0.5, 2.0) as UQ1.(SB+1),
@@ -914,13 +944,8 @@ intrinsics! {
914944
div64(a, b)
915945
}
916946

917-
// TODO: how should `HInt` be handled?
918947
pub extern "C" fn __divtf3(a: f128, b: f128) -> f128 {
919-
if cfg!(target_pointer_width = "64") {
920-
div32(a, b)
921-
} else {
922-
div64(a, b)
923-
}
948+
div64(a, b)
924949
}
925950

926951
#[cfg(target_arch = "arm")]

src/float/extend.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::float::Float;
2-
use crate::int::{CastInto, Int};
2+
use crate::int::{CastInto, Int, MinInt};
33

44
/// Generic conversion from a narrower to a wider IEEE-754 floating-point type
55
fn extend<F: Float, R: Float>(a: F) -> R

src/float/mod.rs

+20-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use core::ops;
22

3-
use super::int::Int;
3+
use crate::int::DInt;
4+
5+
use super::int::{Int, MinInt};
46

57
pub mod add;
68
pub mod cmp;
@@ -12,6 +14,9 @@ pub mod pow;
1214
pub mod sub;
1315
pub mod trunc;
1416

17+
/// Wrapper to extract the integer type half of the float's size
18+
pub(crate) type HalfRep<F: Float> = <F::Int as DInt>::H;
19+
1520
public_test_dep! {
1621
/// Trait for some basic operations on floats
1722
pub(crate) trait Float:
@@ -127,7 +132,20 @@ macro_rules! float_impl {
127132
self.to_bits() as Self::SignedInt
128133
}
129134
fn eq_repr(self, rhs: Self) -> bool {
130-
if self.is_nan() && rhs.is_nan() {
135+
#[cfg(feature = "mangled-names")]
136+
fn is_nan(x: $ty) -> bool {
137+
// When using mangled-names, the "real" compiler-builtins might not have the
138+
// necessary builtin (__unordtf2) to test whether `f128` is NaN.
139+
// FIXME: Remove once the nightly toolchain has the __unordtf2 builtin
140+
// x is NaN if all the bits of the exponent are set and the significand is non-0
141+
x.repr() & $ty::EXPONENT_MASK == $ty::EXPONENT_MASK
142+
&& x.repr() & $ty::SIGNIFICAND_MASK != 0
143+
}
144+
#[cfg(not(feature = "mangled-names"))]
145+
fn is_nan(x: $ty) -> bool {
146+
x.is_nan()
147+
}
148+
if is_nan(self) && is_nan(rhs) {
131149
true
132150
} else {
133151
self.repr() == rhs.repr()
@@ -171,7 +189,6 @@ macro_rules! float_impl {
171189
};
172190
}
173191

174-
// FIXME: there aren't any intrinsics for f16 that I know of, do we need this?
175192
float_impl!(f16, u16, i16, i16, 16, 10);
176193
float_impl!(f32, u32, i32, i16, 32, 23);
177194
float_impl!(f64, u64, i64, i16, 64, 52);

0 commit comments

Comments
 (0)