Skip to content

Commit f3a832d

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 43755fb commit f3a832d

File tree

4 files changed

+411
-2
lines changed

4 files changed

+411
-2
lines changed

library/core/src/num/f128.rs

+181
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,187 @@ impl f128 {
686686
self * RADS_PER_DEG
687687
}
688688

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

library/core/src/num/f16.rs

+176
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,182 @@ impl f16 {
720720
self * RADS_PER_DEG
721721
}
722722

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

0 commit comments

Comments
 (0)