Add classify and related methods for f16 and f128 · model-checking/verify-rust-std@f710e38 (original) (raw)

`@@ -13,6 +13,7 @@

`

13

13

``

14

14

`use crate::convert::FloatToInt;

`

15

15

`use crate::mem;

`

``

16

`+

use crate::num::FpCategory;

`

16

17

``

17

18

`/// Basic mathematical constants.

`

18

19

`#[unstable(feature = "f16", issue = "116909")]

`

`@@ -244,7 +245,13 @@ impl f16 {

`

244

245

``

245

246

`/// Sign bit

`

246

247

`#[cfg(not(bootstrap))]

`

247

``

`-

const SIGN_MASK: u16 = 0x8000;

`

``

248

`+

pub(crate) const SIGN_MASK: u16 = 0x8000;

`

``

249

+

``

250

`+

/// Exponent mask

`

``

251

`+

pub(crate) const EXP_MASK: u16 = 0x7c00;

`

``

252

+

``

253

`+

/// Mantissa mask

`

``

254

`+

pub(crate) const MAN_MASK: u16 = 0x03ff;

`

248

255

``

249

256

`/// Minimum representable positive value (min subnormal)

`

250

257

`#[cfg(not(bootstrap))]

`

`@@ -344,6 +351,159 @@ impl f16 {

`

344

351

`self.abs_private() < Self::INFINITY

`

345

352

`}

`

346

353

``

``

354

`` +

/// Returns true if the number is [subnormal].

``

``

355

`+

///

`

``

356


/// ```

``

357

`+

/// #![feature(f16)]

`

``

358

`+

/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885

`

``

359

`+

///

`

``

360

`+

/// let min = f16::MIN_POSITIVE; // 6.1035e-5

`

``

361

`+

/// let max = f16::MAX;

`

``

362

`+

/// let lower_than_min = 1.0e-7_f16;

`

``

363

`+

/// let zero = 0.0_f16;

`

``

364

`+

///

`

``

365

`+

/// assert!(!min.is_subnormal());

`

``

366

`+

/// assert!(!max.is_subnormal());

`

``

367

`+

///

`

``

368

`+

/// assert!(!zero.is_subnormal());

`

``

369

`+

/// assert!(!f16::NAN.is_subnormal());

`

``

370

`+

/// assert!(!f16::INFINITY.is_subnormal());

`

``

371

`` +

/// // Values between 0 and min are Subnormal.

``

``

372

`+

/// assert!(lower_than_min.is_subnormal());

`

``

373

`+

/// # }

`

``

374


/// ```

``

375

`+

/// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number

`

``

376

`+

#[inline]

`

``

377

`+

#[must_use]

`

``

378

`+

#[cfg(not(bootstrap))]

`

``

379

`+

#[unstable(feature = "f16", issue = "116909")]

`

``

380

`+

#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]

`

``

381

`+

pub const fn is_subnormal(self) -> bool {

`

``

382

`+

matches!(self.classify(), FpCategory::Subnormal)

`

``

383

`+

}

`

``

384

+

``

385

`` +

/// Returns true if the number is neither zero, infinite, [subnormal], or NaN.

``

``

386

`+

///

`

``

387


/// ```

``

388

`+

/// #![feature(f16)]

`

``

389

`+

/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885

`

``

390

`+

///

`

``

391

`+

/// let min = f16::MIN_POSITIVE; // 6.1035e-5

`

``

392

`+

/// let max = f16::MAX;

`

``

393

`+

/// let lower_than_min = 1.0e-7_f16;

`

``

394

`+

/// let zero = 0.0_f16;

`

``

395

`+

///

`

``

396

`+

/// assert!(min.is_normal());

`

``

397

`+

/// assert!(max.is_normal());

`

``

398

`+

///

`

``

399

`+

/// assert!(!zero.is_normal());

`

``

400

`+

/// assert!(!f16::NAN.is_normal());

`

``

401

`+

/// assert!(!f16::INFINITY.is_normal());

`

``

402

`` +

/// // Values between 0 and min are Subnormal.

``

``

403

`+

/// assert!(!lower_than_min.is_normal());

`

``

404

`+

/// # }

`

``

405


/// ```

``

406

`+

/// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number

`

``

407

`+

#[inline]

`

``

408

`+

#[must_use]

`

``

409

`+

#[cfg(not(bootstrap))]

`

``

410

`+

#[unstable(feature = "f16", issue = "116909")]

`

``

411

`+

#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]

`

``

412

`+

pub const fn is_normal(self) -> bool {

`

``

413

`+

matches!(self.classify(), FpCategory::Normal)

`

``

414

`+

}

`

``

415

+

``

416

`+

/// Returns the floating point category of the number. If only one property

`

``

417

`+

/// is going to be tested, it is generally faster to use the specific

`

``

418

`+

/// predicate instead.

`

``

419

`+

///

`

``

420


/// ```

``

421

`+

/// #![feature(f16)]

`

``

422

`+

/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885

`

``

423

`+

///

`

``

424

`+

/// use std::num::FpCategory;

`

``

425

`+

///

`

``

426

`+

/// let num = 12.4_f16;

`

