Skip to content

Commit 8ff7052

Browse files
committed
Expose algebraic floating point intrinsics
1 parent 5cc6072 commit 8ff7052

File tree

15 files changed

+544
-24
lines changed

15 files changed

+544
-24
lines changed

library/core/src/intrinsics/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -2475,35 +2475,35 @@ pub unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> In
24752475

24762476
/// Float addition that allows optimizations based on algebraic rules.
24772477
///
2478-
/// This intrinsic does not have a stable counterpart.
2478+
/// Stabilized as [`f16::algebraic_add`], [`f32::algebraic_add`], [`f64::algebraic_add`] and [`f128::algebraic_add`].
24792479
#[rustc_nounwind]
24802480
#[rustc_intrinsic]
24812481
pub fn fadd_algebraic<T: Copy>(a: T, b: T) -> T;
24822482

24832483
/// Float subtraction that allows optimizations based on algebraic rules.
24842484
///
2485-
/// This intrinsic does not have a stable counterpart.
2485+
/// Stabilized as [`f16::algebraic_sub`], [`f32::algebraic_sub`], [`f64::algebraic_sub`] and [`f128::algebraic_sub`].
24862486
#[rustc_nounwind]
24872487
#[rustc_intrinsic]
24882488
pub fn fsub_algebraic<T: Copy>(a: T, b: T) -> T;
24892489

24902490
/// Float multiplication that allows optimizations based on algebraic rules.
24912491
///
2492-
/// This intrinsic does not have a stable counterpart.
2492+
/// Stabilized as [`f16::algebraic_mul`], [`f32::algebraic_mul`], [`f64::algebraic_mul`] and [`f128::algebraic_mul`].
24932493
#[rustc_nounwind]
24942494
#[rustc_intrinsic]
24952495
pub fn fmul_algebraic<T: Copy>(a: T, b: T) -> T;
24962496

24972497
/// Float division that allows optimizations based on algebraic rules.
24982498
///
2499-
/// This intrinsic does not have a stable counterpart.
2499+
/// Stabilized as [`f16::algebraic_div`], [`f32::algebraic_div`], [`f64::algebraic_div`] and [`f128::algebraic_div`].
25002500
#[rustc_nounwind]
25012501
#[rustc_intrinsic]
25022502
pub fn fdiv_algebraic<T: Copy>(a: T, b: T) -> T;
25032503

25042504
/// Float remainder that allows optimizations based on algebraic rules.
25052505
///
2506-
/// This intrinsic does not have a stable counterpart.
2506+
/// Stabilized as [`f16::algebraic_rem`], [`f32::algebraic_rem`], [`f64::algebraic_rem`] and [`f128::algebraic_rem`].
25072507
#[rustc_nounwind]
25082508
#[rustc_intrinsic]
25092509
pub fn frem_algebraic<T: Copy>(a: T, b: T) -> T;

library/core/src/num/f128.rs

