mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 00:34:06 +00:00
Replace in-tree rustc_apfloat
with the new version of the crate
This commit is contained in:
parent
52bdc37727
commit
15e9f56088
@ -38,11 +38,6 @@ Files: compiler/*
|
|||||||
Copyright: The Rust Project Developers (see https://thanks.rust-lang.org)
|
Copyright: The Rust Project Developers (see https://thanks.rust-lang.org)
|
||||||
License: MIT or Apache-2.0
|
License: MIT or Apache-2.0
|
||||||
|
|
||||||
Files: compiler/rustc_apfloat/*
|
|
||||||
Copyright: LLVM APFloat authors
|
|
||||||
The Rust Project Developers (see https://thanks.rust-lang.org)
|
|
||||||
License: NCSA AND (MIT OR Apache-2.0)
|
|
||||||
|
|
||||||
Files: compiler/rustc_codegen_cranelift/src/cranelift_native.rs
|
Files: compiler/rustc_codegen_cranelift/src/cranelift_native.rs
|
||||||
Copyright: The Cranelift Project Developers
|
Copyright: The Cranelift Project Developers
|
||||||
The Rust Project Developers (see https://thanks.rust-lang.org)
|
The Rust Project Developers (see https://thanks.rust-lang.org)
|
||||||
|
@ -3135,7 +3135,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_apfloat"
|
name = "rustc_apfloat"
|
||||||
version = "0.0.0"
|
version = "0.2.0+llvm-462a31f5a5ab"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "465187772033a5ee566f69fe008df03628fce549a0899aae76f0a0c2e34696be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@ -4725,9 +4727,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.10.0"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "snap"
|
name = "snap"
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "rustc_apfloat"
|
|
||||||
version = "0.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bitflags = "1.2.1"
|
|
||||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
|
File diff suppressed because it is too large
Load Diff
@ -1,695 +0,0 @@
|
|||||||
//! Port of LLVM's APFloat software floating-point implementation from the
|
|
||||||
//! following C++ sources (please update commit hash when backporting):
|
|
||||||
//! <https://github.com/llvm-mirror/llvm/tree/23efab2bbd424ed13495a420ad8641cb2c6c28f9>
|
|
||||||
//!
|
|
||||||
//! * `include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits
|
|
||||||
//! * `lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules
|
|
||||||
//! * `unittests/ADT/APFloatTest.cpp` -> `tests` directory
|
|
||||||
//!
|
|
||||||
//! The port contains no unsafe code, global state, or side-effects in general,
|
|
||||||
//! and the only allocations are in the conversion to/from decimal strings.
|
|
||||||
//!
|
|
||||||
//! Most of the API and the testcases are intact in some form or another,
|
|
||||||
//! with some ergonomic changes, such as idiomatic short names, returning
|
|
||||||
//! new values instead of mutating the receiver, and having separate method
|
|
||||||
//! variants that take a non-default rounding mode (with the suffix `_r`).
|
|
||||||
//! Comments have been preserved where possible, only slightly adapted.
|
|
||||||
//!
|
|
||||||
//! Instead of keeping a pointer to a configuration struct and inspecting it
|
|
||||||
//! dynamically on every operation, types (e.g., `ieee::Double`), traits
|
|
||||||
//! (e.g., `ieee::Semantics`) and associated constants are employed for
|
|
||||||
//! increased type safety and performance.
|
|
||||||
//!
|
|
||||||
//! On-heap bigints are replaced everywhere (except in decimal conversion),
|
|
||||||
//! with short arrays of `type Limb = u128` elements (instead of `u64`),
|
|
||||||
//! This allows fitting the largest supported significands in one integer
|
|
||||||
//! (`ieee::Quad` and `ppc::Fallback` use slightly less than 128 bits).
|
|
||||||
//! All of the functions in the `ieee::sig` module operate on slices.
|
|
||||||
//!
|
|
||||||
//! # Note
|
|
||||||
//!
|
|
||||||
//! This API is completely unstable and subject to change.
|
|
||||||
|
|
||||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
|
||||||
#![no_std]
|
|
||||||
#![forbid(unsafe_code)]
|
|
||||||
#![deny(rustc::untranslatable_diagnostic)]
|
|
||||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
use core::fmt;
|
|
||||||
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
|
|
||||||
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
|
|
||||||
use core::str::FromStr;
|
|
||||||
|
|
||||||
bitflags::bitflags! {
|
|
||||||
/// IEEE-754R 7: Default exception handling.
|
|
||||||
///
|
|
||||||
/// UNDERFLOW or OVERFLOW are always returned or-ed with INEXACT.
|
|
||||||
#[must_use]
|
|
||||||
pub struct Status: u8 {
|
|
||||||
const OK = 0x00;
|
|
||||||
const INVALID_OP = 0x01;
|
|
||||||
const DIV_BY_ZERO = 0x02;
|
|
||||||
const OVERFLOW = 0x04;
|
|
||||||
const UNDERFLOW = 0x08;
|
|
||||||
const INEXACT = 0x10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
|
||||||
pub struct StatusAnd<T> {
|
|
||||||
pub status: Status,
|
|
||||||
pub value: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Status {
|
|
||||||
pub fn and<T>(self, value: T) -> StatusAnd<T> {
|
|
||||||
StatusAnd { status: self, value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> StatusAnd<T> {
|
|
||||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> StatusAnd<U> {
|
|
||||||
StatusAnd { status: self.status, value: f(self.value) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! unpack {
|
|
||||||
($status:ident|=, $e:expr) => {
|
|
||||||
match $e {
|
|
||||||
$crate::StatusAnd { status, value } => {
|
|
||||||
$status |= status;
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($status:ident=, $e:expr) => {
|
|
||||||
match $e {
|
|
||||||
$crate::StatusAnd { status, value } => {
|
|
||||||
$status = status;
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Category of internally-represented number.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub enum Category {
|
|
||||||
Infinity,
|
|
||||||
NaN,
|
|
||||||
Normal,
|
|
||||||
Zero,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// IEEE-754R 4.3: Rounding-direction attributes.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub enum Round {
|
|
||||||
NearestTiesToEven,
|
|
||||||
TowardPositive,
|
|
||||||
TowardNegative,
|
|
||||||
TowardZero,
|
|
||||||
NearestTiesToAway,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Neg for Round {
|
|
||||||
type Output = Round;
|
|
||||||
fn neg(self) -> Round {
|
|
||||||
match self {
|
|
||||||
Round::TowardPositive => Round::TowardNegative,
|
|
||||||
Round::TowardNegative => Round::TowardPositive,
|
|
||||||
Round::NearestTiesToEven | Round::TowardZero | Round::NearestTiesToAway => self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A signed type to represent a floating point number's unbiased exponent.
|
|
||||||
pub type ExpInt = i16;
|
|
||||||
|
|
||||||
// \c ilogb error results.
|
|
||||||
pub const IEK_INF: ExpInt = ExpInt::MAX;
|
|
||||||
pub const IEK_NAN: ExpInt = ExpInt::MIN;
|
|
||||||
pub const IEK_ZERO: ExpInt = ExpInt::MIN + 1;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub struct ParseError(pub &'static str);
|
|
||||||
|
|
||||||
/// A self-contained host- and target-independent arbitrary-precision
|
|
||||||
/// floating-point software implementation.
|
|
||||||
///
|
|
||||||
/// `apfloat` uses significand bignum integer arithmetic as provided by functions
|
|
||||||
/// in the `ieee::sig`.
|
|
||||||
///
|
|
||||||
/// Written for clarity rather than speed, in particular with a view to use in
|
|
||||||
/// the front-end of a cross compiler so that target arithmetic can be correctly
|
|
||||||
/// performed on the host. Performance should nonetheless be reasonable,
|
|
||||||
/// particularly for its intended use. It may be useful as a base
|
|
||||||
/// implementation for a run-time library during development of a faster
|
|
||||||
/// target-specific one.
|
|
||||||
///
|
|
||||||
/// All 5 rounding modes in the IEEE-754R draft are handled correctly for all
|
|
||||||
/// implemented operations. Currently implemented operations are add, subtract,
|
|
||||||
/// multiply, divide, fused-multiply-add, conversion-to-float,
|
|
||||||
/// conversion-to-integer and conversion-from-integer. New rounding modes
|
|
||||||
/// (e.g., away from zero) can be added with three or four lines of code.
|
|
||||||
///
|
|
||||||
/// Four formats are built-in: IEEE single precision, double precision,
|
|
||||||
/// quadruple precision, and x87 80-bit extended double (when operating with
|
|
||||||
/// full extended precision). Adding a new format that obeys IEEE semantics
|
|
||||||
/// only requires adding two lines of code: a declaration and definition of the
|
|
||||||
/// format.
|
|
||||||
///
|
|
||||||
/// All operations return the status of that operation as an exception bit-mask,
|
|
||||||
/// so multiple operations can be done consecutively with their results or-ed
|
|
||||||
/// together. The returned status can be useful for compiler diagnostics; e.g.,
|
|
||||||
/// inexact, underflow and overflow can be easily diagnosed on constant folding,
|
|
||||||
/// and compiler optimizers can determine what exceptions would be raised by
|
|
||||||
/// folding operations and optimize, or perhaps not optimize, accordingly.
|
|
||||||
///
|
|
||||||
/// At present, underflow tininess is detected after rounding; it should be
|
|
||||||
/// straight forward to add support for the before-rounding case too.
|
|
||||||
///
|
|
||||||
/// The library reads hexadecimal floating point numbers as per C99, and
|
|
||||||
/// correctly rounds if necessary according to the specified rounding mode.
|
|
||||||
/// Syntax is required to have been validated by the caller.
|
|
||||||
///
|
|
||||||
/// It also reads decimal floating point numbers and correctly rounds according
|
|
||||||
/// to the specified rounding mode.
|
|
||||||
///
|
|
||||||
/// Non-zero finite numbers are represented internally as a sign bit, a 16-bit
|
|
||||||
/// signed exponent, and the significand as an array of integer limbs. After
|
|
||||||
/// normalization of a number of precision P the exponent is within the range of
|
|
||||||
/// the format, and if the number is not denormal the P-th bit of the
|
|
||||||
/// significand is set as an explicit integer bit. For denormals the most
|
|
||||||
/// significant bit is shifted right so that the exponent is maintained at the
|
|
||||||
/// format's minimum, so that the smallest denormal has just the least
|
|
||||||
/// significant bit of the significand set. The sign of zeros and infinities
|
|
||||||
/// is significant; the exponent and significand of such numbers is not stored,
|
|
||||||
/// but has a known implicit (deterministic) value: 0 for the significands, 0
|
|
||||||
/// for zero exponent, all 1 bits for infinity exponent. For NaNs the sign and
|
|
||||||
/// significand are deterministic, although not really meaningful, and preserved
|
|
||||||
/// in non-conversion operations. The exponent is implicitly all 1 bits.
|
|
||||||
///
|
|
||||||
/// `apfloat` does not provide any exception handling beyond default exception
|
|
||||||
/// handling. We represent Signaling NaNs via IEEE-754R 2008 6.2.1 should clause
|
|
||||||
/// by encoding Signaling NaNs with the first bit of its trailing significand
|
|
||||||
/// as 0.
|
|
||||||
///
|
|
||||||
/// Future work
|
|
||||||
/// ===========
|
|
||||||
///
|
|
||||||
/// Some features that may or may not be worth adding:
|
|
||||||
///
|
|
||||||
/// Optional ability to detect underflow tininess before rounding.
|
|
||||||
///
|
|
||||||
/// New formats: x87 in single and double precision mode (IEEE apart from
|
|
||||||
/// extended exponent range) (hard).
|
|
||||||
///
|
|
||||||
/// New operations: sqrt, nexttoward.
|
|
||||||
///
|
|
||||||
pub trait Float:
|
|
||||||
Copy
|
|
||||||
+ Default
|
|
||||||
+ FromStr<Err = ParseError>
|
|
||||||
+ PartialOrd
|
|
||||||
+ fmt::Display
|
|
||||||
+ Neg<Output = Self>
|
|
||||||
+ AddAssign
|
|
||||||
+ SubAssign
|
|
||||||
+ MulAssign
|
|
||||||
+ DivAssign
|
|
||||||
+ RemAssign
|
|
||||||
+ Add<Output = StatusAnd<Self>>
|
|
||||||
+ Sub<Output = StatusAnd<Self>>
|
|
||||||
+ Mul<Output = StatusAnd<Self>>
|
|
||||||
+ Div<Output = StatusAnd<Self>>
|
|
||||||
+ Rem<Output = StatusAnd<Self>>
|
|
||||||
{
|
|
||||||
/// Total number of bits in the in-memory format.
|
|
||||||
const BITS: usize;
|
|
||||||
|
|
||||||
/// Number of bits in the significand. This includes the integer bit.
|
|
||||||
const PRECISION: usize;
|
|
||||||
|
|
||||||
/// The largest E such that 2<sup>E</sup> is representable; this matches the
|
|
||||||
/// definition of IEEE 754.
|
|
||||||
const MAX_EXP: ExpInt;
|
|
||||||
|
|
||||||
/// The smallest E such that 2<sup>E</sup> is a normalized number; this
|
|
||||||
/// matches the definition of IEEE 754.
|
|
||||||
const MIN_EXP: ExpInt;
|
|
||||||
|
|
||||||
/// Positive Zero.
|
|
||||||
const ZERO: Self;
|
|
||||||
|
|
||||||
/// Positive Infinity.
|
|
||||||
const INFINITY: Self;
|
|
||||||
|
|
||||||
/// NaN (Not a Number).
|
|
||||||
// FIXME(eddyb) provide a default when qnan becomes const fn.
|
|
||||||
const NAN: Self;
|
|
||||||
|
|
||||||
/// Factory for QNaN values.
|
|
||||||
// FIXME(eddyb) should be const fn.
|
|
||||||
fn qnan(payload: Option<u128>) -> Self;
|
|
||||||
|
|
||||||
/// Factory for SNaN values.
|
|
||||||
// FIXME(eddyb) should be const fn.
|
|
||||||
fn snan(payload: Option<u128>) -> Self;
|
|
||||||
|
|
||||||
/// Largest finite number.
|
|
||||||
// FIXME(eddyb) should be const (but FloatPair::largest is nontrivial).
|
|
||||||
fn largest() -> Self;
|
|
||||||
|
|
||||||
/// Smallest (by magnitude) finite number.
|
|
||||||
/// Might be denormalized, which implies a relative loss of precision.
|
|
||||||
const SMALLEST: Self;
|
|
||||||
|
|
||||||
/// Smallest (by magnitude) normalized finite number.
|
|
||||||
// FIXME(eddyb) should be const (but FloatPair::smallest_normalized is nontrivial).
|
|
||||||
fn smallest_normalized() -> Self;
|
|
||||||
|
|
||||||
// Arithmetic
|
|
||||||
|
|
||||||
fn add_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
|
|
||||||
fn sub_r(self, rhs: Self, round: Round) -> StatusAnd<Self> {
|
|
||||||
self.add_r(-rhs, round)
|
|
||||||
}
|
|
||||||
fn mul_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
|
|
||||||
fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self>;
|
|
||||||
fn mul_add(self, multiplicand: Self, addend: Self) -> StatusAnd<Self> {
|
|
||||||
self.mul_add_r(multiplicand, addend, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
fn div_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
|
|
||||||
/// IEEE remainder.
|
|
||||||
// This is not currently correct in all cases.
|
|
||||||
fn ieee_rem(self, rhs: Self) -> StatusAnd<Self> {
|
|
||||||
let mut v = self;
|
|
||||||
|
|
||||||
let status;
|
|
||||||
v = unpack!(status=, v / rhs);
|
|
||||||
if status == Status::DIV_BY_ZERO {
|
|
||||||
return status.and(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(Self::PRECISION < 128);
|
|
||||||
|
|
||||||
let status;
|
|
||||||
let x = unpack!(status=, v.to_i128_r(128, Round::NearestTiesToEven, &mut false));
|
|
||||||
if status == Status::INVALID_OP {
|
|
||||||
return status.and(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
let status;
|
|
||||||
let mut v = unpack!(status=, Self::from_i128(x));
|
|
||||||
assert_eq!(status, Status::OK); // should always work
|
|
||||||
|
|
||||||
let status;
|
|
||||||
v = unpack!(status=, v * rhs);
|
|
||||||
assert_eq!(status - Status::INEXACT, Status::OK); // should not overflow or underflow
|
|
||||||
|
|
||||||
let status;
|
|
||||||
v = unpack!(status=, self - v);
|
|
||||||
assert_eq!(status - Status::INEXACT, Status::OK); // likewise
|
|
||||||
|
|
||||||
if v.is_zero() {
|
|
||||||
status.and(v.copy_sign(self)) // IEEE754 requires this
|
|
||||||
} else {
|
|
||||||
status.and(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// C fmod, or llvm frem.
|
|
||||||
fn c_fmod(self, rhs: Self) -> StatusAnd<Self>;
|
|
||||||
fn round_to_integral(self, round: Round) -> StatusAnd<Self>;
|
|
||||||
|
|
||||||
/// IEEE-754R 2008 5.3.1: nextUp.
|
|
||||||
fn next_up(self) -> StatusAnd<Self>;
|
|
||||||
|
|
||||||
/// IEEE-754R 2008 5.3.1: nextDown.
|
|
||||||
///
|
|
||||||
/// *NOTE* since nextDown(x) = -nextUp(-x), we only implement nextUp with
|
|
||||||
/// appropriate sign switching before/after the computation.
|
|
||||||
fn next_down(self) -> StatusAnd<Self> {
|
|
||||||
(-self).next_up().map(|r| -r)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn abs(self) -> Self {
|
|
||||||
if self.is_negative() { -self } else { self }
|
|
||||||
}
|
|
||||||
fn copy_sign(self, rhs: Self) -> Self {
|
|
||||||
if self.is_negative() != rhs.is_negative() { -self } else { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conversions
|
|
||||||
fn from_bits(input: u128) -> Self;
|
|
||||||
fn from_i128_r(input: i128, round: Round) -> StatusAnd<Self> {
|
|
||||||
if input < 0 {
|
|
||||||
Self::from_u128_r(input.wrapping_neg() as u128, -round).map(|r| -r)
|
|
||||||
} else {
|
|
||||||
Self::from_u128_r(input as u128, round)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn from_i128(input: i128) -> StatusAnd<Self> {
|
|
||||||
Self::from_i128_r(input, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self>;
|
|
||||||
fn from_u128(input: u128) -> StatusAnd<Self> {
|
|
||||||
Self::from_u128_r(input, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError>;
|
|
||||||
fn to_bits(self) -> u128;
|
|
||||||
|
|
||||||
/// Converts a floating point number to an integer according to the
|
|
||||||
/// rounding mode. In case of an invalid operation exception,
|
|
||||||
/// deterministic values are returned, namely zero for NaNs and the
|
|
||||||
/// minimal or maximal value respectively for underflow or overflow.
|
|
||||||
/// If the rounded value is in range but the floating point number is
|
|
||||||
/// not the exact integer, the C standard doesn't require an inexact
|
|
||||||
/// exception to be raised. IEEE-854 does require it so we do that.
|
|
||||||
///
|
|
||||||
/// Note that for conversions to integer type the C standard requires
|
|
||||||
/// round-to-zero to always be used.
|
|
||||||
///
|
|
||||||
/// The *is_exact output tells whether the result is exact, in the sense
|
|
||||||
/// that converting it back to the original floating point type produces
|
|
||||||
/// the original value. This is almost equivalent to `result == Status::OK`,
|
|
||||||
/// except for negative zeroes.
|
|
||||||
fn to_i128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<i128> {
|
|
||||||
let status;
|
|
||||||
if self.is_negative() {
|
|
||||||
if self.is_zero() {
|
|
||||||
// Negative zero can't be represented as an int.
|
|
||||||
*is_exact = false;
|
|
||||||
}
|
|
||||||
let r = unpack!(status=, (-self).to_u128_r(width, -round, is_exact));
|
|
||||||
|
|
||||||
// Check for values that don't fit in the signed integer.
|
|
||||||
if r > (1 << (width - 1)) {
|
|
||||||
// Return the most negative integer for the given width.
|
|
||||||
*is_exact = false;
|
|
||||||
Status::INVALID_OP.and(-1 << (width - 1))
|
|
||||||
} else {
|
|
||||||
status.and(r.wrapping_neg() as i128)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Positive case is simpler, can pretend it's a smaller unsigned
|
|
||||||
// integer, and `to_u128` will take care of all the edge cases.
|
|
||||||
self.to_u128_r(width - 1, round, is_exact).map(|r| r as i128)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn to_i128(self, width: usize) -> StatusAnd<i128> {
|
|
||||||
self.to_i128_r(width, Round::TowardZero, &mut true)
|
|
||||||
}
|
|
||||||
fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128>;
|
|
||||||
fn to_u128(self, width: usize) -> StatusAnd<u128> {
|
|
||||||
self.to_u128_r(width, Round::TowardZero, &mut true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmp_abs_normal(self, rhs: Self) -> Ordering;
|
|
||||||
|
|
||||||
/// Bitwise comparison for equality (QNaNs compare equal, 0!=-0).
|
|
||||||
fn bitwise_eq(self, rhs: Self) -> bool;
|
|
||||||
|
|
||||||
// IEEE-754R 5.7.2 General operations.
|
|
||||||
|
|
||||||
/// Implements IEEE minNum semantics. Returns the smaller of the 2 arguments if
|
|
||||||
/// both are not NaN. If either argument is a NaN, returns the other argument.
|
|
||||||
fn min(self, other: Self) -> Self {
|
|
||||||
if self.is_nan() {
|
|
||||||
other
|
|
||||||
} else if other.is_nan() {
|
|
||||||
self
|
|
||||||
} else if other.partial_cmp(&self) == Some(Ordering::Less) {
|
|
||||||
other
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements IEEE maxNum semantics. Returns the larger of the 2 arguments if
|
|
||||||
/// both are not NaN. If either argument is a NaN, returns the other argument.
|
|
||||||
fn max(self, other: Self) -> Self {
|
|
||||||
if self.is_nan() {
|
|
||||||
other
|
|
||||||
} else if other.is_nan() {
|
|
||||||
self
|
|
||||||
} else if self.partial_cmp(&other) == Some(Ordering::Less) {
|
|
||||||
other
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// IEEE-754R isSignMinus: Returns whether the current value is
|
|
||||||
/// negative.
|
|
||||||
///
|
|
||||||
/// This applies to zeros and NaNs as well.
|
|
||||||
fn is_negative(self) -> bool;
|
|
||||||
|
|
||||||
/// IEEE-754R isNormal: Returns whether the current value is normal.
|
|
||||||
///
|
|
||||||
/// This implies that the current value of the float is not zero, subnormal,
|
|
||||||
/// infinite, or NaN following the definition of normality from IEEE-754R.
|
|
||||||
fn is_normal(self) -> bool {
|
|
||||||
!self.is_denormal() && self.is_finite_non_zero()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the current value is zero, subnormal, or
|
|
||||||
/// normal.
|
|
||||||
///
|
|
||||||
/// This means that the value is not infinite or NaN.
|
|
||||||
fn is_finite(self) -> bool {
|
|
||||||
!self.is_nan() && !self.is_infinite()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the float is plus or minus zero.
|
|
||||||
fn is_zero(self) -> bool {
|
|
||||||
self.category() == Category::Zero
|
|
||||||
}
|
|
||||||
|
|
||||||
/// IEEE-754R isSubnormal(): Returns whether the float is a
|
|
||||||
/// denormal.
|
|
||||||
fn is_denormal(self) -> bool;
|
|
||||||
|
|
||||||
/// IEEE-754R isInfinite(): Returns whether the float is infinity.
|
|
||||||
fn is_infinite(self) -> bool {
|
|
||||||
self.category() == Category::Infinity
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the float is a quiet or signaling NaN.
|
|
||||||
fn is_nan(self) -> bool {
|
|
||||||
self.category() == Category::NaN
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the float is a signaling NaN.
|
|
||||||
fn is_signaling(self) -> bool;
|
|
||||||
|
|
||||||
// Simple Queries
|
|
||||||
|
|
||||||
fn category(self) -> Category;
|
|
||||||
fn is_non_zero(self) -> bool {
|
|
||||||
!self.is_zero()
|
|
||||||
}
|
|
||||||
fn is_finite_non_zero(self) -> bool {
|
|
||||||
self.is_finite() && !self.is_zero()
|
|
||||||
}
|
|
||||||
fn is_pos_zero(self) -> bool {
|
|
||||||
self.is_zero() && !self.is_negative()
|
|
||||||
}
|
|
||||||
fn is_neg_zero(self) -> bool {
|
|
||||||
self.is_zero() && self.is_negative()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the number has the smallest possible non-zero
|
|
||||||
/// magnitude in the current semantics.
|
|
||||||
fn is_smallest(self) -> bool {
|
|
||||||
Self::SMALLEST.copy_sign(self).bitwise_eq(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the number has the largest possible finite
|
|
||||||
/// magnitude in the current semantics.
|
|
||||||
fn is_largest(self) -> bool {
|
|
||||||
Self::largest().copy_sign(self).bitwise_eq(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the number is an exact integer.
|
|
||||||
fn is_integer(self) -> bool {
|
|
||||||
// This could be made more efficient; I'm going for obviously correct.
|
|
||||||
if !self.is_finite() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
self.round_to_integral(Round::TowardZero).value.bitwise_eq(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If this value has an exact multiplicative inverse, return it.
|
|
||||||
fn get_exact_inverse(self) -> Option<Self>;
|
|
||||||
|
|
||||||
/// Returns the exponent of the internal representation of the Float.
|
|
||||||
///
|
|
||||||
/// Because the radix of Float is 2, this is equivalent to floor(log2(x)).
|
|
||||||
/// For special Float values, this returns special error codes:
|
|
||||||
///
|
|
||||||
/// NaN -> \c IEK_NAN
|
|
||||||
/// 0 -> \c IEK_ZERO
|
|
||||||
/// Inf -> \c IEK_INF
|
|
||||||
///
|
|
||||||
fn ilogb(self) -> ExpInt;
|
|
||||||
|
|
||||||
/// Returns: self * 2<sup>exp</sup> for integral exponents.
|
|
||||||
/// Equivalent to C standard library function `ldexp`.
|
|
||||||
fn scalbn_r(self, exp: ExpInt, round: Round) -> Self;
|
|
||||||
fn scalbn(self, exp: ExpInt) -> Self {
|
|
||||||
self.scalbn_r(exp, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Equivalent to C standard library function with the same name.
|
|
||||||
///
|
|
||||||
/// While the C standard says exp is an unspecified value for infinity and nan,
|
|
||||||
/// this returns INT_MAX for infinities, and INT_MIN for NaNs (see `ilogb`).
|
|
||||||
fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self;
|
|
||||||
fn frexp(self, exp: &mut ExpInt) -> Self {
|
|
||||||
self.frexp_r(exp, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FloatConvert<T: Float>: Float {
|
|
||||||
/// Converts a value of one floating point type to another.
|
|
||||||
/// The return value corresponds to the IEEE754 exceptions. *loses_info
|
|
||||||
/// records whether the transformation lost information, i.e., whether
|
|
||||||
/// converting the result back to the original type will produce the
|
|
||||||
/// original value (this is almost the same as return `value == Status::OK`,
|
|
||||||
/// but there are edge cases where this is not so).
|
|
||||||
fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd<T>;
|
|
||||||
fn convert(self, loses_info: &mut bool) -> StatusAnd<T> {
|
|
||||||
self.convert_r(Round::NearestTiesToEven, loses_info)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! float_common_impls {
|
|
||||||
($ty:ident<$t:tt>) => {
|
|
||||||
impl<$t> Default for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::ZERO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::str::FromStr for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
type Err = ParseError;
|
|
||||||
fn from_str(s: &str) -> Result<Self, ParseError> {
|
|
||||||
Self::from_str_r(s, Round::NearestTiesToEven).map(|x| x.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rounding ties to the nearest even, by default.
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::Add for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
type Output = StatusAnd<Self>;
|
|
||||||
fn add(self, rhs: Self) -> StatusAnd<Self> {
|
|
||||||
self.add_r(rhs, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::Sub for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
type Output = StatusAnd<Self>;
|
|
||||||
fn sub(self, rhs: Self) -> StatusAnd<Self> {
|
|
||||||
self.sub_r(rhs, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::Mul for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
type Output = StatusAnd<Self>;
|
|
||||||
fn mul(self, rhs: Self) -> StatusAnd<Self> {
|
|
||||||
self.mul_r(rhs, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::Div for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
type Output = StatusAnd<Self>;
|
|
||||||
fn div(self, rhs: Self) -> StatusAnd<Self> {
|
|
||||||
self.div_r(rhs, Round::NearestTiesToEven)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::Rem for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
type Output = StatusAnd<Self>;
|
|
||||||
fn rem(self, rhs: Self) -> StatusAnd<Self> {
|
|
||||||
self.c_fmod(rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::AddAssign for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
|
||||||
*self = (*self + rhs).value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::SubAssign for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
fn sub_assign(&mut self, rhs: Self) {
|
|
||||||
*self = (*self - rhs).value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::MulAssign for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
fn mul_assign(&mut self, rhs: Self) {
|
|
||||||
*self = (*self * rhs).value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::DivAssign for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
fn div_assign(&mut self, rhs: Self) {
|
|
||||||
*self = (*self / rhs).value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$t> ::core::ops::RemAssign for $ty<$t>
|
|
||||||
where
|
|
||||||
Self: Float,
|
|
||||||
{
|
|
||||||
fn rem_assign(&mut self, rhs: Self) {
|
|
||||||
*self = (*self % rhs).value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod ieee;
|
|
||||||
pub mod ppc;
|
|
@ -1,434 +0,0 @@
|
|||||||
use crate::ieee;
|
|
||||||
use crate::{Category, ExpInt, Float, FloatConvert, ParseError, Round, Status, StatusAnd};
|
|
||||||
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
use core::fmt;
|
|
||||||
use core::ops::Neg;
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
|
|
||||||
pub struct DoubleFloat<F>(F, F);
|
|
||||||
pub type DoubleDouble = DoubleFloat<ieee::Double>;
|
|
||||||
|
|
||||||
// These are legacy semantics for the Fallback, inaccurate implementation of
|
|
||||||
// IBM double-double, if the accurate DoubleDouble doesn't handle the
|
|
||||||
// operation. It's equivalent to having an IEEE number with consecutive 106
|
|
||||||
// bits of mantissa and 11 bits of exponent.
|
|
||||||
//
|
|
||||||
// It's not equivalent to IBM double-double. For example, a legit IBM
|
|
||||||
// double-double, 1 + epsilon:
|
|
||||||
//
|
|
||||||
// 1 + epsilon = 1 + (1 >> 1076)
|
|
||||||
//
|
|
||||||
// is not representable by a consecutive 106 bits of mantissa.
|
|
||||||
//
|
|
||||||
// Currently, these semantics are used in the following way:
|
|
||||||
//
|
|
||||||
// DoubleDouble -> (Double, Double) ->
|
|
||||||
// DoubleDouble's Fallback -> IEEE operations
|
|
||||||
//
|
|
||||||
// FIXME: Implement all operations in DoubleDouble, and delete these
|
|
||||||
// semantics.
|
|
||||||
// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds.
|
|
||||||
pub struct FallbackS<F>(#[allow(unused)] F);
|
|
||||||
type Fallback<F> = ieee::IeeeFloat<FallbackS<F>>;
|
|
||||||
impl<F: Float> ieee::Semantics for FallbackS<F> {
|
|
||||||
// Forbid any conversion to/from bits.
|
|
||||||
const BITS: usize = 0;
|
|
||||||
const PRECISION: usize = F::PRECISION * 2;
|
|
||||||
const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt;
|
|
||||||
const MIN_EXP: ExpInt = F::MIN_EXP as ExpInt + F::PRECISION as ExpInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert number to F. To avoid spurious underflows, we re-
|
|
||||||
// normalize against the F exponent range first, and only *then*
|
|
||||||
// truncate the mantissa. The result of that second conversion
|
|
||||||
// may be inexact, but should never underflow.
|
|
||||||
// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds.
|
|
||||||
pub struct FallbackExtendedS<F>(#[allow(unused)] F);
|
|
||||||
type FallbackExtended<F> = ieee::IeeeFloat<FallbackExtendedS<F>>;
|
|
||||||
impl<F: Float> ieee::Semantics for FallbackExtendedS<F> {
|
|
||||||
// Forbid any conversion to/from bits.
|
|
||||||
const BITS: usize = 0;
|
|
||||||
const PRECISION: usize = Fallback::<F>::PRECISION;
|
|
||||||
const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: Float> From<Fallback<F>> for DoubleFloat<F>
|
|
||||||
where
|
|
||||||
F: FloatConvert<FallbackExtended<F>>,
|
|
||||||
FallbackExtended<F>: FloatConvert<F>,
|
|
||||||
{
|
|
||||||
fn from(x: Fallback<F>) -> Self {
|
|
||||||
let mut status;
|
|
||||||
let mut loses_info = false;
|
|
||||||
|
|
||||||
let extended: FallbackExtended<F> = unpack!(status=, x.convert(&mut loses_info));
|
|
||||||
assert_eq!((status, loses_info), (Status::OK, false));
|
|
||||||
|
|
||||||
let a = unpack!(status=, extended.convert(&mut loses_info));
|
|
||||||
assert_eq!(status - Status::INEXACT, Status::OK);
|
|
||||||
|
|
||||||
// If conversion was exact or resulted in a special case, we're done;
|
|
||||||
// just set the second double to zero. Otherwise, re-convert back to
|
|
||||||
// the extended format and compute the difference. This now should
|
|
||||||
// convert exactly to double.
|
|
||||||
let b = if a.is_finite_non_zero() && loses_info {
|
|
||||||
let u: FallbackExtended<F> = unpack!(status=, a.convert(&mut loses_info));
|
|
||||||
assert_eq!((status, loses_info), (Status::OK, false));
|
|
||||||
let v = unpack!(status=, extended - u);
|
|
||||||
assert_eq!(status, Status::OK);
|
|
||||||
let v = unpack!(status=, v.convert(&mut loses_info));
|
|
||||||
assert_eq!((status, loses_info), (Status::OK, false));
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
F::ZERO
|
|
||||||
};
|
|
||||||
|
|
||||||
DoubleFloat(a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FloatConvert<Self>> From<DoubleFloat<F>> for Fallback<F> {
|
|
||||||
fn from(DoubleFloat(a, b): DoubleFloat<F>) -> Self {
|
|
||||||
let mut status;
|
|
||||||
let mut loses_info = false;
|
|
||||||
|
|
||||||
// Get the first F and convert to our format.
|
|
||||||
let a = unpack!(status=, a.convert(&mut loses_info));
|
|
||||||
assert_eq!((status, loses_info), (Status::OK, false));
|
|
||||||
|
|
||||||
// Unless we have a special case, add in second F.
|
|
||||||
if a.is_finite_non_zero() {
|
|
||||||
let b = unpack!(status=, b.convert(&mut loses_info));
|
|
||||||
assert_eq!((status, loses_info), (Status::OK, false));
|
|
||||||
|
|
||||||
(a + b).value
|
|
||||||
} else {
|
|
||||||
a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float_common_impls!(DoubleFloat<F>);
|
|
||||||
|
|
||||||
impl<F: Float> Neg for DoubleFloat<F> {
|
|
||||||
type Output = Self;
|
|
||||||
fn neg(self) -> Self {
|
|
||||||
if self.1.is_finite_non_zero() {
|
|
||||||
DoubleFloat(-self.0, -self.1)
|
|
||||||
} else {
|
|
||||||
DoubleFloat(-self.0, self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FloatConvert<Fallback<F>>> fmt::Display for DoubleFloat<F> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt::Display::fmt(&Fallback::from(*self), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FloatConvert<Fallback<F>>> Float for DoubleFloat<F>
|
|
||||||
where
|
|
||||||
Self: From<Fallback<F>>,
|
|
||||||
{
|
|
||||||
const BITS: usize = F::BITS * 2;
|
|
||||||
const PRECISION: usize = Fallback::<F>::PRECISION;
|
|
||||||
const MAX_EXP: ExpInt = Fallback::<F>::MAX_EXP;
|
|
||||||
const MIN_EXP: ExpInt = Fallback::<F>::MIN_EXP;
|
|
||||||
|
|
||||||
const ZERO: Self = DoubleFloat(F::ZERO, F::ZERO);
|
|
||||||
|
|
||||||
const INFINITY: Self = DoubleFloat(F::INFINITY, F::ZERO);
|
|
||||||
|
|
||||||
// FIXME(eddyb) remove when qnan becomes const fn.
|
|
||||||
const NAN: Self = DoubleFloat(F::NAN, F::ZERO);
|
|
||||||
|
|
||||||
fn qnan(payload: Option<u128>) -> Self {
|
|
||||||
DoubleFloat(F::qnan(payload), F::ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn snan(payload: Option<u128>) -> Self {
|
|
||||||
DoubleFloat(F::snan(payload), F::ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn largest() -> Self {
|
|
||||||
let status;
|
|
||||||
let mut r = DoubleFloat(F::largest(), F::largest());
|
|
||||||
r.1 = r.1.scalbn(-(F::PRECISION as ExpInt + 1));
|
|
||||||
r.1 = unpack!(status=, r.1.next_down());
|
|
||||||
assert_eq!(status, Status::OK);
|
|
||||||
r
|
|
||||||
}
|
|
||||||
|
|
||||||
const SMALLEST: Self = DoubleFloat(F::SMALLEST, F::ZERO);
|
|
||||||
|
|
||||||
fn smallest_normalized() -> Self {
|
|
||||||
DoubleFloat(F::smallest_normalized().scalbn(F::PRECISION as ExpInt), F::ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement addition, subtraction, multiplication and division based on:
|
|
||||||
// "Software for Doubled-Precision Floating-Point Computations",
|
|
||||||
// by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283.
|
|
||||||
|
|
||||||
fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
|
|
||||||
match (self.category(), rhs.category()) {
|
|
||||||
(Category::Infinity, Category::Infinity) => {
|
|
||||||
if self.is_negative() != rhs.is_negative() {
|
|
||||||
Status::INVALID_OP.and(Self::NAN.copy_sign(self))
|
|
||||||
} else {
|
|
||||||
Status::OK.and(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => {
|
|
||||||
Status::OK.and(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Category::Zero, _) | (_, Category::NaN | Category::Infinity) => Status::OK.and(rhs),
|
|
||||||
|
|
||||||
(Category::Normal, Category::Normal) => {
|
|
||||||
let mut status = Status::OK;
|
|
||||||
let (a, aa, c, cc) = (self.0, self.1, rhs.0, rhs.1);
|
|
||||||
let mut z = a;
|
|
||||||
z = unpack!(status|=, z.add_r(c, round));
|
|
||||||
if !z.is_finite() {
|
|
||||||
if !z.is_infinite() {
|
|
||||||
return status.and(DoubleFloat(z, F::ZERO));
|
|
||||||
}
|
|
||||||
status = Status::OK;
|
|
||||||
let a_cmp_c = a.cmp_abs_normal(c);
|
|
||||||
z = cc;
|
|
||||||
z = unpack!(status|=, z.add_r(aa, round));
|
|
||||||
if a_cmp_c == Ordering::Greater {
|
|
||||||
// z = cc + aa + c + a;
|
|
||||||
z = unpack!(status|=, z.add_r(c, round));
|
|
||||||
z = unpack!(status|=, z.add_r(a, round));
|
|
||||||
} else {
|
|
||||||
// z = cc + aa + a + c;
|
|
||||||
z = unpack!(status|=, z.add_r(a, round));
|
|
||||||
z = unpack!(status|=, z.add_r(c, round));
|
|
||||||
}
|
|
||||||
if !z.is_finite() {
|
|
||||||
return status.and(DoubleFloat(z, F::ZERO));
|
|
||||||
}
|
|
||||||
self.0 = z;
|
|
||||||
let mut zz = aa;
|
|
||||||
zz = unpack!(status|=, zz.add_r(cc, round));
|
|
||||||
if a_cmp_c == Ordering::Greater {
|
|
||||||
// self.1 = a - z + c + zz;
|
|
||||||
self.1 = a;
|
|
||||||
self.1 = unpack!(status|=, self.1.sub_r(z, round));
|
|
||||||
self.1 = unpack!(status|=, self.1.add_r(c, round));
|
|
||||||
self.1 = unpack!(status|=, self.1.add_r(zz, round));
|
|
||||||
} else {
|
|
||||||
// self.1 = c - z + a + zz;
|
|
||||||
self.1 = c;
|
|
||||||
self.1 = unpack!(status|=, self.1.sub_r(z, round));
|
|
||||||
self.1 = unpack!(status|=, self.1.add_r(a, round));
|
|
||||||
self.1 = unpack!(status|=, self.1.add_r(zz, round));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// q = a - z;
|
|
||||||
let mut q = a;
|
|
||||||
q = unpack!(status|=, q.sub_r(z, round));
|
|
||||||
|
|
||||||
// zz = q + c + (a - (q + z)) + aa + cc;
|
|
||||||
// Compute a - (q + z) as -((q + z) - a) to avoid temporary copies.
|
|
||||||
let mut zz = q;
|
|
||||||
zz = unpack!(status|=, zz.add_r(c, round));
|
|
||||||
q = unpack!(status|=, q.add_r(z, round));
|
|
||||||
q = unpack!(status|=, q.sub_r(a, round));
|
|
||||||
q = -q;
|
|
||||||
zz = unpack!(status|=, zz.add_r(q, round));
|
|
||||||
zz = unpack!(status|=, zz.add_r(aa, round));
|
|
||||||
zz = unpack!(status|=, zz.add_r(cc, round));
|
|
||||||
if zz.is_zero() && !zz.is_negative() {
|
|
||||||
return Status::OK.and(DoubleFloat(z, F::ZERO));
|
|
||||||
}
|
|
||||||
self.0 = z;
|
|
||||||
self.0 = unpack!(status|=, self.0.add_r(zz, round));
|
|
||||||
if !self.0.is_finite() {
|
|
||||||
self.1 = F::ZERO;
|
|
||||||
return status.and(self);
|
|
||||||
}
|
|
||||||
self.1 = z;
|
|
||||||
self.1 = unpack!(status|=, self.1.sub_r(self.0, round));
|
|
||||||
self.1 = unpack!(status|=, self.1.add_r(zz, round));
|
|
||||||
}
|
|
||||||
status.and(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
|
|
||||||
// Interesting observation: For special categories, finding the lowest
|
|
||||||
// common ancestor of the following layered graph gives the correct
|
|
||||||
// return category:
|
|
||||||
//
|
|
||||||
// NaN
|
|
||||||
// / \
|
|
||||||
// Zero Inf
|
|
||||||
// \ /
|
|
||||||
// Normal
|
|
||||||
//
|
|
||||||
// e.g., NaN * NaN = NaN
|
|
||||||
// Zero * Inf = NaN
|
|
||||||
// Normal * Zero = Zero
|
|
||||||
// Normal * Inf = Inf
|
|
||||||
match (self.category(), rhs.category()) {
|
|
||||||
(Category::NaN, _) => Status::OK.and(self),
|
|
||||||
|
|
||||||
(_, Category::NaN) => Status::OK.and(rhs),
|
|
||||||
|
|
||||||
(Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => {
|
|
||||||
Status::OK.and(Self::NAN)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Category::Zero | Category::Infinity, _) => Status::OK.and(self),
|
|
||||||
|
|
||||||
(_, Category::Zero | Category::Infinity) => Status::OK.and(rhs),
|
|
||||||
|
|
||||||
(Category::Normal, Category::Normal) => {
|
|
||||||
let mut status = Status::OK;
|
|
||||||
let (a, b, c, d) = (self.0, self.1, rhs.0, rhs.1);
|
|
||||||
// t = a * c
|
|
||||||
let mut t = a;
|
|
||||||
t = unpack!(status|=, t.mul_r(c, round));
|
|
||||||
if !t.is_finite_non_zero() {
|
|
||||||
return status.and(DoubleFloat(t, F::ZERO));
|
|
||||||
}
|
|
||||||
|
|
||||||
// tau = fmsub(a, c, t), that is -fmadd(-a, c, t).
|
|
||||||
let mut tau = a;
|
|
||||||
tau = unpack!(status|=, tau.mul_add_r(c, -t, round));
|
|
||||||
// v = a * d
|
|
||||||
let mut v = a;
|
|
||||||
v = unpack!(status|=, v.mul_r(d, round));
|
|
||||||
// w = b * c
|
|
||||||
let mut w = b;
|
|
||||||
w = unpack!(status|=, w.mul_r(c, round));
|
|
||||||
v = unpack!(status|=, v.add_r(w, round));
|
|
||||||
// tau += v + w
|
|
||||||
tau = unpack!(status|=, tau.add_r(v, round));
|
|
||||||
// u = t + tau
|
|
||||||
let mut u = t;
|
|
||||||
u = unpack!(status|=, u.add_r(tau, round));
|
|
||||||
|
|
||||||
self.0 = u;
|
|
||||||
if !u.is_finite() {
|
|
||||||
self.1 = F::ZERO;
|
|
||||||
} else {
|
|
||||||
// self.1 = (t - u) + tau
|
|
||||||
t = unpack!(status|=, t.sub_r(u, round));
|
|
||||||
t = unpack!(status|=, t.add_r(tau, round));
|
|
||||||
self.1 = t;
|
|
||||||
}
|
|
||||||
status.and(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self> {
|
|
||||||
Fallback::from(self)
|
|
||||||
.mul_add_r(Fallback::from(multiplicand), Fallback::from(addend), round)
|
|
||||||
.map(Self::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn div_r(self, rhs: Self, round: Round) -> StatusAnd<Self> {
|
|
||||||
Fallback::from(self).div_r(Fallback::from(rhs), round).map(Self::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn c_fmod(self, rhs: Self) -> StatusAnd<Self> {
|
|
||||||
Fallback::from(self).c_fmod(Fallback::from(rhs)).map(Self::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn round_to_integral(self, round: Round) -> StatusAnd<Self> {
|
|
||||||
Fallback::from(self).round_to_integral(round).map(Self::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_up(self) -> StatusAnd<Self> {
|
|
||||||
Fallback::from(self).next_up().map(Self::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_bits(input: u128) -> Self {
|
|
||||||
let (a, b) = (input, input >> F::BITS);
|
|
||||||
DoubleFloat(F::from_bits(a & ((1 << F::BITS) - 1)), F::from_bits(b & ((1 << F::BITS) - 1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self> {
|
|
||||||
Fallback::from_u128_r(input, round).map(Self::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> {
|
|
||||||
Fallback::from_str_r(s, round).map(|r| r.map(Self::from))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_bits(self) -> u128 {
|
|
||||||
self.0.to_bits() | (self.1.to_bits() << F::BITS)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128> {
|
|
||||||
Fallback::from(self).to_u128_r(width, round, is_exact)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmp_abs_normal(self, rhs: Self) -> Ordering {
|
|
||||||
self.0.cmp_abs_normal(rhs.0).then_with(|| {
|
|
||||||
let result = self.1.cmp_abs_normal(rhs.1);
|
|
||||||
if result != Ordering::Equal {
|
|
||||||
let against = self.0.is_negative() ^ self.1.is_negative();
|
|
||||||
let rhs_against = rhs.0.is_negative() ^ rhs.1.is_negative();
|
|
||||||
(!against)
|
|
||||||
.cmp(&!rhs_against)
|
|
||||||
.then_with(|| if against { result.reverse() } else { result })
|
|
||||||
} else {
|
|
||||||
result
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bitwise_eq(self, rhs: Self) -> bool {
|
|
||||||
self.0.bitwise_eq(rhs.0) && self.1.bitwise_eq(rhs.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_negative(self) -> bool {
|
|
||||||
self.0.is_negative()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_denormal(self) -> bool {
|
|
||||||
self.category() == Category::Normal
|
|
||||||
&& (self.0.is_denormal() || self.0.is_denormal() ||
|
|
||||||
// (double)(Hi + Lo) == Hi defines a normal number.
|
|
||||||
!(self.0 + self.1).value.bitwise_eq(self.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_signaling(self) -> bool {
|
|
||||||
self.0.is_signaling()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn category(self) -> Category {
|
|
||||||
self.0.category()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_exact_inverse(self) -> Option<Self> {
|
|
||||||
Fallback::from(self).get_exact_inverse().map(Self::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ilogb(self) -> ExpInt {
|
|
||||||
self.0.ilogb()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scalbn_r(self, exp: ExpInt, round: Round) -> Self {
|
|
||||||
DoubleFloat(self.0.scalbn_r(exp, round), self.1.scalbn_r(exp, round))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self {
|
|
||||||
let a = self.0.frexp_r(exp, round);
|
|
||||||
let mut b = self.1;
|
|
||||||
if self.category() == Category::Normal {
|
|
||||||
b = b.scalbn_r(-*exp, round);
|
|
||||||
}
|
|
||||||
DoubleFloat(a, b)
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,530 +0,0 @@
|
|||||||
use rustc_apfloat::ppc::DoubleDouble;
|
|
||||||
use rustc_apfloat::{Category, Float, Round};
|
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double() {
|
|
||||||
let test = DoubleDouble::ZERO;
|
|
||||||
let expected = "0x0p+0".parse::<DoubleDouble>().unwrap();
|
|
||||||
assert!(test.is_zero());
|
|
||||||
assert!(!test.is_negative());
|
|
||||||
assert!(test.bitwise_eq(expected));
|
|
||||||
assert_eq!(0, test.to_bits());
|
|
||||||
|
|
||||||
let test = -DoubleDouble::ZERO;
|
|
||||||
let expected = "-0x0p+0".parse::<DoubleDouble>().unwrap();
|
|
||||||
assert!(test.is_zero());
|
|
||||||
assert!(test.is_negative());
|
|
||||||
assert!(test.bitwise_eq(expected));
|
|
||||||
assert_eq!(0x8000000000000000, test.to_bits());
|
|
||||||
|
|
||||||
let test = "1.0".parse::<DoubleDouble>().unwrap();
|
|
||||||
assert_eq!(0x3ff0000000000000, test.to_bits());
|
|
||||||
|
|
||||||
// LDBL_MAX
|
|
||||||
let test = "1.79769313486231580793728971405301e+308".parse::<DoubleDouble>().unwrap();
|
|
||||||
assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, test.to_bits());
|
|
||||||
|
|
||||||
// LDBL_MIN
|
|
||||||
let test = "2.00416836000897277799610805135016e-292".parse::<DoubleDouble>().unwrap();
|
|
||||||
assert_eq!(0x0000000000000000_0360000000000000, test.to_bits());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_add_special() {
|
|
||||||
let data = [
|
|
||||||
// (1 + 0) + (-1 + 0) = Category::Zero
|
|
||||||
(0x3ff0000000000000, 0xbff0000000000000, Category::Zero, Round::NearestTiesToEven),
|
|
||||||
// LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity
|
|
||||||
(
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
0x7948000000000000,
|
|
||||||
Category::Infinity,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// FIXME: change the 4th 0x75effffffffffffe to 0x75efffffffffffff when
|
|
||||||
// DoubleDouble's fallback is gone.
|
|
||||||
// LDBL_MAX + (1.011111... >> (1023 - 106) + (1.1111111...0 >> (1023 -
|
|
||||||
// 160))) = Category::Normal
|
|
||||||
(
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
0x75effffffffffffe_7947ffffffffffff,
|
|
||||||
Category::Normal,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity
|
|
||||||
(
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
Category::Infinity,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// NaN + (1 + 0) = Category::NaN
|
|
||||||
(0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected, round) in data {
|
|
||||||
{
|
|
||||||
let mut a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a1 = a1.add_r(a2, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a1.category(), "{:#x} + {:#x}", op1, op2);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let mut a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a2 = a2.add_r(a1, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a2.category(), "{:#x} + {:#x}", op2, op1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_add() {
|
|
||||||
let data = [
|
|
||||||
// (1 + 0) + (1e-105 + 0) = (1 + 1e-105)
|
|
||||||
(
|
|
||||||
0x3ff0000000000000,
|
|
||||||
0x3960000000000000,
|
|
||||||
0x3960000000000000_3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// (1 + 0) + (1e-106 + 0) = (1 + 1e-106)
|
|
||||||
(
|
|
||||||
0x3ff0000000000000,
|
|
||||||
0x3950000000000000,
|
|
||||||
0x3950000000000000_3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// (1 + 1e-106) + (1e-106 + 0) = (1 + 1e-105)
|
|
||||||
(
|
|
||||||
0x3950000000000000_3ff0000000000000,
|
|
||||||
0x3950000000000000,
|
|
||||||
0x3960000000000000_3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// (1 + 0) + (epsilon + 0) = (1 + epsilon)
|
|
||||||
(
|
|
||||||
0x3ff0000000000000,
|
|
||||||
0x0000000000000001,
|
|
||||||
0x0000000000000001_3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// FIXME: change 0xf950000000000000 to 0xf940000000000000, when
|
|
||||||
// DoubleDouble's fallback is gone.
|
|
||||||
// (DBL_MAX - 1 << (1023 - 105)) + (1 << (1023 - 53) + 0) = DBL_MAX +
|
|
||||||
// 1.11111... << (1023 - 52)
|
|
||||||
(
|
|
||||||
0xf950000000000000_7fefffffffffffff,
|
|
||||||
0x7c90000000000000,
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// FIXME: change 0xf950000000000000 to 0xf940000000000000, when
|
|
||||||
// DoubleDouble's fallback is gone.
|
|
||||||
// (1 << (1023 - 53) + 0) + (DBL_MAX - 1 << (1023 - 105)) = DBL_MAX +
|
|
||||||
// 1.11111... << (1023 - 52)
|
|
||||||
(
|
|
||||||
0x7c90000000000000,
|
|
||||||
0xf950000000000000_7fefffffffffffff,
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected, round) in data {
|
|
||||||
{
|
|
||||||
let mut a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a1 = a1.add_r(a2, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a1.to_bits(), "{:#x} + {:#x}", op1, op2);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let mut a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a2 = a2.add_r(a1, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a2.to_bits(), "{:#x} + {:#x}", op2, op1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_subtract() {
|
|
||||||
let data = [
|
|
||||||
// (1 + 0) - (-1e-105 + 0) = (1 + 1e-105)
|
|
||||||
(
|
|
||||||
0x3ff0000000000000,
|
|
||||||
0xb960000000000000,
|
|
||||||
0x3960000000000000_3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// (1 + 0) - (-1e-106 + 0) = (1 + 1e-106)
|
|
||||||
(
|
|
||||||
0x3ff0000000000000,
|
|
||||||
0xb950000000000000,
|
|
||||||
0x3950000000000000_3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected, round) in data {
|
|
||||||
let mut a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a1 = a1.sub_r(a2, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a1.to_bits(), "{:#x} - {:#x}", op1, op2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_multiply_special() {
|
|
||||||
let data = [
|
|
||||||
// Category::NaN * Category::NaN = Category::NaN
|
|
||||||
(0x7ff8000000000000, 0x7ff8000000000000, Category::NaN, Round::NearestTiesToEven),
|
|
||||||
// Category::NaN * Category::Zero = Category::NaN
|
|
||||||
(0x7ff8000000000000, 0, Category::NaN, Round::NearestTiesToEven),
|
|
||||||
// Category::NaN * Category::Infinity = Category::NaN
|
|
||||||
(0x7ff8000000000000, 0x7ff0000000000000, Category::NaN, Round::NearestTiesToEven),
|
|
||||||
// Category::NaN * Category::Normal = Category::NaN
|
|
||||||
(0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven),
|
|
||||||
// Category::Infinity * Category::Infinity = Category::Infinity
|
|
||||||
(0x7ff0000000000000, 0x7ff0000000000000, Category::Infinity, Round::NearestTiesToEven),
|
|
||||||
// Category::Infinity * Category::Zero = Category::NaN
|
|
||||||
(0x7ff0000000000000, 0, Category::NaN, Round::NearestTiesToEven),
|
|
||||||
// Category::Infinity * Category::Normal = Category::Infinity
|
|
||||||
(0x7ff0000000000000, 0x3ff0000000000000, Category::Infinity, Round::NearestTiesToEven),
|
|
||||||
// Category::Zero * Category::Zero = Category::Zero
|
|
||||||
(0, 0, Category::Zero, Round::NearestTiesToEven),
|
|
||||||
// Category::Zero * Category::Normal = Category::Zero
|
|
||||||
(0, 0x3ff0000000000000, Category::Zero, Round::NearestTiesToEven),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected, round) in data {
|
|
||||||
{
|
|
||||||
let mut a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a1 = a1.mul_r(a2, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a1.category(), "{:#x} * {:#x}", op1, op2);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let mut a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a2 = a2.mul_r(a1, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a2.category(), "{:#x} * {:#x}", op2, op1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_multiply() {
|
|
||||||
let data = [
|
|
||||||
// 1/3 * 3 = 1.0
|
|
||||||
(
|
|
||||||
0x3c75555555555556_3fd5555555555555,
|
|
||||||
0x4008000000000000,
|
|
||||||
0x3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// (1 + epsilon) * (1 + 0) = Category::Zero
|
|
||||||
(
|
|
||||||
0x0000000000000001_3ff0000000000000,
|
|
||||||
0x3ff0000000000000,
|
|
||||||
0x0000000000000001_3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// (1 + epsilon) * (1 + epsilon) = 1 + 2 * epsilon
|
|
||||||
(
|
|
||||||
0x0000000000000001_3ff0000000000000,
|
|
||||||
0x0000000000000001_3ff0000000000000,
|
|
||||||
0x0000000000000002_3ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// -(1 + epsilon) * (1 + epsilon) = -1
|
|
||||||
(
|
|
||||||
0x0000000000000001_bff0000000000000,
|
|
||||||
0x0000000000000001_3ff0000000000000,
|
|
||||||
0xbff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// (0.5 + 0) * (1 + 2 * epsilon) = 0.5 + epsilon
|
|
||||||
(
|
|
||||||
0x3fe0000000000000,
|
|
||||||
0x0000000000000002_3ff0000000000000,
|
|
||||||
0x0000000000000001_3fe0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// (0.5 + 0) * (1 + epsilon) = 0.5
|
|
||||||
(
|
|
||||||
0x3fe0000000000000,
|
|
||||||
0x0000000000000001_3ff0000000000000,
|
|
||||||
0x3fe0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// __LDBL_MAX__ * (1 + 1 << 106) = inf
|
|
||||||
(
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
0x3950000000000000_3ff0000000000000,
|
|
||||||
0x7ff0000000000000,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// __LDBL_MAX__ * (1 + 1 << 107) > __LDBL_MAX__, but not inf, yes =_=|||
|
|
||||||
(
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
0x3940000000000000_3ff0000000000000,
|
|
||||||
0x7c8fffffffffffff_7fefffffffffffff,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
// __LDBL_MAX__ * (1 + 1 << 108) = __LDBL_MAX__
|
|
||||||
(
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
0x3930000000000000_3ff0000000000000,
|
|
||||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected, round) in data {
|
|
||||||
{
|
|
||||||
let mut a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a1 = a1.mul_r(a2, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a1.to_bits(), "{:#x} * {:#x}", op1, op2);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let mut a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a2 = a2.mul_r(a1, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a2.to_bits(), "{:#x} * {:#x}", op2, op1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_divide() {
|
|
||||||
// FIXME: Only a sanity check for now. Add more edge cases when the
|
|
||||||
// double-double algorithm is implemented.
|
|
||||||
let data = [
|
|
||||||
// 1 / 3 = 1/3
|
|
||||||
(
|
|
||||||
0x3ff0000000000000,
|
|
||||||
0x4008000000000000,
|
|
||||||
0x3c75555555555556_3fd5555555555555,
|
|
||||||
Round::NearestTiesToEven,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected, round) in data {
|
|
||||||
let mut a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
a1 = a1.div_r(a2, round).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, a1.to_bits(), "{:#x} / {:#x}", op1, op2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_remainder() {
|
|
||||||
let data = [
|
|
||||||
// ieee_rem(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53)
|
|
||||||
(
|
|
||||||
0x3cb8000000000000_4008000000000000,
|
|
||||||
0x3ca4000000000000_3ff4000000000000,
|
|
||||||
0x3c90000000000000_3fe0000000000000,
|
|
||||||
),
|
|
||||||
// ieee_rem(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (-0.5 - 0.5 << 53)
|
|
||||||
(
|
|
||||||
0x3cb8000000000000_4008000000000000,
|
|
||||||
0x3cac000000000000_3ffc000000000000,
|
|
||||||
0xbc90000000000000_bfe0000000000000,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected) in data {
|
|
||||||
let a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
let result = a1.ieee_rem(a2).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, result.to_bits(), "ieee_rem({:#x}, {:#x})", op1, op2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_mod() {
|
|
||||||
let data = [
|
|
||||||
// mod(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53)
|
|
||||||
(
|
|
||||||
0x3cb8000000000000_4008000000000000,
|
|
||||||
0x3ca4000000000000_3ff4000000000000,
|
|
||||||
0x3c90000000000000_3fe0000000000000,
|
|
||||||
),
|
|
||||||
// mod(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (1.25 + 1.25 << 53)
|
|
||||||
// 0xbc98000000000000 doesn't seem right, but it's what we currently have.
|
|
||||||
// FIXME: investigate
|
|
||||||
(
|
|
||||||
0x3cb8000000000000_4008000000000000,
|
|
||||||
0x3cac000000000000_3ffc000000000000,
|
|
||||||
0xbc98000000000000_3ff4000000000001,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected) in data {
|
|
||||||
let a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
let r = (a1 % a2).value;
|
|
||||||
|
|
||||||
assert_eq!(expected, r.to_bits(), "fmod({:#x}, {:#x})", op1, op2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_fma() {
|
|
||||||
// Sanity check for now.
|
|
||||||
let mut a = "2".parse::<DoubleDouble>().unwrap();
|
|
||||||
a = a.mul_add("3".parse::<DoubleDouble>().unwrap(), "4".parse::<DoubleDouble>().unwrap()).value;
|
|
||||||
assert_eq!(Some(Ordering::Equal), "10".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_round_to_integral() {
|
|
||||||
{
|
|
||||||
let a = "1.5".parse::<DoubleDouble>().unwrap();
|
|
||||||
let a = a.round_to_integral(Round::NearestTiesToEven).value;
|
|
||||||
assert_eq!(Some(Ordering::Equal), "2".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let a = "2.5".parse::<DoubleDouble>().unwrap();
|
|
||||||
let a = a.round_to_integral(Round::NearestTiesToEven).value;
|
|
||||||
assert_eq!(Some(Ordering::Equal), "2".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_compare() {
|
|
||||||
let data = [
|
|
||||||
// (1 + 0) = (1 + 0)
|
|
||||||
(0x3ff0000000000000, 0x3ff0000000000000, Some(Ordering::Equal)),
|
|
||||||
// (1 + 0) < (1.00...1 + 0)
|
|
||||||
(0x3ff0000000000000, 0x3ff0000000000001, Some(Ordering::Less)),
|
|
||||||
// (1.00...1 + 0) > (1 + 0)
|
|
||||||
(0x3ff0000000000001, 0x3ff0000000000000, Some(Ordering::Greater)),
|
|
||||||
// (1 + 0) < (1 + epsilon)
|
|
||||||
(0x3ff0000000000000, 0x0000000000000001_3ff0000000000001, Some(Ordering::Less)),
|
|
||||||
// NaN != NaN
|
|
||||||
(0x7ff8000000000000, 0x7ff8000000000000, None),
|
|
||||||
// (1 + 0) != NaN
|
|
||||||
(0x3ff0000000000000, 0x7ff8000000000000, None),
|
|
||||||
// Inf = Inf
|
|
||||||
(0x7ff0000000000000, 0x7ff0000000000000, Some(Ordering::Equal)),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected) in data {
|
|
||||||
let a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
assert_eq!(expected, a1.partial_cmp(&a2), "compare({:#x}, {:#x})", op1, op2,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_bitwise_eq() {
|
|
||||||
let data = [
|
|
||||||
// (1 + 0) = (1 + 0)
|
|
||||||
(0x3ff0000000000000, 0x3ff0000000000000, true),
|
|
||||||
// (1 + 0) != (1.00...1 + 0)
|
|
||||||
(0x3ff0000000000000, 0x3ff0000000000001, false),
|
|
||||||
// NaN = NaN
|
|
||||||
(0x7ff8000000000000, 0x7ff8000000000000, true),
|
|
||||||
// NaN != NaN with a different bit pattern
|
|
||||||
(0x7ff8000000000000, 0x3ff0000000000000_7ff8000000000000, false),
|
|
||||||
// Inf = Inf
|
|
||||||
(0x7ff0000000000000, 0x7ff0000000000000, true),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (op1, op2, expected) in data {
|
|
||||||
let a1 = DoubleDouble::from_bits(op1);
|
|
||||||
let a2 = DoubleDouble::from_bits(op2);
|
|
||||||
assert_eq!(expected, a1.bitwise_eq(a2), "{:#x} = {:#x}", op1, op2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_change_sign() {
|
|
||||||
let float = DoubleDouble::from_bits(0xbcb0000000000000_400f000000000000);
|
|
||||||
{
|
|
||||||
let actual = float.copy_sign("1".parse::<DoubleDouble>().unwrap());
|
|
||||||
assert_eq!(0xbcb0000000000000_400f000000000000, actual.to_bits());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let actual = float.copy_sign("-1".parse::<DoubleDouble>().unwrap());
|
|
||||||
assert_eq!(0x3cb0000000000000_c00f000000000000, actual.to_bits());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_factories() {
|
|
||||||
assert_eq!(0, DoubleDouble::ZERO.to_bits());
|
|
||||||
assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, DoubleDouble::largest().to_bits());
|
|
||||||
assert_eq!(0x0000000000000001, DoubleDouble::SMALLEST.to_bits());
|
|
||||||
assert_eq!(0x0360000000000000, DoubleDouble::smallest_normalized().to_bits());
|
|
||||||
assert_eq!(0x0000000000000000_8000000000000000, (-DoubleDouble::ZERO).to_bits());
|
|
||||||
assert_eq!(0xfc8ffffffffffffe_ffefffffffffffff, (-DoubleDouble::largest()).to_bits());
|
|
||||||
assert_eq!(0x0000000000000000_8000000000000001, (-DoubleDouble::SMALLEST).to_bits());
|
|
||||||
assert_eq!(
|
|
||||||
0x0000000000000000_8360000000000000,
|
|
||||||
(-DoubleDouble::smallest_normalized()).to_bits()
|
|
||||||
);
|
|
||||||
assert!(DoubleDouble::SMALLEST.is_smallest());
|
|
||||||
assert!(DoubleDouble::largest().is_largest());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_is_denormal() {
|
|
||||||
assert!(DoubleDouble::SMALLEST.is_denormal());
|
|
||||||
assert!(!DoubleDouble::largest().is_denormal());
|
|
||||||
assert!(!DoubleDouble::smallest_normalized().is_denormal());
|
|
||||||
{
|
|
||||||
// (4 + 3) is not normalized
|
|
||||||
let data = 0x4008000000000000_4010000000000000;
|
|
||||||
assert!(DoubleDouble::from_bits(data).is_denormal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_exact_inverse() {
|
|
||||||
assert!(
|
|
||||||
"2.0"
|
|
||||||
.parse::<DoubleDouble>()
|
|
||||||
.unwrap()
|
|
||||||
.get_exact_inverse()
|
|
||||||
.unwrap()
|
|
||||||
.bitwise_eq("0.5".parse::<DoubleDouble>().unwrap())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_scalbn() {
|
|
||||||
// 3.0 + 3.0 << 53
|
|
||||||
let input = 0x3cb8000000000000_4008000000000000;
|
|
||||||
let result = DoubleDouble::from_bits(input).scalbn(1);
|
|
||||||
// 6.0 + 6.0 << 53
|
|
||||||
assert_eq!(0x3cc8000000000000_4018000000000000, result.to_bits());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ppc_double_double_frexp() {
|
|
||||||
// 3.0 + 3.0 << 53
|
|
||||||
let input = 0x3cb8000000000000_4008000000000000;
|
|
||||||
let mut exp = 0;
|
|
||||||
// 0.75 + 0.75 << 53
|
|
||||||
let result = DoubleDouble::from_bits(input).frexp(&mut exp);
|
|
||||||
assert_eq!(2, exp);
|
|
||||||
assert_eq!(0x3c98000000000000_3fe8000000000000, result.to_bits());
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
either = "1"
|
either = "1"
|
||||||
rustc_apfloat = { path = "../rustc_apfloat" }
|
rustc_apfloat = "0.2.0"
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
rustc_attr = { path = "../rustc_attr" }
|
rustc_attr = { path = "../rustc_attr" }
|
||||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||||
|
@ -13,7 +13,7 @@ gsgdt = "0.1.2"
|
|||||||
field-offset = "0.3.5"
|
field-offset = "0.3.5"
|
||||||
measureme = "10.0.0"
|
measureme = "10.0.0"
|
||||||
polonius-engine = "0.13.0"
|
polonius-engine = "0.13.0"
|
||||||
rustc_apfloat = { path = "../rustc_apfloat" }
|
rustc_apfloat = "0.2.0"
|
||||||
rustc_arena = { path = "../rustc_arena" }
|
rustc_arena = { path = "../rustc_arena" }
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
rustc_attr = { path = "../rustc_attr" }
|
rustc_attr = { path = "../rustc_attr" }
|
||||||
|
@ -10,7 +10,7 @@ rustc_arena = { path = "../rustc_arena" }
|
|||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
either = "1"
|
either = "1"
|
||||||
rustc_middle = { path = "../rustc_middle" }
|
rustc_middle = { path = "../rustc_middle" }
|
||||||
rustc_apfloat = { path = "../rustc_apfloat" }
|
rustc_apfloat = "0.2.0"
|
||||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||||
rustc_index = { path = "../rustc_index" }
|
rustc_index = { path = "../rustc_index" }
|
||||||
rustc_errors = { path = "../rustc_errors" }
|
rustc_errors = { path = "../rustc_errors" }
|
||||||
|
@ -46,6 +46,7 @@ const EXCEPTIONS: &[(&str, &str)] = &[
|
|||||||
("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
|
("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
|
||||||
("mdbook", "MPL-2.0"), // mdbook
|
("mdbook", "MPL-2.0"), // mdbook
|
||||||
("openssl", "Apache-2.0"), // opt-dist
|
("openssl", "Apache-2.0"), // opt-dist
|
||||||
|
("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses)
|
||||||
("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde)
|
("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde)
|
||||||
("self_cell", "Apache-2.0"), // rustc (fluent translations)
|
("self_cell", "Apache-2.0"), // rustc (fluent translations)
|
||||||
("snap", "BSD-3-Clause"), // rustc
|
("snap", "BSD-3-Clause"), // rustc
|
||||||
@ -224,6 +225,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
|||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustc-rayon",
|
"rustc-rayon",
|
||||||
"rustc-rayon-core",
|
"rustc-rayon-core",
|
||||||
|
"rustc_apfloat",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"rustix",
|
"rustix",
|
||||||
"ruzstd", // via object in thorin-dwp
|
"ruzstd", // via object in thorin-dwp
|
||||||
|
@ -302,10 +302,6 @@ pub fn check(path: &Path, bad: &mut bool) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// apfloat shouldn't be changed because of license problems
|
|
||||||
if is_in(file, "compiler", "rustc_apfloat") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr");
|
let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr");
|
||||||
let mut skip_undocumented_unsafe =
|
let mut skip_undocumented_unsafe =
|
||||||
contains_ignore_directive(can_contain, &contents, "undocumented-unsafe");
|
contains_ignore_directive(can_contain, &contents, "undocumented-unsafe");
|
||||||
|
@ -318,14 +318,6 @@ changelog-branch = "master"
|
|||||||
|
|
||||||
[shortcut]
|
[shortcut]
|
||||||
|
|
||||||
|
|
||||||
[mentions."compiler/rustc_apfloat"]
|
|
||||||
message = """
|
|
||||||
Changes rustc_apfloat. rustc_apfloat is currently in limbo and you almost \
|
|
||||||
certainly don't want to change it (see #55993).
|
|
||||||
"""
|
|
||||||
cc = ["@eddyb"]
|
|
||||||
|
|
||||||
[mentions."compiler/rustc_codegen_cranelift"]
|
[mentions."compiler/rustc_codegen_cranelift"]
|
||||||
cc = ["@bjorn3"]
|
cc = ["@bjorn3"]
|
||||||
|
|
||||||
@ -609,7 +601,6 @@ style-team = [
|
|||||||
"/Cargo.lock" = ["@Mark-Simulacrum"]
|
"/Cargo.lock" = ["@Mark-Simulacrum"]
|
||||||
"/Cargo.toml" = ["@Mark-Simulacrum"]
|
"/Cargo.toml" = ["@Mark-Simulacrum"]
|
||||||
"/compiler" = ["compiler"]
|
"/compiler" = ["compiler"]
|
||||||
"/compiler/rustc_apfloat" = ["@eddyb"]
|
|
||||||
"/compiler/rustc_ast" = ["compiler", "parser"]
|
"/compiler/rustc_ast" = ["compiler", "parser"]
|
||||||
"/compiler/rustc_ast_lowering" = ["compiler", "ast_lowering"]
|
"/compiler/rustc_ast_lowering" = ["compiler", "ast_lowering"]
|
||||||
"/compiler/rustc_hir_analysis" = ["compiler", "types"]
|
"/compiler/rustc_hir_analysis" = ["compiler", "types"]
|
||||||
|
Loading…
Reference in New Issue
Block a user