Skip to content

Commit 6a99a10

Browse files
committed
Add core functions for f16 and f128 that require math routines
`min`, `max`, and similar functions require external math routines. Add these under the same gates as `std` math functions (`reliable_f16_math` and `reliable_f128_math`).
1 parent 4824e0e commit 6a99a10

File tree

4 files changed

+411
-2
lines changed

4 files changed

+411
-2
lines changed

library/core/src/num/f128.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,187 @@ impl f128 {
704704
self * RADS_PER_DEG
705705
}
706706

707+
/// Returns the maximum of the two numbers, ignoring NaN.
708+
///
709+
/// If one of the arguments is NaN, then the other argument is returned.
710+
/// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs;
711+
/// this function handles all NaNs the same way and avoids maxNum's problems with associativity.
712+
/// This also matches the behavior of libm’s fmax.
713+
///
714+
/// ```
715+
/// #![feature(f128)]
716+
/// # // Using aarch64 because `reliable_f128_math` is needed
717+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
718+
///
719+
/// let x = 1.0f128;
720+
/// let y = 2.0f128;
721+
///
722+
/// assert_eq!(x.max(y), y);
723+
/// # }
724+
/// ```
725+
#[inline]
726+
#[cfg(not(bootstrap))]
727+
#[unstable(feature = "f128", issue = "116909")]
728+
#[must_use = "this returns the result of the comparison, without modifying either input"]
729+
pub fn max(self, other: f128) -> f128 {
730+
intrinsics::maxnumf128(self, other)
731+
}
732+
733+
/// Returns the minimum of the two numbers, ignoring NaN.
734+
///
735+
/// If one of the arguments is NaN, then the other argument is returned.
736+
/// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs;
737+
/// this function handles all NaNs the same way and avoids minNum's problems with associativity.
738+
/// This also matches the behavior of libm’s fmin.
739+
///
740+
/// ```
741+
/// #![feature(f128)]
742+
/// # // Using aarch64 because `reliable_f128_math` is needed
743+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
744+
///
745+
/// let x = 1.0f128;
746+
/// let y = 2.0f128;
747+
///
748+
/// assert_eq!(x.min(y), x);
749+
/// # }
750+
/// ```
751+
#[inline]
752+
#[cfg(not(bootstrap))]
753+
#[unstable(feature = "f128", issue = "116909")]
754+
#[must_use = "this returns the result of the comparison, without modifying either input"]
755+
pub fn min(self, other: f128) -> f128 {
756+
intrinsics::minnumf128(self, other)
757+
}
758+
759+
/// Returns the maximum of the two numbers, propagating NaN.
760+
///
761+
/// This returns NaN when *either* argument is NaN, as opposed to
762+
/// [`f128::max`] which only returns NaN when *both* arguments are NaN.
763+
///
764+
/// ```
765+
/// #![feature(f128)]
766+
/// #![feature(float_minimum_maximum)]
767+
/// # // Using aarch64 because `reliable_f128_math` is needed
768+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
769+
///
770+
/// let x = 1.0f128;
771+
/// let y = 2.0f128;
772+
///
773+
/// assert_eq!(x.maximum(y), y);
774+
/// assert!(x.maximum(f128::NAN).is_nan());
775+
/// # }
776+
/// ```
777+
///
778+
/// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater
779+
/// of the two numbers. For this operation, -0.0 is considered to be less than +0.0.
780+
/// Note that this follows the semantics specified in IEEE 754-2019.
781+
///
782+
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
783+
/// operand is conserved; see [explanation of NaN as a special value](f128) for more info.
784+
#[inline]
785+
#[cfg(not(bootstrap))]
786+
#[unstable(feature = "f128", issue = "116909")]
787+
// #[unstable(feature = "float_minimum_maximum", issue = "91079")]
788+
#[must_use = "this returns the result of the comparison, without modifying either input"]
789+
pub fn maximum(self, other: f128) -> f128 {
790+
if self > other {
791+
self
792+
} else if other > self {
793+
other
794+
} else if self == other {
795+
if self.is_sign_positive() && other.is_sign_negative() { self } else { other }
796+
} else {
797+
self + other
798+
}
799+
}
800+
801+
/// Returns the minimum of the two numbers, propagating NaN.
802+
///
803+
/// This returns NaN when *either* argument is NaN, as opposed to
804+
/// [`f128::min`] which only returns NaN when *both* arguments are NaN.
805+
///
806+
/// ```
807+
/// #![feature(f128)]
808+
/// #![feature(float_minimum_maximum)]
809+
/// # // Using aarch64 because `reliable_f128_math` is needed
810+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
811+
///
812+
/// let x = 1.0f128;
813+
/// let y = 2.0f128;
814+
///
815+
/// assert_eq!(x.minimum(y), x);
816+
/// assert!(x.minimum(f128::NAN).is_nan());
817+
/// # }
818+
/// ```
819+
///
820+
/// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser
821+
/// of the two numbers. For this operation, -0.0 is considered to be less than +0.0.
822+
/// Note that this follows the semantics specified in IEEE 754-2019.
823+
///
824+
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
825+
/// operand is conserved; see [explanation of NaN as a special value](f128) for more info.
826+
#[inline]
827+
#[cfg(not(bootstrap))]
828+
#[unstable(feature = "f128", issue = "116909")]
829+
// #[unstable(feature = "float_minimum_maximum", issue = "91079")]
830+
#[must_use = "this returns the result of the comparison, without modifying either input"]
831+
pub fn minimum(self, other: f128) -> f128 {
832+
if self < other {
833+
self
834+
} else if other < self {
835+
other
836+
} else if self == other {
837+
if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
838+
} else {
839+
// At least one input is NaN. Use `+` to perform NaN propagation and quieting.
840+
self + other
841+
}
842+
}
843+
844+
/// Calculates the middle point of `self` and `rhs`.
845+
///
846+
/// This returns NaN when *either* argument is NaN or if a combination of
847+
/// +inf and -inf is provided as arguments.
848+
///
849+
/// # Examples
850+
///
851+
/// ```
852+
/// #![feature(f128)]
853+
/// #![feature(num_midpoint)]
854+
/// # // Using aarch64 because `reliable_f128_math` is needed
855+
/// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
856+
///
857+
/// assert_eq!(1f128.midpoint(4.0), 2.5);
858+
/// assert_eq!((-5.5f128).midpoint(8.0), 1.25);
859+
/// # }
860+
/// ```
861+
#[inline]
862+
#[cfg(not(bootstrap))]
863+
#[unstable(feature = "f128", issue = "116909")]
864+
// #[unstable(feature = "num_midpoint", issue = "110840")]
865+
pub fn midpoint(self, other: f128) -> f128 {
866+
const LO: f128 = f128::MIN_POSITIVE * 2.;
867+
const HI: f128 = f128::MAX / 2.;
868+
869+
let (a, b) = (self, other);
870+
let abs_a = a.abs_private();
871+
let abs_b = b.abs_private();
872+
873+
if abs_a <= HI && abs_b <= HI {
874+
// Overflow is impossible
875+
(a + b) / 2.
876+
} else if abs_a < LO {
877+
// Not safe to halve a
878+
a + (b / 2.)
879+
} else if abs_b < LO {
880+
// Not safe to halve b
881+
(a / 2.) + b
882+
} else {
883+
// Not safe to halve a and b
884+
(a / 2.) + (b / 2.)
885+
}
886+
}
887+
707888
/// Rounds toward zero and converts to any primitive integer type,
708889
/// assuming that the value is finite and fits in that type.
709890
///