+50
Original file line numberDiff line numberDiff line change
@@ -1362,4 +1362,54 @@ impl f128 {
13621362
// SAFETY: this is actually a safe intrinsic
13631363
unsafe { intrinsics::copysignf128(self, sign) }
13641364
}
1365+
1366+
/// Float addition that allows optimizations based on algebraic rules.
1367+
///
1368+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1369+
#[must_use = "method returns a new number and does not mutate the original value"]
1370+
#[unstable(feature = "float_algebraic", issue = "136469")]
1371+
#[inline]
1372+
pub fn algebraic_add(self, rhs: f128) -> f128 {
1373+
intrinsics::fadd_algebraic(self, rhs)
1374+
}
1375+
1376+
/// Float subtraction that allows optimizations based on algebraic rules.
1377+
///
1378+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1379+
#[must_use = "method returns a new number and does not mutate the original value"]
1380+
#[unstable(feature = "float_algebraic", issue = "136469")]
1381+
#[inline]
1382+
pub fn algebraic_sub(self, rhs: f128) -> f128 {
1383+
intrinsics::fsub_algebraic(self, rhs)
1384+
}
1385+
1386+
/// Float multiplication that allows optimizations based on algebraic rules.
1387+
///
1388+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1389+
#[must_use = "method returns a new number and does not mutate the original value"]
1390+
#[unstable(feature = "float_algebraic", issue = "136469")]
1391+
#[inline]
1392+
pub fn algebraic_mul(self, rhs: f128) -> f128 {
1393+
intrinsics::fmul_algebraic(self, rhs)
1394+
}
1395+
1396+
/// Float division that allows optimizations based on algebraic rules.
1397+
///
1398+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1399+
#[must_use = "method returns a new number and does not mutate the original value"]
1400+
#[unstable(feature = "float_algebraic", issue = "136469")]
1401+
#[inline]
1402+
pub fn algebraic_div(self, rhs: f128) -> f128 {
1403+
intrinsics::fdiv_algebraic(self, rhs)
1404+
}
1405+
1406+
/// Float remainder that allows optimizations based on algebraic rules.
1407+
///
1408+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1409+
#[must_use = "method returns a new number and does not mutate the original value"]
1410+
#[unstable(feature = "float_algebraic", issue = "136469")]
1411+
#[inline]
1412+
pub fn algebraic_rem(self, rhs: f128) -> f128 {
1413+
intrinsics::frem_algebraic(self, rhs)
1414+
}
13651415
}

library/core/src/num/f16.rs

+50
Original file line numberDiff line numberDiff line change
@@ -1338,4 +1338,54 @@ impl f16 {
13381338
// SAFETY: this is actually a safe intrinsic
13391339
unsafe { intrinsics::copysignf16(self, sign) }
13401340
}
1341+
1342+
/// Float addition that allows optimizations based on algebraic rules.
1343+
///
1344+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1345+
#[must_use = "method returns a new number and does not mutate the original value"]
1346+
#[unstable(feature = "float_algebraic", issue = "136469")]
1347+
#[inline]
1348+
pub fn algebraic_add(self, rhs: f16) -> f16 {
1349+
intrinsics::fadd_algebraic(self, rhs)
1350+
}
1351+
1352+
/// Float subtraction that allows optimizations based on algebraic rules.
1353+
///
1354+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1355+
#[must_use = "method returns a new number and does not mutate the original value"]
1356+
#[unstable(feature = "float_algebraic", issue = "136469")]
1357+
#[inline]
1358+
pub fn algebraic_sub(self, rhs: f16) -> f16 {
1359+
intrinsics::fsub_algebraic(self, rhs)
1360+
}
1361+
1362+
/// Float multiplication that allows optimizations based on algebraic rules.
1363+
///
1364+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1365+
#[must_use = "method returns a new number and does not mutate the original value"]
1366+
#[unstable(feature = "float_algebraic", issue = "136469")]
1367+
#[inline]
1368+
pub fn algebraic_mul(self, rhs: f16) -> f16 {
1369+
intrinsics::fmul_algebraic(self, rhs)
1370+
}
1371+
1372+
/// Float division that allows optimizations based on algebraic rules.
1373+
///
1374+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1375+
#[must_use = "method returns a new number and does not mutate the original value"]
1376+
#[unstable(feature = "float_algebraic", issue = "136469")]
1377+
#[inline]
1378+
pub fn algebraic_div(self, rhs: f16) -> f16 {
1379+
intrinsics::fdiv_algebraic(self, rhs)
1380+
}
1381+
1382+
/// Float remainder that allows optimizations based on algebraic rules.
1383+
///
1384+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1385+
#[must_use = "method returns a new number and does not mutate the original value"]
1386+
#[unstable(feature = "float_algebraic", issue = "136469")]
1387+
#[inline]
1388+
pub fn algebraic_rem(self, rhs: f16) -> f16 {
1389+
intrinsics::frem_algebraic(self, rhs)
1390+
}
13411391
}

