Skip to content

Commit 5d7906c

Browse files
authored
Rollup merge of rust-lang#128417 - tgross35:f16-f128-math, r=dtolnay
Add `f16` and `f128` math functions This adds intrinsics and math functions for `f16` and `f128` floating point types. Support is quite limited and some things are broken so tests don't run on many platforms, but this provides a starting point.
2 parents 6dc79bb + e844eff commit 5d7906c

File tree

13 files changed

+4151
-100
lines changed

13 files changed

+4151
-100
lines changed

Diff for: core/src/intrinsics.rs

+289
Large diffs are not rendered by default.

Diff for: core/src/num/f128.rs

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

Diff for: core/src/num/f16.rs

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

Diff for: core/src/num/f32.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1070,13 +1070,13 @@ impl f32 {
10701070
// Overflow is impossible
10711071
(a + b) / 2.
10721072
} else if abs_a < LO {
1073-
// Not safe to halve a
1073+
// Not safe to halve `a` (would underflow)
10741074
a + (b / 2.)
10751075
} else if abs_b < LO {
1076-
// Not safe to halve b
1076+
// Not safe to halve `b` (would underflow)
10771077
(a / 2.) + b
10781078
} else {
1079-
// Not safe to halve a and b
1079+
// Safe to halve `a` and `b`
10801080
(a / 2.) + (b / 2.)
10811081
}
10821082
}

Diff for: core/src/num/f64.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1064,13 +1064,13 @@ impl f64 {
10641064
// Overflow is impossible
10651065
(a + b) / 2.
10661066
} else if abs_a < LO {
1067-
// Not safe to halve a
1067+
// Not safe to halve `a` (would underflow)
10681068
a + (b / 2.)
10691069
} else if abs_b < LO {
1070-
// Not safe to halve b
1070+
// Not safe to halve `b` (would underflow)
10711071
(a / 2.) + b
10721072
} else {
1073-
// Not safe to halve a and b
1073+
// Safe to halve `a` and `b`
10741074
(a / 2.) + (b / 2.)
10751075
}
10761076
}

0 commit comments

Comments
 (0)