library/core/src/num/f16.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,182 @@ impl f16 {
739739
self * RADS_PER_DEG
740740
}
741741

742+
/// Returns the maximum of the two numbers, ignoring NaN.
743+
///
744+
/// If one of the arguments is NaN, then the other argument is returned.
745+
/// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs;
746+
/// this function handles all NaNs the same way and avoids maxNum's problems with associativity.
747+
/// This also matches the behavior of libm’s fmax.
748+
///
749+
/// ```
750+
/// #![feature(f16)]
751+
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
752+
///
753+
/// let x = 1.0f16;
754+
/// let y = 2.0f16;
755+
///
756+
/// assert_eq!(x.max(y), y);
757+
/// # }
758+
/// ```
759+
#[inline]
760+
#[cfg(not(bootstrap))]
761+
#[unstable(feature = "f16", issue = "116909")]
762+
#[must_use = "this returns the result of the comparison, without modifying either input"]
763+
pub fn max(self, other: f16) -> f16 {
764+
intrinsics::maxnumf16(self, other)
765+
}
766+
767+
/// Returns the minimum of the two numbers, ignoring NaN.
768+
///
769+
/// If one of the arguments is NaN, then the other argument is returned.
770+
/// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs;
771+
/// this function handles all NaNs the same way and avoids minNum's problems with associativity.
772+
/// This also matches the behavior of libm’s fmin.
773+
///
774+
/// ```
775+
/// #![feature(f16)]
776+
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
777+
///
778+
/// let x = 1.0f16;
779+
/// let y = 2.0f16;
780+
///
781+
/// assert_eq!(x.min(y), x);
782+
/// # }
783+
/// ```
784+
#[inline]
785+
#[cfg(not(bootstrap))]
786+
#[unstable(feature = "f16", issue = "116909")]
787+
#[must_use = "this returns the result of the comparison, without modifying either input"]
788+
pub fn min(self, other: f16) -> f16 {
789+
intrinsics::minnumf16(self, other)
790+
}
791+
792+
/// Returns the maximum of the two numbers, propagating NaN.
793+
///
794+
/// This returns NaN when *either* argument is NaN, as opposed to
795+
/// [`f16::max`] which only returns NaN when *both* arguments are NaN.
796+
///
797+
/// ```
798+
/// #![feature(f16)]
799+
/// #![feature(float_minimum_maximum)]
800+
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
801+
///
802+
/// let x = 1.0f16;
803+
/// let y = 2.0f16;
804+
///
805+
/// assert_eq!(x.maximum(y), y);
806+
/// assert!(x.maximum(f16::NAN).is_nan());
807+
/// # }
808+
/// ```
809+
///
810+
/// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater
811+
/// of the two numbers. For this operation, -0.0 is considered to be less than +0.0.
812+
/// Note that this follows the semantics specified in IEEE 754-2019.
813+
///
814+
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
815+
/// operand is conserved; see [explanation of NaN as a special value](f16) for more info.
816+
#[inline]
817+
#[cfg(not(bootstrap))]
818+
#[unstable(feature = "f16", issue = "116909")]
819+
// #[unstable(feature = "float_minimum_maximum", issue = "91079")]
820+
#[must_use = "this returns the result of the comparison, without modifying either input"]
821+
pub fn maximum(self, other: f16) -> f16 {
822+
if self > other {
823+
self
824+
} else if other > self {
825+
other
826+
} else if self == other {
827+
if self.is_sign_positive() && other.is_sign_negative() { self } else { other }
828+
} else {
829+
self + other
830+
}
831+
}
832+
833+
/// Returns the minimum of the two numbers, propagating NaN.
834+
///
835+
/// This returns NaN when *either* argument is NaN, as opposed to
836+
/// [`f16::min`] which only returns NaN when *both* arguments are NaN.
837+
///
838+
/// ```
839+
/// #![feature(f16)]
840+
/// #![feature(float_minimum_maximum)]
841+
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
842+
///
843+
/// let x = 1.0f16;
844+
/// let y = 2.0f16;
845+
///
846+
/// assert_eq!(x.minimum(y), x);
847+
/// assert!(x.minimum(f16::NAN).is_nan());
848+
/// # }
849+
/// ```
850+
///
851+
/// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser
852+
/// of the two numbers. For this operation, -0.0 is considered to be less than +0.0.
853+
/// Note that this follows the semantics specified in IEEE 754-2019.
854+
///
855+
/// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN
856+
/// operand is conserved; see [explanation of NaN as a special value](f16) for more info.
857+
#[inline]
858+
#[cfg(not(bootstrap))]
859+
#[unstable(feature = "f16", issue = "116909")]
860+
// #[unstable(feature = "float_minimum_maximum", issue = "91079")]
861+
#[must_use = "this returns the result of the comparison, without modifying either input"]
862+
pub fn minimum(self, other: f16) -> f16 {
863+
if self < other {
864+
self
865+
} else if other < self {
866+
other
867+
} else if self == other {
868+
if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
869+
} else {
870+
// At least one input is NaN. Use `+` to perform NaN propagation and quieting.
871+
self + other
872+
}
873+
}
874+
875+
/// Calculates the middle point of `self` and `rhs`.
876+
///
877+
/// This returns NaN when *either* argument is NaN or if a combination of
878+
/// +inf and -inf is provided as arguments.
879+
///
880+
/// # Examples
881+
///
882+
/// ```
883+
/// #![feature(f16)]
884+
/// #![feature(num_midpoint)]
885+
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
886+
///
887+
/// assert_eq!(1f16.midpoint(4.0), 2.5);
888+
/// assert_eq!((-5.5f16).midpoint(8.0), 1.25);
889+
/// # }
890+
/// ```
891+
#[inline]
892+
#[cfg(not(bootstrap))]
893+
#[unstable(feature = "f16", issue = "116909")]
894+
// #[unstable(feature = "num_midpoint", issue = "110840")]
895+
pub fn midpoint(self, other: f16) -> f16 {
896+
const LO: f16 = f16::MIN_POSITIVE * 2.;
897+
const HI: f16 = f16::MAX / 2.;
898+
899+
let (a, b) = (self, other);
900+
let abs_a = a.abs_private();
901+
let abs_b = b.abs_private();
902+
903+
if abs_a <= HI && abs_b <= HI {
904+
// Overflow is impossible
905+
(a + b) / 2.
906+
} else if abs_a < LO {
907+
// Not safe to halve a
908+
a + (b / 2.)
909+
} else if abs_b < LO {
910+
// Not safe to halve b
911+
(a / 2.) + b
912+
} else {
913+
// Not safe to halve a and b
914+
(a / 2.) + (b / 2.)
915+
}
916+
}
917+
742918
/// Rounds toward zero and converts to any primitive integer type,
743919
/// assuming that the value is finite and fits in that type.
744920
///

0 commit comments

Comments
 (0)