library/core/src/num/f32.rs

+50
Original file line numberDiff line numberDiff line change
@@ -1504,4 +1504,54 @@ impl f32 {
15041504
// SAFETY: this is actually a safe intrinsic
15051505
unsafe { intrinsics::copysignf32(self, sign) }
15061506
}
1507+
1508+
/// Float addition that allows optimizations based on algebraic rules.
1509+
///
1510+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1511+
#[must_use = "method returns a new number and does not mutate the original value"]
1512+
#[unstable(feature = "float_algebraic", issue = "136469")]
1513+
#[inline]
1514+
pub fn algebraic_add(self, rhs: f32) -> f32 {
1515+
intrinsics::fadd_algebraic(self, rhs)
1516+
}
1517+
1518+
/// Float subtraction that allows optimizations based on algebraic rules.
1519+
///
1520+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1521+
#[must_use = "method returns a new number and does not mutate the original value"]
1522+
#[unstable(feature = "float_algebraic", issue = "136469")]
1523+
#[inline]
1524+
pub fn algebraic_sub(self, rhs: f32) -> f32 {
1525+
intrinsics::fsub_algebraic(self, rhs)
1526+
}
1527+
1528+
/// Float multiplication that allows optimizations based on algebraic rules.
1529+
///
1530+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1531+
#[must_use = "method returns a new number and does not mutate the original value"]
1532+
#[unstable(feature = "float_algebraic", issue = "136469")]
1533+
#[inline]
1534+
pub fn algebraic_mul(self, rhs: f32) -> f32 {
1535+
intrinsics::fmul_algebraic(self, rhs)
1536+
}
1537+
1538+
/// Float division that allows optimizations based on algebraic rules.
1539+
///
1540+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1541+
#[must_use = "method returns a new number and does not mutate the original value"]
1542+
#[unstable(feature = "float_algebraic", issue = "136469")]
1543+
#[inline]
1544+
pub fn algebraic_div(self, rhs: f32) -> f32 {
1545+
intrinsics::fdiv_algebraic(self, rhs)
1546+
}
1547+
1548+
/// Float remainder that allows optimizations based on algebraic rules.
1549+
///
1550+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1551+
#[must_use = "method returns a new number and does not mutate the original value"]
1552+
#[unstable(feature = "float_algebraic", issue = "136469")]
1553+
#[inline]
1554+
pub fn algebraic_rem(self, rhs: f32) -> f32 {
1555+
intrinsics::frem_algebraic(self, rhs)
1556+
}
15071557
}

library/core/src/num/f64.rs

+50
Original file line numberDiff line numberDiff line change
@@ -1503,4 +1503,54 @@ impl f64 {
15031503
// SAFETY: this is actually a safe intrinsic
15041504
unsafe { intrinsics::copysignf64(self, sign) }
15051505
}
1506+
1507+
/// Float addition that allows optimizations based on algebraic rules.
1508+
///
1509+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1510+
#[must_use = "method returns a new number and does not mutate the original value"]
1511+
#[unstable(feature = "float_algebraic", issue = "136469")]
1512+
#[inline]
1513+
pub fn algebraic_add(self, rhs: f64) -> f64 {
1514+
intrinsics::fadd_algebraic(self, rhs)
1515+
}
1516+
1517+
/// Float subtraction that allows optimizations based on algebraic rules.
1518+
///
1519+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1520+
#[must_use = "method returns a new number and does not mutate the original value"]
1521+
#[unstable(feature = "float_algebraic", issue = "136469")]
1522+
#[inline]
1523+
pub fn algebraic_sub(self, rhs: f64) -> f64 {
1524+
intrinsics::fsub_algebraic(self, rhs)
1525+
}
1526+
1527+
/// Float multiplication that allows optimizations based on algebraic rules.
1528+
///
1529+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1530+
#[must_use = "method returns a new number and does not mutate the original value"]
1531+
#[unstable(feature = "float_algebraic", issue = "136469")]
1532+
#[inline]
1533+
pub fn algebraic_mul(self, rhs: f64) -> f64 {
1534+
intrinsics::fmul_algebraic(self, rhs)
1535+
}
1536+
1537+
/// Float division that allows optimizations based on algebraic rules.
1538+
///
1539+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1540+
#[must_use = "method returns a new number and does not mutate the original value"]
1541+
#[unstable(feature = "float_algebraic", issue = "136469")]
1542+
#[inline]
1543+
pub fn algebraic_div(self, rhs: f64) -> f64 {
1544+
intrinsics::fdiv_algebraic(self, rhs)
1545+
}
1546+
1547+
/// Float remainder that allows optimizations based on algebraic rules.
1548+
///
1549+
/// See [algebraic operators](primitive@f32#algebraic-operators) for more info.
1550+
#[must_use = "method returns a new number and does not mutate the original value"]
1551+
#[unstable(feature = "float_algebraic", issue = "136469")]
1552+
#[inline]
1553+
pub fn algebraic_rem(self, rhs: f64) -> f64 {
1554+
intrinsics::frem_algebraic(self, rhs)
1555+
}
15061556
}