``

427

`+

/// let inf = f16::INFINITY;

`

``

428

`+

///

`

``

429

`+

/// assert_eq!(num.classify(), FpCategory::Normal);

`

``

430

`+

/// assert_eq!(inf.classify(), FpCategory::Infinite);

`

``

431

`+

/// # }

`

``

432


/// ```

``

433

`+

#[inline]

`

``

434

`+

#[cfg(not(bootstrap))]

`

``

435

`+

#[unstable(feature = "f16", issue = "116909")]

`

``

436

`+

#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]

`

``

437

`+

pub const fn classify(self) -> FpCategory {

`

``

438

`+

// A previous implementation for f32/f64 tried to only use bitmask-based checks,

`

``

439

`` +

// using to_bits to transmute the float to its bit repr and match on that.

``

``

440

`+

// Unfortunately, floating point numbers can be much worse than that.

`

``

441

`` +

// This also needs to not result in recursive evaluations of to_bits.

``

``

442

`+

//

`

``

443

+

``

444

`` +

// Platforms without native support generally convert to f32 to perform operations,

``

``

445

`` +

// and most of these platforms correctly round back to f16 after each operation.

``

``

446

`` +

// However, some platforms have bugs where they keep the excess f32 precision (e.g.

``

``

447

`+

// WASM, see llvm/llvm-project#96437). This implementation makes a best-effort attempt

`

``

448

`+

// to account for that excess precision.

`

``

449

`+

if self.is_infinite() {

`

``

450

`+

// Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.

`

``

451

`+

FpCategory::Infinite

`

``

452

`+

} else if self.is_nan() {

`

``

453

`+

// And it may not be NaN, as it can simply be an "overextended" finite value.

`

``

454

`+

FpCategory::Nan

`

``

455

`+

} else {

`

``

456

`+

// However, std can't simply compare to zero to check for zero, either,

`

``

457

`+

// as correctness requires avoiding equality tests that may be Subnormal == -0.0

`

``

458

`+

// because it may be wrong under "denormals are zero" and "flush to zero" modes.

`

``

459

`+

// Most of std's targets don't use those, but they are used for thumbv7neon.

`

``

460

`+

// So, this does use bitpattern matching for the rest.

`

``

461

+

``

462

`+

// SAFETY: f16 to u16 is fine. Usually.

`

``

463

`+

// If classify has gotten this far, the value is definitely in one of these categories.

`

``

464

`+

unsafe { f16::partial_classify(self) }

`

``

465

`+

}

`

``

466

`+

}

`

``

467

+

``

468

`+

/// This doesn't actually return a right answer for NaN on purpose,

`

``

469

`+

/// seeing as how it cannot correctly discern between a floating point NaN,

`

``

470

`+

/// and some normal floating point numbers truncated from an x87 FPU.

`

``

471

`+

///

`

``

472

`+

/// # Safety

`

``

473

`+

///

`

``

474

`+

/// This requires making sure you call this function for values it answers correctly on,

`

``

475

`+

/// otherwise it returns a wrong answer. This is not important for memory safety per se,

`

``

476

`+

/// but getting floats correct is important for not accidentally leaking const eval

`

``

477

`+

/// runtime-deviating logic which may or may not be acceptable.

`

``

478

`+

#[inline]

`

``

479

`+

#[cfg(not(bootstrap))]

`

``

480

`+

#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]

`

``

481

`+

const unsafe fn partial_classify(self) -> FpCategory {

`

``

482

`+

// SAFETY: The caller is not asking questions for which this will tell lies.

`

``

483

`+

let b = unsafe { mem::transmute::<f16, u16>(self) };

`

``

484

`+

match (b & Self::MAN_MASK, b & Self::EXP_MASK) {

`

``

485

`+

(0, Self::EXP_MASK) => FpCategory::Infinite,

`

``

486

`+

(0, 0) => FpCategory::Zero,

`

``

487

`+

(_, 0) => FpCategory::Subnormal,

`

``

488

`+

_ => FpCategory::Normal,

`

``

489

`+

}

`

``

490

`+

}

`

``

491

+

``

492

`+

/// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.

`

``

493

`+

/// FIXME(jubilee): In a just world, this would be the entire impl for classify,

`

``

494

`+

/// plus a transmute. We do not live in a just world, but we can make it more so.

`

``

495

`+

#[inline]

`

``

496

`+

#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]

`

``

497

`+

const fn classify_bits(b: u16) -> FpCategory {

`

``

498

`+

match (b & Self::MAN_MASK, b & Self::EXP_MASK) {

`

``

499

`+

(0, Self::EXP_MASK) => FpCategory::Infinite,

`

``

500

`+

(_, Self::EXP_MASK) => FpCategory::Nan,

`

``

501

`+

(0, 0) => FpCategory::Zero,

`

``

502

`+

(_, 0) => FpCategory::Subnormal,

`

``

503

`+

_ => FpCategory::Normal,

`

``

504

`+

}

`

``

505

`+

}

`

``

506

+

347

507

`` /// Returns true if self has a positive sign, including +0.0, NaNs with

``

348

508

`/// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any

`

349

509

`/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that

`