Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 11fc30d

Browse files
committedMar 6, 2025
Auto merge of rust-lang#138087 - tgross35:core-float-math, r=<try>
Initial implementation of `core_float_math` Since [1], `compiler-builtins` makes a certain set of math symbols weakly available on all platforms. This means we can begin exposing some of the related functions in `core`, so begin this process here. It is not possible to provide inherent methods in both `core` and `std` while giving them different stability gates, so standalone functions are added instead. This provides a way to experiment with the functionality while unstable; once it is time to stabilize, they can be converted to inherent. For `f16` and `f128`, everything is unstable so we can move the inherent methods. The following are included to start: * floor * ceil * round * round_ties_even * trunc * fract * mul_add * div_euclid * rem_euclid * powi * sqrt * abs_sub * cbrt These mirror the set of functions that we have in `compiler-builtins` since [1]. Tracking issue: rust-lang#137578 [1]: rust-lang/compiler-builtins#763 r? `@ghost` try-job: aarch64-apple try-job: aarch64-gnu try-job: arm-android tru-job: armhf-gnu try-job: dist-various-1 try-job: dist-various-2 try-job: i686-msvc-1 try-job: test-various try-job: x86_64-apple-1 try-job: x86_64-msvc-ext2
2 parents 30f168e + 12ad9b8 commit 11fc30d

25 files changed

+4612
-3683
lines changed
 

‎library/core/src/num/f128.rs