library/core/src/primitive_docs.rs

+45
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,51 @@ mod prim_f16 {}
13151315
/// | `wasm32`, `wasm64` | If all input NaNs are quiet with all-zero payload: None.<br> Otherwise: all possible payloads. |
13161316
///
13171317
/// For targets not in this table, all payloads are possible.
1318+
///
1319+
/// # Algebraic operators
1320+
///
1321+
/// Algebraic operators of the form `a.algebraic_*(b)` allow the compiler to optimize
1322+
/// floating point operations using all the usual algebraic properties of real numbers --
1323+
/// despite the fact that those properties do *not* hold on floating point numbers.
1324+
/// This can give a great performance boost since it may unlock vectorization.
1325+
///
1326+
/// The exact set of optimizations is unspecified but typically allows combining operations,
1327+
/// rearranging series of operations based on mathematical properties, converting between division
1328+
/// and reciprocal multiplication, and disregarding the sign of zero. This means that the results of
1329+
/// elementary operations may have undefined precision, and "non-mathematical" values
1330+
/// such as NaN, +/-Inf, or -0.0 may behave in unexpected ways, but these operations
1331+
/// will never cause undefined behavior.
1332+
///
1333+
/// Because of the unpredictable nature of compiler optimizations, the same inputs may produce
1334+
/// different results even within a single program run. **Unsafe code must not rely on any property
1335+
/// of the return value for soundness.** However, implementations will generally do their best to
1336+
/// pick a reasonable tradeoff between performance and accuracy of the result.
1337+
///
1338+
/// For example:
1339+
///
1340+
/// ```
1341+
/// # #![feature(float_algebraic)]
1342+
/// # #![allow(unused_assignments)]
1343+
/// # let mut x: f32 = 0.0;
1344+
/// # let a: f32 = 1.0;
1345+
/// # let b: f32 = 2.0;
1346+
/// # let c: f32 = 3.0;
1347+
/// # let d: f32 = 4.0;
1348+
/// x = a.algebraic_add(b).algebraic_add(c).algebraic_add(d);
1349+
/// ```
1350+
///
1351+
/// May be rewritten as:
1352+
///
1353+
/// ```
1354+
/// # #![allow(unused_assignments)]
1355+
/// # let mut x: f32 = 0.0;
1356+
/// # let a: f32 = 1.0;
1357+
/// # let b: f32 = 2.0;
1358+
/// # let c: f32 = 3.0;
1359+
/// # let d: f32 = 4.0;
1360+
/// x = a + b + c + d; // As written
1361+
/// x = (a + c) + (b + d); // Reordered to shorten critical path and enable vectorization
1362+
/// ```
13181363
13191364
#[stable(feature = "rust1", since = "1.0.0")]
13201365
mod prim_f32 {}

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@
340340
#![feature(exact_size_is_empty)]
341341
#![feature(exclusive_wrapper)]
342342
#![feature(extend_one)]
343+
#![feature(float_algebraic)]
343344
#![feature(float_gamma)]
344345
#![feature(float_minimum_maximum)]
345346
#![feature(fmt_internals)]

