Skip to content

Commit 6e1ac39

Browse files
committed
Expose algebraic floating point intrinsics
1 parent ed49386 commit 6e1ac39

File tree

15 files changed

+508
-20
lines changed

15 files changed

+508
-20
lines changed

library/core/src/intrinsics/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -2995,7 +2995,7 @@ pub unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(_value: Float) -> I
29952995

29962996
/// Float addition that allows optimizations based on algebraic rules.
29972997
///
2998-
/// This intrinsic does not have a stable counterpart.
2998+
/// Stabilized as [`f16::algebraic_add`], [`f32::algebraic_add`], [`f64::algebraic_add`] and [`f128::algebraic_add`].
29992999
#[rustc_nounwind]
30003000
#[rustc_intrinsic]
30013001
#[rustc_intrinsic_must_be_overridden]
@@ -3005,7 +3005,7 @@ pub fn fadd_algebraic<T: Copy>(_a: T, _b: T) -> T {
30053005

30063006
/// Float subtraction that allows optimizations based on algebraic rules.
30073007
///
3008-
/// This intrinsic does not have a stable counterpart.
3008+
/// Stabilized as [`f16::algebraic_sub`], [`f32::algebraic_sub`], [`f64::algebraic_sub`] and [`f128::algebraic_sub`].
30093009
#[rustc_nounwind]
30103010
#[rustc_intrinsic]
30113011
#[rustc_intrinsic_must_be_overridden]
@@ -3015,7 +3015,7 @@ pub fn fsub_algebraic<T: Copy>(_a: T, _b: T) -> T {
30153015

30163016
/// Float multiplication that allows optimizations based on algebraic rules.
30173017
///
3018-
/// This intrinsic does not have a stable counterpart.
3018+
/// Stabilized as [`f16::algebraic_mul`], [`f32::algebraic_mul`], [`f64::algebraic_mul`] and [`f128::algebraic_mul`].
30193019
#[rustc_nounwind]
30203020
#[rustc_intrinsic]
30213021
#[rustc_intrinsic_must_be_overridden]
@@ -3025,7 +3025,7 @@ pub fn fmul_algebraic<T: Copy>(_a: T, _b: T) -> T {
30253025

30263026
/// Float division that allows optimizations based on algebraic rules.
30273027
///
3028-
/// This intrinsic does not have a stable counterpart.
3028+
/// Stabilized as [`f16::algebraic_div`], [`f32::algebraic_div`], [`f64::algebraic_div`] and [`f128::algebraic_div`].
30293029
#[rustc_nounwind]
30303030
#[rustc_intrinsic]
30313031
#[rustc_intrinsic_must_be_overridden]
@@ -3035,7 +3035,7 @@ pub fn fdiv_algebraic<T: Copy>(_a: T, _b: T) -> T {
30353035

30363036
/// Float remainder that allows optimizations based on algebraic rules.
30373037
///
3038-
/// This intrinsic does not have a stable counterpart.
3038+
/// Stabilized as [`f16::algebraic_rem`], [`f32::algebraic_rem`], [`f64::algebraic_rem`] and [`f128::algebraic_rem`].
30393039
#[rustc_nounwind]
30403040
#[rustc_intrinsic]
30413041
#[rustc_intrinsic_must_be_overridden]

library/core/src/num/f128.rs

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

library/core/src/num/f16.rs

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

library/core/src/num/f32.rs

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

library/core/src/num/f64.rs

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

library/core/src/primitive_docs.rs

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

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@
337337
#![feature(exact_size_is_empty)]
338338
#![feature(exclusive_wrapper)]
339339
#![feature(extend_one)]
340+
#![feature(float_algebraic)]
340341
#![feature(float_gamma)]
341342
#![feature(float_minimum_maximum)]
342343
#![feature(fmt_internals)]

library/std/tests/floats/f128.rs

+12
Original file line numberDiff line numberDiff line change
@@ -983,3 +983,15 @@ fn test_total_cmp() {
983983
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY));
984984
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
985985
}
986+
987+
#[test]
988+
fn test_algebraic() {
989+
let a: f128 = 123.0;
990+
let b: f128 = 456.0;
991+
992+
assert_approx_eq!(a.algebraic_add(b), a + b);
993+
assert_approx_eq!(a.algebraic_sub(b), a - b);
994+
assert_approx_eq!(a.algebraic_mul(b), a * b);
995+
assert_approx_eq!(a.algebraic_div(b), a / b);
996+
assert_approx_eq!(a.algebraic_rem(b), a % b);
997+
}

library/std/tests/floats/f16.rs

+12
Original file line numberDiff line numberDiff line change
@@ -955,3 +955,15 @@ fn test_total_cmp() {
955955
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY));
956956
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
957957
}
958+
959+
#[test]
960+
fn test_algebraic() {
961+
let a: f16 = 123.0;
962+
let b: f16 = 456.0;
963+
964+
assert_approx_eq!(a.algebraic_add(b), a + b);
965+
assert_approx_eq!(a.algebraic_sub(b), a - b);
966+
assert_approx_eq!(a.algebraic_mul(b), a * b);
967+
assert_approx_eq!(a.algebraic_div(b), a / b);
968+
assert_approx_eq!(a.algebraic_rem(b), a % b);
969+
}

library/std/tests/floats/f32.rs

+12
Original file line numberDiff line numberDiff line change
@@ -915,3 +915,15 @@ 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+
assert_approx_eq!(a.algebraic_add(b), a + b);
925+
assert_approx_eq!(a.algebraic_sub(b), a - b);
926+
assert_approx_eq!(a.algebraic_mul(b), a * b);
927+
assert_approx_eq!(a.algebraic_div(b), a / b);
928+
assert_approx_eq!(a.algebraic_rem(b), a % b);
929+
}

library/std/tests/floats/f64.rs

+12
Original file line numberDiff line numberDiff line change
@@ -894,3 +894,15 @@ fn test_total_cmp() {
894894
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY));
895895
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
896896
}
897+
898+
#[test]
899+
fn test_algebraic() {
900+
let a: f64 = 123.0;
901+
let b: f64 = 456.0;
902+
903+
assert_approx_eq!(a.algebraic_add(b), a + b);
904+
assert_approx_eq!(a.algebraic_sub(b), a - b);
905+
assert_approx_eq!(a.algebraic_mul(b), a * b);
906+
assert_approx_eq!(a.algebraic_div(b), a / b);
907+
assert_approx_eq!(a.algebraic_rem(b), a % b);
908+
}

library/std/tests/floats/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(f16, f128, float_gamma, float_minimum_maximum)]
1+
#![feature(f16, f128, float_algebraic, float_gamma, float_minimum_maximum)]
22

33
use std::fmt;
44
use std::ops::{Add, Div, Mul, Rem, Sub};

0 commit comments

Comments
 (0)