+377
Original file line numberDiff line numberDiff line change
@@ -1366,3 +1366,380 @@ impl f128 {
13661366
unsafe { intrinsics::copysignf128(self, sign) }
13671367
}
13681368
}
1369+
1370+
// Functions in this module fall into `core_float_math`
1371+
// FIXME(f16_f128): all doctests must be gated to platforms that have `long double` === `_Float128`
1372+
// due to https://github.com/llvm/llvm-project/issues/44744. aarch64 linux matches this.
1373+
// #[unstable(feature = "core_float_math", issue = "137578")]
1374+
#[cfg(not(test))]
1375+
impl f128 {
1376+
/// Returns the largest integer less than or equal to `self`.
1377+
///
1378+
/// This function always returns the precise result.
1379+
///
1380+
/// # Examples
1381+
///
1382+
/// ```
1383+
/// #![feature(f128)]
1384+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1385+
///
1386+
/// let f = 3.7_f128;
1387+
/// let g = 3.0_f128;
1388+
/// let h = -3.7_f128;
1389+
///
1390+
/// assert_eq!(f.floor(), 3.0);
1391+
/// assert_eq!(g.floor(), 3.0);
1392+
/// assert_eq!(h.floor(), -4.0);
1393+
/// # }
1394+
/// ```
1395+
#[inline]
1396+
#[rustc_allow_incoherent_impl]
1397+
#[unstable(feature = "f128", issue = "116909")]
1398+
#[must_use = "method returns a new number and does not mutate the original value"]
1399+
pub fn floor(self) -> f128 {
1400+
// SAFETY: intrinsic with no preconditions
1401+
unsafe { intrinsics::floorf128(self) }
1402+
}
1403+
1404+
/// Returns the smallest integer greater than or equal to `self`.
1405+
///
1406+
/// This function always returns the precise result.
1407+
///
1408+
/// # Examples
1409+
///
1410+
/// ```
1411+
/// #![feature(f128)]
1412+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1413+
///
1414+
/// let f = 3.01_f128;
1415+
/// let g = 4.0_f128;
1416+
///
1417+
/// assert_eq!(f.ceil(), 4.0);
1418+
/// assert_eq!(g.ceil(), 4.0);
1419+
/// # }
1420+
/// ```
1421+
#[inline]
1422+
#[doc(alias = "ceiling")]
1423+
#[rustc_allow_incoherent_impl]
1424+
#[unstable(feature = "f128", issue = "116909")]
1425+
#[must_use = "method returns a new number and does not mutate the original value"]
1426+
pub fn ceil(self) -> f128 {
1427+
// SAFETY: intrinsic with no preconditions
1428+
unsafe { intrinsics::ceilf128(self) }
1429+
}
1430+
1431+
/// Returns the nearest integer to `self`. If a value is half-way between two
1432+
/// integers, round away from `0.0`.
1433+
///
1434+
/// This function always returns the precise result.
1435+
///
1436+
/// # Examples
1437+
///
1438+
/// ```
1439+
/// #![feature(f128)]
1440+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1441+
///
1442+
/// let f = 3.3_f128;
1443+
/// let g = -3.3_f128;
1444+
/// let h = -3.7_f128;
1445+
/// let i = 3.5_f128;
1446+
/// let j = 4.5_f128;
1447+
///
1448+
/// assert_eq!(f.round(), 3.0);
1449+
/// assert_eq!(g.round(), -3.0);
1450+
/// assert_eq!(h.round(), -4.0);
1451+
/// assert_eq!(i.round(), 4.0);
1452+
/// assert_eq!(j.round(), 5.0);
1453+
/// # }
1454+
/// ```
1455+
#[inline]
1456+
#[rustc_allow_incoherent_impl]
1457+
#[unstable(feature = "f128", issue = "116909")]
1458+
#[must_use = "method returns a new number and does not mutate the original value"]
1459+
pub fn round(self) -> f128 {
1460+
// SAFETY: intrinsic with no preconditions
1461+
unsafe { intrinsics::roundf128(self) }
1462+
}
1463+
1464+
/// Returns the nearest integer to a number. Rounds half-way cases to the number
1465+
/// with an even least significant digit.
1466+
///
1467+
/// This function always returns the precise result.
1468+
///
1469+
/// # Examples
1470+
///
1471+
/// ```
1472+
/// #![feature(f128)]
1473+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1474+
///
1475+
/// let f = 3.3_f128;
1476+
/// let g = -3.3_f128;
1477+
/// let h = 3.5_f128;
1478+
/// let i = 4.5_f128;
1479+
///
1480+
/// assert_eq!(f.round_ties_even(), 3.0);
1481+
/// assert_eq!(g.round_ties_even(), -3.0);
1482+
/// assert_eq!(h.round_ties_even(), 4.0);
1483+
/// assert_eq!(i.round_ties_even(), 4.0);
1484+
/// # }
1485+
/// ```
1486+
#[inline]
1487+
#[rustc_allow_incoherent_impl]
1488+
#[unstable(feature = "f128", issue = "116909")]
1489+
#[must_use = "method returns a new number and does not mutate the original value"]
1490+
pub fn round_ties_even(self) -> f128 {
1491+
intrinsics::round_ties_even_f128(self)
1492+
}
1493+
1494+
/// Returns the integer part of `self`.
1495+
/// This means that non-integer numbers are always truncated towards zero.
1496+
///
1497+
/// This function always returns the precise result.
1498+
///
1499+
/// # Examples
1500+
///
1501+
/// ```
1502+
/// #![feature(f128)]
1503+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1504+
///
1505+
/// let f = 3.7_f128;
1506+
/// let g = 3.0_f128;
1507+
/// let h = -3.7_f128;
1508+
///
1509+
/// assert_eq!(f.trunc(), 3.0);
1510+
/// assert_eq!(g.trunc(), 3.0);
1511+
/// assert_eq!(h.trunc(), -3.0);
1512+
/// # }
1513+
/// ```
1514+
#[inline]
1515+
#[doc(alias = "truncate")]
1516+
#[rustc_allow_incoherent_impl]
1517+
#[unstable(feature = "f128", issue = "116909")]
1518+
#[must_use = "method returns a new number and does not mutate the original value"]
1519+
pub fn trunc(self) -> f128 {
1520+
// SAFETY: intrinsic with no preconditions
1521+
unsafe { intrinsics::truncf128(self) }
1522+
}
1523+
1524+
/// Returns the fractional part of `self`.
1525+
///
1526+
/// This function always returns the precise result.
1527+
///
1528+
/// # Examples
1529+
///
1530+
/// ```
1531+
/// #![feature(f128)]
1532+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1533+
///
1534+
/// let x = 3.6_f128;
1535+
/// let y = -3.6_f128;
1536+
/// let abs_difference_x = (x.fract() - 0.6).abs();
1537+
/// let abs_difference_y = (y.fract() - (-0.6)).abs();
1538+
///
1539+
/// assert!(abs_difference_x <= f128::EPSILON);
1540+
/// assert!(abs_difference_y <= f128::EPSILON);
1541+
/// # }
1542+
/// ```
1543+
#[inline]
1544+
#[rustc_allow_incoherent_impl]
1545+
#[unstable(feature = "f128", issue = "116909")]
1546+
#[must_use = "method returns a new number and does not mutate the original value"]
1547+
pub fn fract(self) -> f128 {
1548+
self - self.trunc()
1549+
}
1550+
1551+
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
1552+
/// error, yielding a more accurate result than an unfused multiply-add.
1553+
///
1554+
/// Using `mul_add` *may* be more performant than an unfused multiply-add if
1555+
/// the target architecture has a dedicated `fma` CPU instruction. However,
1556+
/// this is not always true, and will be heavily dependant on designing
1557+
/// algorithms with specific target hardware in mind.
1558+
///
1559+
/// # Precision
1560+
///
1561+
/// The result of this operation is guaranteed to be the rounded
1562+
/// infinite-precision result. It is specified by IEEE 754 as
1563+
/// `fusedMultiplyAdd` and guaranteed not to change.
1564+
///
1565+
/// # Examples
1566+
///
1567+
/// ```
1568+
/// #![feature(f128)]
1569+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1570+
///
1571+
/// let m = 10.0_f128;
1572+
/// let x = 4.0_f128;
1573+
/// let b = 60.0_f128;
1574+
///
1575+
/// assert_eq!(m.mul_add(x, b), 100.0);
1576+
/// assert_eq!(m * x + b, 100.0);
1577+
///
1578+
/// let one_plus_eps = 1.0_f128 + f128::EPSILON;
1579+
/// let one_minus_eps = 1.0_f128 - f128::EPSILON;
1580+
/// let minus_one = -1.0_f128;
1581+
///
1582+
/// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps.
1583+
/// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON);
1584+
/// // Different rounding with the non-fused multiply and add.
1585+
/// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0);
1586+
/// # }
1587+
/// ```
1588+
#[inline]
1589+
#[rustc_allow_incoherent_impl]
1590+
#[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")]
1591+
#[unstable(feature = "f128", issue = "116909")]
1592+
#[must_use = "method returns a new number and does not mutate the original value"]
1593+
pub fn mul_add(self, a: f128, b: f128) -> f128 {
1594+
// SAFETY: intrinsic with no preconditions
1595+
unsafe { intrinsics::fmaf128(self, a, b) }
1596+
}
1597+
1598+
/// Calculates Euclidean division, the matching method for `rem_euclid`.
1599+
///
1600+
/// This computes the integer `n` such that
1601+
/// `self = n * rhs + self.rem_euclid(rhs)`.
1602+
/// In other words, the result is `self / rhs` rounded to the integer `n`
1603+
/// such that `self >= n * rhs`.
1604+
///
1605+
/// # Precision
1606+
///
1607+
/// The result of this operation is guaranteed to be the rounded
1608+
/// infinite-precision result.
1609+
///
1610+
/// # Examples
1611+
///
1612+
/// ```
1613+
/// #![feature(f128)]
1614+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1615+
///
1616+
/// let a: f128 = 7.0;
1617+
/// let b = 4.0;
1618+
/// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0
1619+
/// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0
1620+
/// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0
1621+
/// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0
1622+
/// # }
1623+
/// ```
1624+
#[inline]
1625+
#[rustc_allow_incoherent_impl]
1626+
#[unstable(feature = "f128", issue = "116909")]
1627+
#[must_use = "method returns a new number and does not mutate the original value"]
1628+
pub fn div_euclid(self, rhs: f128) -> f128 {
1629+
let q = (self / rhs).trunc();
1630+
if self % rhs < 0.0 {
1631+
return if rhs > 0.0 { q - 1.0 } else { q + 1.0 };
1632+
}
1633+
q
1634+
}
1635+
1636+
/// Calculates the least nonnegative remainder of `self (mod rhs)`.
1637+
///
1638+
/// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in
1639+
/// most cases. However, due to a floating point round-off error it can
1640+
/// result in `r == rhs.abs()`, violating the mathematical definition, if
1641+
/// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`.
1642+
/// This result is not an element of the function's codomain, but it is the
1643+
/// closest floating point number in the real numbers and thus fulfills the
1644+
/// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)`
1645+
/// approximately.
1646+
///
1647+
/// # Precision
1648+
///
1649+
/// The result of this operation is guaranteed to be the rounded
1650+
/// infinite-precision result.
1651+
///
1652+
/// # Examples
1653+
///
1654+
/// ```
1655+
/// #![feature(f128)]
1656+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1657+
///
1658+
/// let a: f128 = 7.0;
1659+
/// let b = 4.0;
1660+
/// assert_eq!(a.rem_euclid(b), 3.0);
1661+
/// assert_eq!((-a).rem_euclid(b), 1.0);
1662+
/// assert_eq!(a.rem_euclid(-b), 3.0);
1663+
/// assert_eq!((-a).rem_euclid(-b), 1.0);
1664+
/// // limitation due to round-off error
1665+
/// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0);
1666+
/// # }
1667+
/// ```
1668+
#[inline]
1669+
#[rustc_allow_incoherent_impl]
1670+
#[doc(alias = "modulo", alias = "mod")]
1671+
#[unstable(feature = "f128", issue = "116909")]
1672+
#[must_use = "method returns a new number and does not mutate the original value"]
1673+
pub fn rem_euclid(self, rhs: f128) -> f128 {
1674+
let r = self % rhs;
1675+
if r < 0.0 { r + rhs.abs() } else { r }
1676+
}
1677+
1678+
/// Raises a number to an integer power.
1679+
///
1680+
/// Using this function is generally faster than using `powf`.
1681+
/// It might have a different sequence of rounding operations than `powf`,
1682+
/// so the results are not guaranteed to agree.
1683+
///
1684+
/// # Unspecified precision
1685+
///
1686+
/// The precision of this function is non-deterministic. This means it varies by platform,
1687+
/// Rust version, and can even differ within the same execution from one invocation to the next.
1688+
///
1689+
/// # Examples
1690+
///
1691+
/// ```
1692+
/// #![feature(f128)]
1693+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1694+
///
1695+
/// let x = 2.0_f128;
1696+
/// let abs_difference = (x.powi(2) - (x * x)).abs();
1697+
/// assert!(abs_difference <= f128::EPSILON);
1698+
///
1699+
/// assert_eq!(f128::powi(f128::NAN, 0), 1.0);
1700+
/// # }
1701+
/// ```
1702+
#[inline]
1703+
#[rustc_allow_incoherent_impl]
1704+
#[unstable(feature = "f128", issue = "116909")]
1705+
#[must_use = "method returns a new number and does not mutate the original value"]
1706+
pub fn powi(self, n: i32) -> f128 {
1707+
// SAFETY: intrinsic with no preconditions
1708+
unsafe { intrinsics::powif128(self, n) }
1709+
}
1710+
1711+
/// Returns the square root of a number.
1712+
///
1713+
/// Returns NaN if `self` is a negative number other than `-0.0`.
1714+
///
1715+
/// # Precision
1716+
///
1717+
/// The result of this operation is guaranteed to be the rounded
1718+
/// infinite-precision result. It is specified by IEEE 754 as `squareRoot`
1719+
/// and guaranteed not to change.
1720+
///
1721+
/// # Examples
1722+
///
1723+
/// ```
1724+
/// #![feature(f128)]
1725+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
1726+
///
1727+
/// let positive = 4.0_f128;
1728+
/// let negative = -4.0_f128;
1729+
/// let negative_zero = -0.0_f128;
1730+
///
1731+
/// assert_eq!(positive.sqrt(), 2.0);
1732+
/// assert!(negative.sqrt().is_nan());
1733+
/// assert!(negative_zero.sqrt() == negative_zero);
1734+
/// # }
1735+
/// ```
1736+
#[inline]
1737+
#[doc(alias = "squareRoot")]
1738+
#[rustc_allow_incoherent_impl]
1739+
#[unstable(feature = "f128", issue = "116909")]
1740+
#[must_use = "method returns a new number and does not mutate the original value"]
1741+
pub fn sqrt(self) -> f128 {
1742+
// SAFETY: intrinsic with no preconditions
1743+
unsafe { intrinsics::sqrtf128(self) }
1744+
}
1745+
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.