library/std/tests/floats/f128.rs

+19
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,25 @@ fn test_total_cmp() {
984984
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
985985
}
986986

987+
#[test]
988+
fn test_algebraic() {
989+
let a: f128 = 123.0;
990+
let b: f128 = 456.0;
991+
992+
// Check that individual operations match their primitive counterparts.
993+
//
994+
// This is a check of current implementations and does NOT imply any form of
995+
// guarantee about future behavior. The compiler reserves the right to make
996+
// these operations inexact matches in the future.
997+
let eps = if cfg!(miri) { 1e-6 } else { 0.0 };
998+
999+
assert_approx_eq!(a.algebraic_add(b), a + b, eps);
1000+
assert_approx_eq!(a.algebraic_sub(b), a - b, eps);
1001+
assert_approx_eq!(a.algebraic_mul(b), a * b, eps);
1002+
assert_approx_eq!(a.algebraic_div(b), a / b, eps);
1003+
assert_approx_eq!(a.algebraic_rem(b), a % b, eps);
1004+
}
1005+
9871006
#[test]
9881007
fn test_from() {
9891008
assert_eq!(f128::from(false), 0.0);

library/std/tests/floats/f16.rs

+21
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,27 @@ fn test_total_cmp() {
954954
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
955955
}
956956

957+
#[test]
958+
fn test_algebraic() {
959+
let a: f16 = 123.0;
960+
let b: f16 = 456.0;
961+
962+
// Check that individual operations match their primitive counterparts.
963+
//
964+
// This is a check of current implementations and does NOT imply any form of
965+
// guarantee about future behavior. The compiler reserves the right to make
966+
// these operations inexact matches in the future.
967+
let eps_add = if cfg!(miri) { 1e1 } else { 0.0 };
968+
let eps_mul = if cfg!(miri) { 1e3 } else { 0.0 };
969+
let eps_div = if cfg!(miri) { 1e0 } else { 0.0 };
970+
971+
assert_approx_eq!(a.algebraic_add(b), a + b, eps_add);
972+
assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add);
973+
assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul);
974+
assert_approx_eq!(a.algebraic_div(b), a / b, eps_div);
975+
assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div);
976+
}
977+
957978
#[test]
958979
fn test_from() {
959980
assert_eq!(f16::from(false), 0.0);

library/std/tests/floats/f32.rs

+21
Original file line numberDiff line numberDiff line change
@@ -915,3 +915,24 @@ fn test_total_cmp() {
915915
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY));
916916
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
917917
}
918+
919+
#[test]
920+
fn test_algebraic() {
921+
let a: f32 = 123.0;
922+
let b: f32 = 456.0;
923+
924+
// Check that individual operations match their primitive counterparts.
925+
//
926+
// This is a check of current implementations and does NOT imply any form of
927+
// guarantee about future behavior. The compiler reserves the right to make
928+
// these operations inexact matches in the future.
929+
let eps_add = if cfg!(miri) { 1e-3 } else { 0.0 };
930+
let eps_mul = if cfg!(miri) { 1e-1 } else { 0.0 };
931+
let eps_div = if cfg!(miri) { 1e-4 } else { 0.0 };
932+
933+
assert_approx_eq!(a.algebraic_add(b), a + b, eps_add);
934+
assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add);
935+
assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul);
936+
assert_approx_eq!(a.algebraic_div(b), a / b, eps_div);
937+
assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div);
938+
}

0 commit comments

Comments
 (0)