Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/ir/immediates.rs
3068 views
1
//! Immediate operands for Cranelift instructions
2
//!
3
//! This module defines the types of immediate operands that can appear on Cranelift instructions.
4
//! Each type here should have a corresponding definition in the
5
//! `cranelift-codegen/meta/src/shared/immediates` crate in the meta language.
6
7
use alloc::vec::Vec;
8
use core::cmp::Ordering;
9
use core::fmt::{self, Display, Formatter};
10
use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Sub};
11
use core::str::FromStr;
12
use core::{i32, u32};
13
use libm::Libm;
14
#[cfg(feature = "enable-serde")]
15
use serde_derive::{Deserialize, Serialize};
16
17
/// Convert a type into a vector of bytes; all implementors in this file must use little-endian
18
/// orderings of bytes to match WebAssembly's little-endianness.
19
pub trait IntoBytes {
20
/// Return the little-endian byte representation of the implementing type.
21
fn into_bytes(self) -> Vec<u8>;
22
}
23
24
impl IntoBytes for u8 {
25
fn into_bytes(self) -> Vec<u8> {
26
vec![self]
27
}
28
}
29
30
impl IntoBytes for i8 {
31
fn into_bytes(self) -> Vec<u8> {
32
vec![self as u8]
33
}
34
}
35
36
impl IntoBytes for i16 {
37
fn into_bytes(self) -> Vec<u8> {
38
self.to_le_bytes().to_vec()
39
}
40
}
41
42
impl IntoBytes for i32 {
43
fn into_bytes(self) -> Vec<u8> {
44
self.to_le_bytes().to_vec()
45
}
46
}
47
48
impl IntoBytes for Vec<u8> {
49
fn into_bytes(self) -> Vec<u8> {
50
self
51
}
52
}
53
54
/// 64-bit immediate signed integer operand.
55
///
56
/// An `Imm64` operand can also be used to represent immediate values of smaller integer types by
57
/// sign-extending to `i64`.
58
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
59
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
60
pub struct Imm64(i64);
61
62
impl Imm64 {
63
/// Create a new `Imm64` representing the signed number `x`.
64
pub fn new(x: i64) -> Self {
65
Self(x)
66
}
67
68
/// Return self negated.
69
pub fn wrapping_neg(self) -> Self {
70
Self(self.0.wrapping_neg())
71
}
72
73
/// Returns the value of this immediate.
74
pub fn bits(&self) -> i64 {
75
self.0
76
}
77
78
/// Mask this immediate to the given power-of-two bit width.
79
#[must_use]
80
pub(crate) fn mask_to_width(&self, bit_width: u32) -> Self {
81
debug_assert!(bit_width.is_power_of_two());
82
83
if bit_width >= 64 {
84
return *self;
85
}
86
87
let bit_width = i64::from(bit_width);
88
let mask = (1 << bit_width) - 1;
89
let masked = self.0 & mask;
90
Imm64(masked)
91
}
92
93
/// Sign extend this immediate as if it were a signed integer of the given
94
/// power-of-two width.
95
#[must_use]
96
pub fn sign_extend_from_width(&self, bit_width: u32) -> Self {
97
debug_assert!(
98
bit_width.is_power_of_two(),
99
"{bit_width} is not a power of two"
100
);
101
102
if bit_width >= 64 {
103
return *self;
104
}
105
106
let bit_width = i64::from(bit_width);
107
let delta = 64 - bit_width;
108
let sign_extended = (self.0 << delta) >> delta;
109
Imm64(sign_extended)
110
}
111
112
/// Zero extend this immediate as if it were an unsigned integer of the
113
/// given power-of-two width.
114
#[must_use]
115
pub fn zero_extend_from_width(&self, bit_width: u32) -> Self {
116
debug_assert!(
117
bit_width.is_power_of_two(),
118
"{bit_width} is not a power of two"
119
);
120
121
if bit_width >= 64 {
122
return *self;
123
}
124
125
let bit_width = u64::from(bit_width);
126
let delta = 64 - bit_width;
127
let zero_extended = (self.0.cast_unsigned() << delta) >> delta;
128
Imm64(zero_extended.cast_signed())
129
}
130
}
131
132
impl From<Imm64> for i64 {
133
fn from(val: Imm64) -> i64 {
134
val.0
135
}
136
}
137
138
impl IntoBytes for Imm64 {
139
fn into_bytes(self) -> Vec<u8> {
140
self.0.to_le_bytes().to_vec()
141
}
142
}
143
144
impl From<i64> for Imm64 {
145
fn from(x: i64) -> Self {
146
Self(x)
147
}
148
}
149
150
impl Display for Imm64 {
151
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
152
let x = self.0;
153
if x < 10_000 {
154
// Use decimal for small and negative numbers.
155
write!(f, "{x}")
156
} else {
157
write_hex(x as u64, f)
158
}
159
}
160
}
161
162
/// Parse a 64-bit signed number.
163
fn parse_i64(s: &str) -> Result<i64, &'static str> {
164
let negative = s.starts_with('-');
165
let s2 = if negative || s.starts_with('+') {
166
&s[1..]
167
} else {
168
s
169
};
170
171
let mut value = parse_u64(s2)?;
172
173
// We support the range-and-a-half from -2^63 .. 2^64-1.
174
if negative {
175
value = value.wrapping_neg();
176
// Don't allow large negative values to wrap around and become positive.
177
if value as i64 > 0 {
178
return Err("Negative number too small");
179
}
180
}
181
Ok(value as i64)
182
}
183
184
impl FromStr for Imm64 {
185
type Err = &'static str;
186
187
// Parse a decimal or hexadecimal `Imm64`, formatted as above.
188
fn from_str(s: &str) -> Result<Self, &'static str> {
189
parse_i64(s).map(Self::new)
190
}
191
}
192
193
/// 64-bit immediate unsigned integer operand.
194
///
195
/// A `Uimm64` operand can also be used to represent immediate values of smaller integer types by
196
/// zero-extending to `i64`.
197
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
198
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
199
pub struct Uimm64(u64);
200
201
impl Uimm64 {
202
/// Create a new `Uimm64` representing the unsigned number `x`.
203
pub fn new(x: u64) -> Self {
204
Self(x)
205
}
206
207
/// Return self negated.
208
pub fn wrapping_neg(self) -> Self {
209
Self(self.0.wrapping_neg())
210
}
211
}
212
213
impl From<Uimm64> for u64 {
214
fn from(val: Uimm64) -> u64 {
215
val.0
216
}
217
}
218
219
impl From<u64> for Uimm64 {
220
fn from(x: u64) -> Self {
221
Self(x)
222
}
223
}
224
225
/// Hexadecimal with a multiple of 4 digits and group separators:
226
///
227
/// 0xfff0
228
/// 0x0001_ffff
229
/// 0xffff_ffff_fff8_4400
230
///
231
fn write_hex(x: u64, f: &mut Formatter) -> fmt::Result {
232
let mut pos = (64 - x.leading_zeros() - 1) & 0xf0;
233
write!(f, "0x{:04x}", (x >> pos) & 0xffff)?;
234
while pos > 0 {
235
pos -= 16;
236
write!(f, "_{:04x}", (x >> pos) & 0xffff)?;
237
}
238
Ok(())
239
}
240
241
impl Display for Uimm64 {
242
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
243
let x = self.0;
244
if x < 10_000 {
245
// Use decimal for small numbers.
246
write!(f, "{x}")
247
} else {
248
write_hex(x, f)
249
}
250
}
251
}
252
253
/// Parse a 64-bit unsigned number.
254
fn parse_u64(s: &str) -> Result<u64, &'static str> {
255
let mut value: u64 = 0;
256
let mut digits = 0;
257
258
if s.starts_with("-0x") {
259
return Err("Invalid character in hexadecimal number");
260
} else if let Some(num) = s.strip_prefix("0x") {
261
// Hexadecimal.
262
for ch in num.chars() {
263
match ch.to_digit(16) {
264
Some(digit) => {
265
digits += 1;
266
if digits > 16 {
267
return Err("Too many hexadecimal digits");
268
}
269
// This can't overflow given the digit limit.
270
value = (value << 4) | u64::from(digit);
271
}
272
None => {
273
// Allow embedded underscores, but fail on anything else.
274
if ch != '_' {
275
return Err("Invalid character in hexadecimal number");
276
}
277
}
278
}
279
}
280
} else {
281
// Decimal number, possibly negative.
282
for ch in s.chars() {
283
match ch.to_digit(10) {
284
Some(digit) => {
285
digits += 1;
286
match value.checked_mul(10) {
287
None => return Err("Too large decimal number"),
288
Some(v) => value = v,
289
}
290
match value.checked_add(u64::from(digit)) {
291
None => return Err("Too large decimal number"),
292
Some(v) => value = v,
293
}
294
}
295
None => {
296
// Allow embedded underscores, but fail on anything else.
297
if ch != '_' {
298
return Err("Invalid character in decimal number");
299
}
300
}
301
}
302
}
303
}
304
305
if digits == 0 {
306
return Err("No digits in number");
307
}
308
309
Ok(value)
310
}
311
312
impl FromStr for Uimm64 {
313
type Err = &'static str;
314
315
// Parse a decimal or hexadecimal `Uimm64`, formatted as above.
316
fn from_str(s: &str) -> Result<Self, &'static str> {
317
parse_u64(s).map(Self::new)
318
}
319
}
320
321
/// 8-bit unsigned integer immediate operand.
322
///
323
/// This is used to indicate lane indexes typically.
324
pub type Uimm8 = u8;
325
326
/// A 32-bit unsigned integer immediate operand.
327
///
328
/// This is used to represent sizes of memory objects.
329
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
330
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
331
pub struct Uimm32(u32);
332
333
impl From<Uimm32> for u32 {
334
fn from(val: Uimm32) -> u32 {
335
val.0
336
}
337
}
338
339
impl From<Uimm32> for u64 {
340
fn from(val: Uimm32) -> u64 {
341
val.0.into()
342
}
343
}
344
345
impl From<Uimm32> for i64 {
346
fn from(val: Uimm32) -> i64 {
347
i64::from(val.0)
348
}
349
}
350
351
impl From<u32> for Uimm32 {
352
fn from(x: u32) -> Self {
353
Self(x)
354
}
355
}
356
357
impl Display for Uimm32 {
358
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
359
if self.0 < 10_000 {
360
write!(f, "{}", self.0)
361
} else {
362
write_hex(u64::from(self.0), f)
363
}
364
}
365
}
366
367
impl FromStr for Uimm32 {
368
type Err = &'static str;
369
370
// Parse a decimal or hexadecimal `Uimm32`, formatted as above.
371
fn from_str(s: &str) -> Result<Self, &'static str> {
372
parse_i64(s).and_then(|x| {
373
if 0 <= x && x <= i64::from(u32::MAX) {
374
Ok(Self(x as u32))
375
} else {
376
Err("Uimm32 out of range")
377
}
378
})
379
}
380
}
381
382
/// A 128-bit immediate operand.
383
///
384
/// This is used as an immediate value in SIMD instructions.
385
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
386
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
387
pub struct V128Imm(pub [u8; 16]);
388
389
impl V128Imm {
390
/// Iterate over the bytes in the constant.
391
pub fn bytes(&self) -> impl Iterator<Item = &u8> {
392
self.0.iter()
393
}
394
395
/// Convert the immediate into a vector.
396
pub fn to_vec(self) -> Vec<u8> {
397
self.0.to_vec()
398
}
399
400
/// Convert the immediate into a slice.
401
pub fn as_slice(&self) -> &[u8] {
402
&self.0[..]
403
}
404
}
405
406
impl From<&[u8]> for V128Imm {
407
fn from(slice: &[u8]) -> Self {
408
assert_eq!(slice.len(), 16);
409
let mut buffer = [0; 16];
410
buffer.copy_from_slice(slice);
411
Self(buffer)
412
}
413
}
414
415
impl From<u128> for V128Imm {
416
fn from(val: u128) -> Self {
417
V128Imm(val.to_le_bytes())
418
}
419
}
420
421
/// 32-bit signed immediate offset.
422
///
423
/// This is used to encode an immediate offset for load/store instructions. All supported ISAs have
424
/// a maximum load/store offset that fits in an `i32`.
425
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
426
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
427
pub struct Offset32(i32);
428
429
impl Offset32 {
430
/// Create a new `Offset32` representing the signed number `x`.
431
pub fn new(x: i32) -> Self {
432
Self(x)
433
}
434
435
/// Create a new `Offset32` representing the signed number `x` if possible.
436
pub fn try_from_i64(x: i64) -> Option<Self> {
437
let x = i32::try_from(x).ok()?;
438
Some(Self::new(x))
439
}
440
441
/// Add in the signed number `x` if possible.
442
pub fn try_add_i64(self, x: i64) -> Option<Self> {
443
let x = i32::try_from(x).ok()?;
444
let ret = self.0.checked_add(x)?;
445
Some(Self::new(ret))
446
}
447
}
448
449
impl From<Offset32> for i32 {
450
fn from(val: Offset32) -> i32 {
451
val.0
452
}
453
}
454
455
impl From<Offset32> for i64 {
456
fn from(val: Offset32) -> i64 {
457
i64::from(val.0)
458
}
459
}
460
461
impl From<i32> for Offset32 {
462
fn from(x: i32) -> Self {
463
Self(x)
464
}
465
}
466
467
impl From<u8> for Offset32 {
468
fn from(val: u8) -> Offset32 {
469
Self(val.into())
470
}
471
}
472
473
impl Display for Offset32 {
474
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
475
// 0 displays as an empty offset.
476
if self.0 == 0 {
477
return Ok(());
478
}
479
480
// Always include a sign.
481
write!(f, "{}", if self.0 < 0 { '-' } else { '+' })?;
482
483
let val = i64::from(self.0).abs();
484
if val < 10_000 {
485
write!(f, "{val}")
486
} else {
487
write_hex(val as u64, f)
488
}
489
}
490
}
491
492
impl FromStr for Offset32 {
493
type Err = &'static str;
494
495
// Parse a decimal or hexadecimal `Offset32`, formatted as above.
496
fn from_str(s: &str) -> Result<Self, &'static str> {
497
if !(s.starts_with('-') || s.starts_with('+')) {
498
return Err("Offset must begin with sign");
499
}
500
parse_i64(s).and_then(|x| {
501
if i64::from(i32::MIN) <= x && x <= i64::from(i32::MAX) {
502
Ok(Self::new(x as i32))
503
} else {
504
Err("Offset out of range")
505
}
506
})
507
}
508
}
509
510
// FIXME(rust-lang/rust#83527): Replace with `${ignore()}` once it is stabilised.
511
macro_rules! ignore {
512
($($t:tt)*) => {};
513
}
514
515
macro_rules! ieee_float {
516
(
517
name = $name:ident,
518
bits = $bits:literal,
519
significand_bits = $significand_bits:literal,
520
bits_ty = $bits_ty:ident,
521
float_ty = $float_ty:ident,
522
$(as_float = $as_float:ident,)?
523
$(rust_type_not_stable = $rust_type_not_stable:ident,)?
524
) => {
525
/// An IEEE
526
#[doc = concat!("binary", stringify!($bits))]
527
/// immediate floating point value, represented as a
528
#[doc = stringify!($bits_ty)]
529
/// containing the bit pattern.
530
///
531
/// We specifically avoid using a
532
#[doc = stringify!($float_ty)]
533
/// here since some architectures may silently alter floats.
534
/// See: <https://github.com/bytecodealliance/wasmtime/pull/2251#discussion_r498508646>
535
///
536
/// The [PartialEq] and [Hash] implementations are over the underlying bit pattern, but
537
/// [PartialOrd] respects IEEE754 semantics.
538
///
539
/// All bit patterns are allowed.
540
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
541
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
542
#[repr(C)]
543
pub struct $name {
544
bits: $bits_ty
545
}
546
547
impl $name {
548
const BITS: u8 = $bits;
549
const SIGNIFICAND_BITS: u8 = $significand_bits;
550
const EXPONENT_BITS: u8 = Self::BITS - Self::SIGNIFICAND_BITS - 1;
551
const SIGN_MASK: $bits_ty = 1 << (Self::EXPONENT_BITS + Self::SIGNIFICAND_BITS);
552
const SIGNIFICAND_MASK: $bits_ty = $bits_ty::MAX >> (Self::EXPONENT_BITS + 1);
553
const EXPONENT_MASK: $bits_ty = !Self::SIGN_MASK & !Self::SIGNIFICAND_MASK;
554
/// The positive WebAssembly canonical NaN.
555
pub const NAN: Self = Self::with_bits(Self::EXPONENT_MASK | (1 << (Self::SIGNIFICAND_BITS - 1)));
556
557
/// Create a new
558
#[doc = concat!("`", stringify!($name), "`")]
559
/// containing the bits of `bits`.
560
pub const fn with_bits(bits: $bits_ty) -> Self {
561
Self { bits }
562
}
563
564
/// Get the bitwise representation.
565
pub fn bits(self) -> $bits_ty {
566
self.bits
567
}
568
569
$(
570
/// Create a new
571
#[doc = concat!("`", stringify!($name), "`")]
572
/// representing the number `x`.
573
pub fn with_float(x: $float_ty) -> Self {
574
Self::with_bits(x.to_bits())
575
}
576
577
/// Converts `self` to a Rust
578
#[doc = concat!("`", stringify!($float_ty), "`.")]
579
pub fn $as_float(self) -> $float_ty {
580
$float_ty::from_bits(self.bits())
581
}
582
)?
583
584
/// Computes the absolute value of `self`.
585
pub fn abs(self) -> Self {
586
Self::with_bits(self.bits() & !Self::SIGN_MASK)
587
}
588
589
/// Returns a number composed of the magnitude of `self` and the sign of `sign`.
590
pub fn copysign(self, sign: Self) -> Self {
591
Self::with_bits((self.bits() & !Self::SIGN_MASK) | (sign.bits() & Self::SIGN_MASK))
592
}
593
594
/// Returns the minimum of `self` and `other`, following the WebAssembly/IEEE 754-2019 definition.
595
pub fn minimum(self, other: Self) -> Self {
596
// FIXME: Replace with Rust float method once it is stabilised.
597
if self.is_nan() || other.is_nan() {
598
Self::NAN
599
} else if self.is_zero() && other.is_zero() {
600
if self.is_negative() {
601
self
602
} else {
603
other
604
}
605
} else if self <= other {
606
self
607
} else {
608
other
609
}
610
}
611
612
/// Returns the maximum of `self` and `other`, following the WebAssembly/IEEE 754-2019 definition.
613
pub fn maximum(self, other: Self) -> Self {
614
// FIXME: Replace with Rust float method once it is stabilised.
615
if self.is_nan() || other.is_nan() {
616
Self::NAN
617
} else if self.is_zero() && other.is_zero() {
618
if self.is_positive() {
619
self
620
} else {
621
other
622
}
623
} else if self >= other {
624
self
625
} else {
626
other
627
}
628
}
629
630
/// Create an
631
#[doc = concat!("`", stringify!($name), "`")]
632
/// number representing `2.0^n`.
633
pub fn pow2<I: Into<i32>>(n: I) -> Self {
634
let n = n.into();
635
let w = Self::EXPONENT_BITS;
636
let t = Self::SIGNIFICAND_BITS;
637
let bias = (1 << (w - 1)) - 1;
638
let exponent = n + bias;
639
assert!(exponent > 0, "Underflow n={}", n);
640
assert!(exponent < (1 << w) + 1, "Overflow n={}", n);
641
Self::with_bits((exponent as $bits_ty) << t)
642
}
643
644
/// Create an
645
#[doc = concat!("`", stringify!($name), "`")]
646
/// number representing the greatest negative value not convertible from
647
#[doc = concat!("`", stringify!($float_ty), "`")]
648
/// to a signed integer with width n.
649
pub fn fcvt_to_sint_negative_overflow<I: Into<i32>>(n: I) -> Self {
650
let n = n.into();
651
debug_assert!(n < i32::from(Self::BITS));
652
debug_assert!(i32::from(Self::SIGNIFICAND_BITS) + 1 - n < i32::from(Self::BITS));
653
Self::with_bits((1 << (Self::BITS - 1)) | Self::pow2(n - 1).bits() | (1 << (i32::from(Self::SIGNIFICAND_BITS) + 1 - n)))
654
}
655
656
/// Check if the value is a NaN. For
657
#[doc = concat!("`", stringify!($name), "`,")]
658
/// this means checking that all the exponent bits are set and the significand is non-zero.
659
pub fn is_nan(self) -> bool {
660
self.abs().bits() > Self::EXPONENT_MASK
661
}
662
663
/// Returns true if `self` has a negative sign, including 0.0, NaNs with positive sign bit and positive infinity.
664
pub fn is_positive(self) -> bool {
665
!self.is_negative()
666
}
667
668
/// Returns true if `self` has a negative sign, including -0.0, NaNs with negative sign bit and negative infinity.
669
pub fn is_negative(self) -> bool {
670
self.bits() & Self::SIGN_MASK == Self::SIGN_MASK
671
}
672
673
/// Returns `true` if `self` is positive or negative zero.
674
pub fn is_zero(self) -> bool {
675
self.abs().bits() == 0
676
}
677
678
/// Returns `None` if `self` is a NaN and `Some(self)` otherwise.
679
pub fn non_nan(self) -> Option<Self> {
680
Some(self).filter(|f| !f.is_nan())
681
}
682
683
$(
684
/// Returns the square root of `self`.
685
pub fn sqrt(self) -> Self {
686
Self::with_float(Libm::<$float_ty>::sqrt(self.$as_float()))
687
}
688
689
/// Returns the smallest integer greater than or equal to `self`.
690
pub fn ceil(self) -> Self {
691
Self::with_float(Libm::<$float_ty>::ceil(self.$as_float()))
692
}
693
694
/// Returns the largest integer less than or equal to `self`.
695
pub fn floor(self) -> Self {
696
Self::with_float(Libm::<$float_ty>::floor(self.$as_float()))
697
}
698
699
/// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero.
700
pub fn trunc(self) -> Self {
701
Self::with_float(Libm::<$float_ty>::trunc(self.$as_float()))
702
}
703
704
/// Returns the nearest integer to `self`. Rounds half-way cases to the number
705
/// with an even least significant digit.
706
pub fn round_ties_even(self) -> Self {
707
Self::with_float(Libm::<$float_ty>::roundeven(self.$as_float()))
708
}
709
)?
710
}
711
712
impl PartialOrd for $name {
713
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
714
$(self.$as_float().partial_cmp(&rhs.$as_float()))?
715
$(
716
ignore!($rust_type_not_stable);
717
// FIXME(#8312): Use builtin Rust comparisons once `f16` and `f128` support is stabalised.
718
if self.is_nan() || rhs.is_nan() {
719
// One of the floats is a NaN.
720
return None;
721
}
722
if self.is_zero() || rhs.is_zero() {
723
// Zeros are always equal regardless of sign.
724
return Some(Ordering::Equal);
725
}
726
let lhs_positive = self.is_positive();
727
let rhs_positive = rhs.is_positive();
728
if lhs_positive != rhs_positive {
729
// Different signs: negative < positive
730
return lhs_positive.partial_cmp(&rhs_positive);
731
}
732
// Finite or infinity will order correctly with an integer comparison of the bits.
733
if lhs_positive {
734
self.bits().partial_cmp(&rhs.bits())
735
} else {
736
// Reverse the comparison when both floats are negative.
737
rhs.bits().partial_cmp(&self.bits())
738
}
739
)?
740
}
741
}
742
743
impl Display for $name {
744
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
745
format_float(u128::from(self.bits()), Self::EXPONENT_BITS, Self::SIGNIFICAND_BITS, f)
746
}
747
}
748
749
impl FromStr for $name {
750
type Err = &'static str;
751
752
fn from_str(s: &str) -> Result<Self, &'static str> {
753
match parse_float(s, Self::EXPONENT_BITS, Self::SIGNIFICAND_BITS) {
754
Ok(b) => Ok(Self::with_bits(b.try_into().unwrap())),
755
Err(s) => Err(s),
756
}
757
}
758
}
759
760
impl IntoBytes for $name {
761
fn into_bytes(self) -> Vec<u8> {
762
self.bits().to_le_bytes().to_vec()
763
}
764
}
765
766
impl Neg for $name {
767
type Output = Self;
768
769
fn neg(self) -> Self {
770
Self::with_bits(self.bits() ^ Self::SIGN_MASK)
771
}
772
}
773
774
775
776
$(
777
impl From<$float_ty> for $name {
778
fn from(x: $float_ty) -> Self {
779
Self::with_float(x)
780
}
781
}
782
783
impl Add for $name {
784
type Output = Self;
785
786
fn add(self, rhs: Self) -> Self {
787
Self::with_float(self.$as_float() + rhs.$as_float())
788
}
789
}
790
791
impl Sub for $name {
792
type Output = Self;
793
794
fn sub(self, rhs: Self) -> Self {
795
Self::with_float(self.$as_float() - rhs.$as_float())
796
}
797
}
798
799
impl Mul for $name {
800
type Output = Self;
801
802
fn mul(self, rhs: Self) -> Self {
803
Self::with_float(self.$as_float() * rhs.$as_float())
804
}
805
}
806
807
impl Div for $name {
808
type Output = Self;
809
810
fn div(self, rhs: Self) -> Self::Output {
811
Self::with_float(self.$as_float() / rhs.$as_float())
812
}
813
}
814
)?
815
816
impl BitAnd for $name {
817
type Output = Self;
818
819
fn bitand(self, rhs: Self) -> Self {
820
Self::with_bits(self.bits() & rhs.bits())
821
}
822
}
823
824
impl BitOr for $name {
825
type Output = Self;
826
827
fn bitor(self, rhs: Self) -> Self {
828
Self::with_bits(self.bits() | rhs.bits())
829
}
830
}
831
832
impl BitXor for $name {
833
type Output = Self;
834
835
fn bitxor(self, rhs: Self) -> Self {
836
Self::with_bits(self.bits() ^ rhs.bits())
837
}
838
}
839
840
impl Not for $name {
841
type Output = Self;
842
843
fn not(self) -> Self {
844
Self::with_bits(!self.bits())
845
}
846
}
847
};
848
}
849
850
ieee_float! {
851
name = Ieee16,
852
bits = 16,
853
significand_bits = 10,
854
bits_ty = u16,
855
float_ty = f16,
856
rust_type_not_stable = rust_type_not_stable,
857
}
858
859
ieee_float! {
860
name = Ieee32,
861
bits = 32,
862
significand_bits = 23,
863
bits_ty = u32,
864
float_ty = f32,
865
as_float = as_f32,
866
}
867
868
ieee_float! {
869
name = Ieee64,
870
bits = 64,
871
significand_bits = 52,
872
bits_ty = u64,
873
float_ty = f64,
874
as_float = as_f64,
875
}
876
877
ieee_float! {
878
name = Ieee128,
879
bits = 128,
880
significand_bits = 112,
881
bits_ty = u128,
882
float_ty = f128,
883
rust_type_not_stable = rust_type_not_stable,
884
}
885
886
/// Format a floating point number in a way that is reasonably human-readable, and that can be
887
/// converted back to binary without any rounding issues. The hexadecimal formatting of normal and
888
/// subnormal numbers is compatible with C99 and the `printf "%a"` format specifier. The NaN and Inf
889
/// formats are not supported by C99.
890
///
891
/// The encoding parameters are:
892
///
893
/// w - exponent field width in bits
894
/// t - trailing significand field width in bits
895
///
896
fn format_float(bits: u128, w: u8, t: u8, f: &mut Formatter) -> fmt::Result {
897
debug_assert!(w > 0 && w <= 16, "Invalid exponent range");
898
debug_assert!(1 + w + t <= 128, "Too large IEEE format for u128");
899
debug_assert!((t + w + 1).is_power_of_two(), "Unexpected IEEE format size");
900
901
let max_e_bits = (1u128 << w) - 1;
902
let t_bits = bits & ((1u128 << t) - 1); // Trailing significand.
903
let e_bits = (bits >> t) & max_e_bits; // Biased exponent.
904
let sign_bit = (bits >> (w + t)) & 1;
905
906
let bias: i32 = (1 << (w - 1)) - 1;
907
let e = e_bits as i32 - bias; // Unbiased exponent.
908
let emin = 1 - bias; // Minimum exponent.
909
910
// How many hexadecimal digits are needed for the trailing significand?
911
let digits = (t + 3) / 4;
912
// Trailing significand left-aligned in `digits` hexadecimal digits.
913
let left_t_bits = t_bits << (4 * digits - t);
914
915
// All formats share the leading sign.
916
if sign_bit != 0 {
917
write!(f, "-")?;
918
}
919
920
if e_bits == 0 {
921
if t_bits == 0 {
922
// Zero.
923
write!(f, "0.0")
924
} else {
925
// Subnormal.
926
write!(
927
f,
928
"0x0.{0:01$x}p{2}",
929
left_t_bits,
930
usize::from(digits),
931
emin
932
)
933
}
934
} else if e_bits == max_e_bits {
935
// Always print a `+` or `-` sign for these special values.
936
// This makes them easier to parse as they can't be confused as identifiers.
937
if sign_bit == 0 {
938
write!(f, "+")?;
939
}
940
if t_bits == 0 {
941
// Infinity.
942
write!(f, "Inf")
943
} else {
944
// NaN.
945
let payload = t_bits & ((1 << (t - 1)) - 1);
946
if t_bits & (1 << (t - 1)) != 0 {
947
// Quiet NaN.
948
if payload != 0 {
949
write!(f, "NaN:0x{payload:x}")
950
} else {
951
write!(f, "NaN")
952
}
953
} else {
954
// Signaling NaN.
955
write!(f, "sNaN:0x{payload:x}")
956
}
957
}
958
} else {
959
// Normal number.
960
write!(f, "0x1.{0:01$x}p{2}", left_t_bits, usize::from(digits), e)
961
}
962
}
963
964
/// Parse a float using the same format as `format_float` above.
965
///
966
/// The encoding parameters are:
967
///
968
/// w - exponent field width in bits
969
/// t - trailing significand field width in bits
970
///
971
fn parse_float(s: &str, w: u8, t: u8) -> Result<u128, &'static str> {
972
debug_assert!(w > 0 && w <= 16, "Invalid exponent range");
973
debug_assert!(1 + w + t <= 128, "Too large IEEE format for u128");
974
debug_assert!((t + w + 1).is_power_of_two(), "Unexpected IEEE format size");
975
976
let (sign_bit, s2) = if let Some(num) = s.strip_prefix('-') {
977
(1u128 << (t + w), num)
978
} else if let Some(num) = s.strip_prefix('+') {
979
(0, num)
980
} else {
981
(0, s)
982
};
983
984
if !s2.starts_with("0x") {
985
let max_e_bits = ((1u128 << w) - 1) << t;
986
let quiet_bit = 1u128 << (t - 1);
987
988
// The only decimal encoding allowed is 0.
989
if s2 == "0.0" {
990
return Ok(sign_bit);
991
}
992
993
if s2 == "Inf" {
994
// +/- infinity: e = max, t = 0.
995
return Ok(sign_bit | max_e_bits);
996
}
997
if s2 == "NaN" {
998
// Canonical quiet NaN: e = max, t = quiet.
999
return Ok(sign_bit | max_e_bits | quiet_bit);
1000
}
1001
if let Some(nan) = s2.strip_prefix("NaN:0x") {
1002
// Quiet NaN with payload.
1003
return match u128::from_str_radix(nan, 16) {
1004
Ok(payload) if payload < quiet_bit => {
1005
Ok(sign_bit | max_e_bits | quiet_bit | payload)
1006
}
1007
_ => Err("Invalid NaN payload"),
1008
};
1009
}
1010
if let Some(nan) = s2.strip_prefix("sNaN:0x") {
1011
// Signaling NaN with payload.
1012
return match u128::from_str_radix(nan, 16) {
1013
Ok(payload) if 0 < payload && payload < quiet_bit => {
1014
Ok(sign_bit | max_e_bits | payload)
1015
}
1016
_ => Err("Invalid sNaN payload"),
1017
};
1018
}
1019
1020
return Err("Float must be hexadecimal");
1021
}
1022
let s3 = &s2[2..];
1023
1024
let mut digits = 0u8;
1025
let mut digits_before_period: Option<u8> = None;
1026
let mut significand = 0u128;
1027
let mut exponent = 0i32;
1028
1029
for (idx, ch) in s3.char_indices() {
1030
match ch {
1031
'.' => {
1032
// This is the radix point. There can only be one.
1033
if digits_before_period != None {
1034
return Err("Multiple radix points");
1035
} else {
1036
digits_before_period = Some(digits);
1037
}
1038
}
1039
'p' => {
1040
// The following exponent is a decimal number.
1041
let exp_str = &s3[1 + idx..];
1042
match exp_str.parse::<i16>() {
1043
Ok(e) => {
1044
exponent = i32::from(e);
1045
break;
1046
}
1047
Err(_) => return Err("Bad exponent"),
1048
}
1049
}
1050
_ => match ch.to_digit(16) {
1051
Some(digit) => {
1052
digits += 1;
1053
if digits > 32 {
1054
return Err("Too many digits");
1055
}
1056
significand = (significand << 4) | u128::from(digit);
1057
}
1058
None => return Err("Invalid character"),
1059
},
1060
}
1061
}
1062
1063
if digits == 0 {
1064
return Err("No digits");
1065
}
1066
1067
if significand == 0 {
1068
// This is +/- 0.0.
1069
return Ok(sign_bit);
1070
}
1071
1072
// Number of bits appearing after the radix point.
1073
match digits_before_period {
1074
None => {} // No radix point present.
1075
Some(d) => exponent -= 4 * i32::from(digits - d),
1076
};
1077
1078
// Normalize the significand and exponent.
1079
let significant_bits = (128 - significand.leading_zeros()) as u8;
1080
if significant_bits > t + 1 {
1081
let adjust = significant_bits - (t + 1);
1082
if significand & ((1u128 << adjust) - 1) != 0 {
1083
return Err("Too many significant bits");
1084
}
1085
// Adjust significand down.
1086
significand >>= adjust;
1087
exponent += i32::from(adjust);
1088
} else {
1089
let adjust = t + 1 - significant_bits;
1090
significand <<= adjust;
1091
exponent -= i32::from(adjust);
1092
}
1093
debug_assert_eq!(significand >> t, 1);
1094
1095
// Trailing significand excludes the high bit.
1096
let t_bits = significand & ((1 << t) - 1);
1097
1098
let max_exp = (1i32 << w) - 2;
1099
let bias: i32 = (1 << (w - 1)) - 1;
1100
exponent += bias + i32::from(t);
1101
1102
if exponent > max_exp {
1103
Err("Magnitude too large")
1104
} else if exponent > 0 {
1105
// This is a normal number.
1106
let e_bits = (exponent as u128) << t;
1107
Ok(sign_bit | e_bits | t_bits)
1108
} else if 1 - exponent <= i32::from(t) {
1109
// This is a subnormal number: e = 0, t = significand bits.
1110
// Renormalize significand for exponent = 1.
1111
let adjust = 1 - exponent;
1112
if significand & ((1u128 << adjust) - 1) != 0 {
1113
Err("Subnormal underflow")
1114
} else {
1115
significand >>= adjust;
1116
Ok(sign_bit | significand)
1117
}
1118
} else {
1119
Err("Magnitude too small")
1120
}
1121
}
1122
1123
#[cfg(test)]
1124
mod tests {
1125
use super::*;
1126
use alloc::string::ToString;
1127
use core::{f32, f64};
1128
1129
#[test]
1130
fn format_imm64() {
1131
assert_eq!(Imm64(0).to_string(), "0");
1132
assert_eq!(Imm64(9999).to_string(), "9999");
1133
assert_eq!(Imm64(10000).to_string(), "0x2710");
1134
assert_eq!(Imm64(-9999).to_string(), "-9999");
1135
assert_eq!(Imm64(-10000).to_string(), "-10000");
1136
assert_eq!(Imm64(0xffff).to_string(), "0xffff");
1137
assert_eq!(Imm64(0x10000).to_string(), "0x0001_0000");
1138
}
1139
1140
#[test]
1141
fn format_uimm64() {
1142
assert_eq!(Uimm64(0).to_string(), "0");
1143
assert_eq!(Uimm64(9999).to_string(), "9999");
1144
assert_eq!(Uimm64(10000).to_string(), "0x2710");
1145
assert_eq!(Uimm64(-9999i64 as u64).to_string(), "0xffff_ffff_ffff_d8f1");
1146
assert_eq!(
1147
Uimm64(-10000i64 as u64).to_string(),
1148
"0xffff_ffff_ffff_d8f0"
1149
);
1150
assert_eq!(Uimm64(0xffff).to_string(), "0xffff");
1151
assert_eq!(Uimm64(0x10000).to_string(), "0x0001_0000");
1152
}
1153
1154
// Verify that `text` can be parsed as a `T` into a value that displays as `want`.
1155
#[track_caller]
1156
fn parse_ok<T: FromStr + Display>(text: &str, want: &str)
1157
where
1158
<T as FromStr>::Err: Display,
1159
{
1160
match text.parse::<T>() {
1161
Err(s) => panic!("\"{text}\".parse() error: {s}"),
1162
Ok(x) => assert_eq!(x.to_string(), want),
1163
}
1164
}
1165
1166
// Verify that `text` fails to parse as `T` with the error `msg`.
1167
fn parse_err<T: FromStr + Display>(text: &str, msg: &str)
1168
where
1169
<T as FromStr>::Err: Display,
1170
{
1171
match text.parse::<T>() {
1172
Err(s) => assert_eq!(s.to_string(), msg),
1173
Ok(x) => panic!("Wanted Err({msg}), but got {x}"),
1174
}
1175
}
1176
1177
#[test]
1178
fn parse_imm64() {
1179
parse_ok::<Imm64>("0", "0");
1180
parse_ok::<Imm64>("1", "1");
1181
parse_ok::<Imm64>("-0", "0");
1182
parse_ok::<Imm64>("-1", "-1");
1183
parse_ok::<Imm64>("0x0", "0");
1184
parse_ok::<Imm64>("0xf", "15");
1185
parse_ok::<Imm64>("-0x9", "-9");
1186
1187
// Probe limits.
1188
parse_ok::<Imm64>("0xffffffff_ffffffff", "-1");
1189
parse_ok::<Imm64>("0x80000000_00000000", "-9223372036854775808");
1190
parse_ok::<Imm64>("-0x80000000_00000000", "-9223372036854775808");
1191
parse_err::<Imm64>("-0x80000000_00000001", "Negative number too small");
1192
parse_ok::<Imm64>("18446744073709551615", "-1");
1193
parse_ok::<Imm64>("-9223372036854775808", "-9223372036854775808");
1194
// Overflow both the `checked_add` and `checked_mul`.
1195
parse_err::<Imm64>("18446744073709551616", "Too large decimal number");
1196
parse_err::<Imm64>("184467440737095516100", "Too large decimal number");
1197
parse_err::<Imm64>("-9223372036854775809", "Negative number too small");
1198
1199
// Underscores are allowed where digits go.
1200
parse_ok::<Imm64>("0_0", "0");
1201
parse_ok::<Imm64>("-_10_0", "-100");
1202
parse_ok::<Imm64>("_10_", "10");
1203
parse_ok::<Imm64>("0x97_88_bb", "0x0097_88bb");
1204
parse_ok::<Imm64>("0x_97_", "151");
1205
1206
parse_err::<Imm64>("", "No digits in number");
1207
parse_err::<Imm64>("-", "No digits in number");
1208
parse_err::<Imm64>("_", "No digits in number");
1209
parse_err::<Imm64>("0x", "No digits in number");
1210
parse_err::<Imm64>("0x_", "No digits in number");
1211
parse_err::<Imm64>("-0x", "No digits in number");
1212
parse_err::<Imm64>(" ", "Invalid character in decimal number");
1213
parse_err::<Imm64>("0 ", "Invalid character in decimal number");
1214
parse_err::<Imm64>(" 0", "Invalid character in decimal number");
1215
parse_err::<Imm64>("--", "Invalid character in decimal number");
1216
parse_err::<Imm64>("-0x-", "Invalid character in hexadecimal number");
1217
parse_err::<Imm64>("abc", "Invalid character in decimal number");
1218
parse_err::<Imm64>("-abc", "Invalid character in decimal number");
1219
1220
// Hex count overflow.
1221
parse_err::<Imm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
1222
}
1223
1224
#[test]
1225
fn parse_uimm64() {
1226
parse_ok::<Uimm64>("0", "0");
1227
parse_ok::<Uimm64>("1", "1");
1228
parse_ok::<Uimm64>("0x0", "0");
1229
parse_ok::<Uimm64>("0xf", "15");
1230
parse_ok::<Uimm64>("0xffffffff_fffffff7", "0xffff_ffff_ffff_fff7");
1231
1232
// Probe limits.
1233
parse_ok::<Uimm64>("0xffffffff_ffffffff", "0xffff_ffff_ffff_ffff");
1234
parse_ok::<Uimm64>("0x80000000_00000000", "0x8000_0000_0000_0000");
1235
parse_ok::<Uimm64>("18446744073709551615", "0xffff_ffff_ffff_ffff");
1236
// Overflow both the `checked_add` and `checked_mul`.
1237
parse_err::<Uimm64>("18446744073709551616", "Too large decimal number");
1238
parse_err::<Uimm64>("184467440737095516100", "Too large decimal number");
1239
1240
// Underscores are allowed where digits go.
1241
parse_ok::<Uimm64>("0_0", "0");
1242
parse_ok::<Uimm64>("_10_", "10");
1243
parse_ok::<Uimm64>("0x97_88_bb", "0x0097_88bb");
1244
parse_ok::<Uimm64>("0x_97_", "151");
1245
1246
parse_err::<Uimm64>("", "No digits in number");
1247
parse_err::<Uimm64>("_", "No digits in number");
1248
parse_err::<Uimm64>("0x", "No digits in number");
1249
parse_err::<Uimm64>("0x_", "No digits in number");
1250
parse_err::<Uimm64>("-", "Invalid character in decimal number");
1251
parse_err::<Uimm64>("-0x", "Invalid character in hexadecimal number");
1252
parse_err::<Uimm64>(" ", "Invalid character in decimal number");
1253
parse_err::<Uimm64>("0 ", "Invalid character in decimal number");
1254
parse_err::<Uimm64>(" 0", "Invalid character in decimal number");
1255
parse_err::<Uimm64>("--", "Invalid character in decimal number");
1256
parse_err::<Uimm64>("-0x-", "Invalid character in hexadecimal number");
1257
parse_err::<Uimm64>("-0", "Invalid character in decimal number");
1258
parse_err::<Uimm64>("-1", "Invalid character in decimal number");
1259
parse_err::<Uimm64>("abc", "Invalid character in decimal number");
1260
parse_err::<Uimm64>("-abc", "Invalid character in decimal number");
1261
1262
// Hex count overflow.
1263
parse_err::<Uimm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
1264
}
1265
1266
#[test]
1267
fn format_offset32() {
1268
assert_eq!(Offset32(0).to_string(), "");
1269
assert_eq!(Offset32(1).to_string(), "+1");
1270
assert_eq!(Offset32(-1).to_string(), "-1");
1271
assert_eq!(Offset32(9999).to_string(), "+9999");
1272
assert_eq!(Offset32(10000).to_string(), "+0x2710");
1273
assert_eq!(Offset32(-9999).to_string(), "-9999");
1274
assert_eq!(Offset32(-10000).to_string(), "-0x2710");
1275
assert_eq!(Offset32(0xffff).to_string(), "+0xffff");
1276
assert_eq!(Offset32(0x10000).to_string(), "+0x0001_0000");
1277
}
1278
1279
#[test]
1280
fn parse_offset32() {
1281
parse_ok::<Offset32>("+0", "");
1282
parse_ok::<Offset32>("+1", "+1");
1283
parse_ok::<Offset32>("-0", "");
1284
parse_ok::<Offset32>("-1", "-1");
1285
parse_ok::<Offset32>("+0x0", "");
1286
parse_ok::<Offset32>("+0xf", "+15");
1287
parse_ok::<Offset32>("-0x9", "-9");
1288
parse_ok::<Offset32>("-0x8000_0000", "-0x8000_0000");
1289
1290
parse_err::<Offset32>("+0x8000_0000", "Offset out of range");
1291
}
1292
1293
#[test]
1294
fn format_ieee16() {
1295
assert_eq!(Ieee16::with_bits(0).to_string(), "0.0"); // 0.0
1296
assert_eq!(Ieee16::with_bits(0x8000).to_string(), "-0.0"); // -0.0
1297
assert_eq!(Ieee16::with_bits(0x3c00).to_string(), "0x1.000p0"); // 1.0
1298
assert_eq!(Ieee16::with_bits(0x3e00).to_string(), "0x1.800p0"); // 1.5
1299
assert_eq!(Ieee16::with_bits(0x3800).to_string(), "0x1.000p-1"); // 0.5
1300
assert_eq!(
1301
Ieee16::with_bits(0x1400).to_string(), // `f16::EPSILON`
1302
"0x1.000p-10"
1303
);
1304
assert_eq!(
1305
Ieee16::with_bits(0xfbff).to_string(), // `f16::MIN`
1306
"-0x1.ffcp15"
1307
);
1308
assert_eq!(
1309
Ieee16::with_bits(0x7bff).to_string(), // `f16::MAX`
1310
"0x1.ffcp15"
1311
);
1312
// Smallest positive normal number.
1313
assert_eq!(
1314
Ieee16::with_bits(0x0400).to_string(), // `f16::MIN_POSITIVE`
1315
"0x1.000p-14"
1316
);
1317
// Subnormals.
1318
assert_eq!(
1319
Ieee16::with_bits(0x0200).to_string(), // `f16::MIN_POSITIVE / 2.0`
1320
"0x0.800p-14"
1321
);
1322
assert_eq!(
1323
Ieee16::with_bits(0x0001).to_string(), // `f16::MIN_POSITIVE * f16::EPSILON`
1324
"0x0.004p-14"
1325
);
1326
assert_eq!(
1327
Ieee16::with_bits(0x7c00).to_string(), // `f16::INFINITY`
1328
"+Inf"
1329
);
1330
assert_eq!(
1331
Ieee16::with_bits(0xfc00).to_string(), // `f16::NEG_INFINITY`
1332
"-Inf"
1333
);
1334
assert_eq!(
1335
Ieee16::with_bits(0x7e00).to_string(), // `f16::NAN`
1336
"+NaN"
1337
);
1338
assert_eq!(
1339
Ieee16::with_bits(0xfe00).to_string(), // `-f16::NAN`
1340
"-NaN"
1341
);
1342
// Construct some qNaNs with payloads.
1343
assert_eq!(Ieee16::with_bits(0x7e01).to_string(), "+NaN:0x1");
1344
assert_eq!(Ieee16::with_bits(0x7f01).to_string(), "+NaN:0x101");
1345
// Signaling NaNs.
1346
assert_eq!(Ieee16::with_bits(0x7c01).to_string(), "+sNaN:0x1");
1347
assert_eq!(Ieee16::with_bits(0x7d01).to_string(), "+sNaN:0x101");
1348
}
1349
1350
#[test]
1351
fn parse_ieee16() {
1352
parse_ok::<Ieee16>("0.0", "0.0");
1353
parse_ok::<Ieee16>("+0.0", "0.0");
1354
parse_ok::<Ieee16>("-0.0", "-0.0");
1355
parse_ok::<Ieee16>("0x0", "0.0");
1356
parse_ok::<Ieee16>("0x0.0", "0.0");
1357
parse_ok::<Ieee16>("0x.0", "0.0");
1358
parse_ok::<Ieee16>("0x0.", "0.0");
1359
parse_ok::<Ieee16>("0x1", "0x1.000p0");
1360
parse_ok::<Ieee16>("+0x1", "0x1.000p0");
1361
parse_ok::<Ieee16>("-0x1", "-0x1.000p0");
1362
parse_ok::<Ieee16>("0x10", "0x1.000p4");
1363
parse_ok::<Ieee16>("0x10.0", "0x1.000p4");
1364
parse_err::<Ieee16>("0.", "Float must be hexadecimal");
1365
parse_err::<Ieee16>(".0", "Float must be hexadecimal");
1366
parse_err::<Ieee16>("0", "Float must be hexadecimal");
1367
parse_err::<Ieee16>("-0", "Float must be hexadecimal");
1368
parse_err::<Ieee16>(".", "Float must be hexadecimal");
1369
parse_err::<Ieee16>("", "Float must be hexadecimal");
1370
parse_err::<Ieee16>("-", "Float must be hexadecimal");
1371
parse_err::<Ieee16>("0x", "No digits");
1372
parse_err::<Ieee16>("0x..", "Multiple radix points");
1373
1374
// Check significant bits.
1375
parse_ok::<Ieee16>("0x0.ffe", "0x1.ffcp-1");
1376
parse_ok::<Ieee16>("0x1.ffc", "0x1.ffcp0");
1377
parse_ok::<Ieee16>("0x3.ff8", "0x1.ffcp1");
1378
parse_ok::<Ieee16>("0x7.ff", "0x1.ffcp2");
1379
parse_ok::<Ieee16>("0xf.fe", "0x1.ffcp3");
1380
parse_err::<Ieee16>("0x1.ffe", "Too many significant bits");
1381
parse_err::<Ieee16>("0x1.ffc00000000000000000000000000000", "Too many digits");
1382
1383
// Exponents.
1384
parse_ok::<Ieee16>("0x1p3", "0x1.000p3");
1385
parse_ok::<Ieee16>("0x1p-3", "0x1.000p-3");
1386
parse_ok::<Ieee16>("0x1.0p3", "0x1.000p3");
1387
parse_ok::<Ieee16>("0x2.0p3", "0x1.000p4");
1388
parse_ok::<Ieee16>("0x1.0p15", "0x1.000p15");
1389
parse_ok::<Ieee16>("0x1.0p-14", "0x1.000p-14");
1390
parse_ok::<Ieee16>("0x0.1p-10", "0x1.000p-14");
1391
parse_err::<Ieee16>("0x2.0p15", "Magnitude too large");
1392
1393
// Subnormals.
1394
parse_ok::<Ieee16>("0x1.0p-15", "0x0.800p-14");
1395
parse_ok::<Ieee16>("0x1.0p-24", "0x0.004p-14");
1396
parse_ok::<Ieee16>("0x0.004p-14", "0x0.004p-14");
1397
parse_err::<Ieee16>("0x0.102p-14", "Subnormal underflow");
1398
parse_err::<Ieee16>("0x1.8p-24", "Subnormal underflow");
1399
parse_err::<Ieee16>("0x1.0p-25", "Magnitude too small");
1400
1401
// NaNs and Infs.
1402
parse_ok::<Ieee16>("Inf", "+Inf");
1403
parse_ok::<Ieee16>("+Inf", "+Inf");
1404
parse_ok::<Ieee16>("-Inf", "-Inf");
1405
parse_ok::<Ieee16>("NaN", "+NaN");
1406
parse_ok::<Ieee16>("+NaN", "+NaN");
1407
parse_ok::<Ieee16>("-NaN", "-NaN");
1408
parse_ok::<Ieee16>("NaN:0x0", "+NaN");
1409
parse_err::<Ieee16>("NaN:", "Float must be hexadecimal");
1410
parse_err::<Ieee16>("NaN:0", "Float must be hexadecimal");
1411
parse_err::<Ieee16>("NaN:0x", "Invalid NaN payload");
1412
parse_ok::<Ieee16>("NaN:0x001", "+NaN:0x1");
1413
parse_ok::<Ieee16>("NaN:0x101", "+NaN:0x101");
1414
parse_err::<Ieee16>("NaN:0x301", "Invalid NaN payload");
1415
parse_ok::<Ieee16>("sNaN:0x1", "+sNaN:0x1");
1416
parse_err::<Ieee16>("sNaN:0x0", "Invalid sNaN payload");
1417
parse_ok::<Ieee16>("sNaN:0x101", "+sNaN:0x101");
1418
parse_err::<Ieee16>("sNaN:0x301", "Invalid sNaN payload");
1419
}
1420
1421
#[test]
1422
fn pow2_ieee16() {
1423
assert_eq!(Ieee16::pow2(0).to_string(), "0x1.000p0");
1424
assert_eq!(Ieee16::pow2(1).to_string(), "0x1.000p1");
1425
assert_eq!(Ieee16::pow2(-1).to_string(), "0x1.000p-1");
1426
assert_eq!(Ieee16::pow2(15).to_string(), "0x1.000p15");
1427
assert_eq!(Ieee16::pow2(-14).to_string(), "0x1.000p-14");
1428
1429
assert_eq!((-Ieee16::pow2(1)).to_string(), "-0x1.000p1");
1430
}
1431
1432
#[test]
1433
fn fcvt_to_sint_negative_overflow_ieee16() {
1434
// FIXME(#8312): Replace with commented out version once Rust f16 support is stabilised.
1435
// let n = 8;
1436
// assert_eq!(
1437
// -((1u16 << (n - 1)) as f16) - 1.0,
1438
// Ieee16::fcvt_to_sint_negative_overflow(n).as_f16()
1439
// );
1440
let n = 8;
1441
assert_eq!(
1442
"-0x1.020p7",
1443
Ieee16::fcvt_to_sint_negative_overflow(n).to_string()
1444
);
1445
}
1446
1447
#[test]
1448
fn format_ieee32() {
1449
assert_eq!(Ieee32::with_float(0.0).to_string(), "0.0");
1450
assert_eq!(Ieee32::with_float(-0.0).to_string(), "-0.0");
1451
assert_eq!(Ieee32::with_float(1.0).to_string(), "0x1.000000p0");
1452
assert_eq!(Ieee32::with_float(1.5).to_string(), "0x1.800000p0");
1453
assert_eq!(Ieee32::with_float(0.5).to_string(), "0x1.000000p-1");
1454
assert_eq!(
1455
Ieee32::with_float(f32::EPSILON).to_string(),
1456
"0x1.000000p-23"
1457
);
1458
assert_eq!(Ieee32::with_float(f32::MIN).to_string(), "-0x1.fffffep127");
1459
assert_eq!(Ieee32::with_float(f32::MAX).to_string(), "0x1.fffffep127");
1460
// Smallest positive normal number.
1461
assert_eq!(
1462
Ieee32::with_float(f32::MIN_POSITIVE).to_string(),
1463
"0x1.000000p-126"
1464
);
1465
// Subnormals.
1466
assert_eq!(
1467
Ieee32::with_float(f32::MIN_POSITIVE / 2.0).to_string(),
1468
"0x0.800000p-126"
1469
);
1470
assert_eq!(
1471
Ieee32::with_float(f32::MIN_POSITIVE * f32::EPSILON).to_string(),
1472
"0x0.000002p-126"
1473
);
1474
assert_eq!(Ieee32::with_float(f32::INFINITY).to_string(), "+Inf");
1475
assert_eq!(Ieee32::with_float(f32::NEG_INFINITY).to_string(), "-Inf");
1476
assert_eq!(Ieee32::with_float(f32::NAN).to_string(), "+NaN");
1477
assert_eq!(Ieee32::with_float(-f32::NAN).to_string(), "-NaN");
1478
// Construct some qNaNs with payloads.
1479
assert_eq!(Ieee32::with_bits(0x7fc00001).to_string(), "+NaN:0x1");
1480
assert_eq!(Ieee32::with_bits(0x7ff00001).to_string(), "+NaN:0x300001");
1481
// Signaling NaNs.
1482
assert_eq!(Ieee32::with_bits(0x7f800001).to_string(), "+sNaN:0x1");
1483
assert_eq!(Ieee32::with_bits(0x7fa00001).to_string(), "+sNaN:0x200001");
1484
}
1485
1486
#[test]
1487
fn parse_ieee32() {
1488
parse_ok::<Ieee32>("0.0", "0.0");
1489
parse_ok::<Ieee32>("+0.0", "0.0");
1490
parse_ok::<Ieee32>("-0.0", "-0.0");
1491
parse_ok::<Ieee32>("0x0", "0.0");
1492
parse_ok::<Ieee32>("0x0.0", "0.0");
1493
parse_ok::<Ieee32>("0x.0", "0.0");
1494
parse_ok::<Ieee32>("0x0.", "0.0");
1495
parse_ok::<Ieee32>("0x1", "0x1.000000p0");
1496
parse_ok::<Ieee32>("+0x1", "0x1.000000p0");
1497
parse_ok::<Ieee32>("-0x1", "-0x1.000000p0");
1498
parse_ok::<Ieee32>("0x10", "0x1.000000p4");
1499
parse_ok::<Ieee32>("0x10.0", "0x1.000000p4");
1500
parse_err::<Ieee32>("0.", "Float must be hexadecimal");
1501
parse_err::<Ieee32>(".0", "Float must be hexadecimal");
1502
parse_err::<Ieee32>("0", "Float must be hexadecimal");
1503
parse_err::<Ieee32>("-0", "Float must be hexadecimal");
1504
parse_err::<Ieee32>(".", "Float must be hexadecimal");
1505
parse_err::<Ieee32>("", "Float must be hexadecimal");
1506
parse_err::<Ieee32>("-", "Float must be hexadecimal");
1507
parse_err::<Ieee32>("0x", "No digits");
1508
parse_err::<Ieee32>("0x..", "Multiple radix points");
1509
1510
// Check significant bits.
1511
parse_ok::<Ieee32>("0x0.ffffff", "0x1.fffffep-1");
1512
parse_ok::<Ieee32>("0x1.fffffe", "0x1.fffffep0");
1513
parse_ok::<Ieee32>("0x3.fffffc", "0x1.fffffep1");
1514
parse_ok::<Ieee32>("0x7.fffff8", "0x1.fffffep2");
1515
parse_ok::<Ieee32>("0xf.fffff0", "0x1.fffffep3");
1516
parse_err::<Ieee32>("0x1.ffffff", "Too many significant bits");
1517
parse_err::<Ieee32>("0x1.fffffe00000000000000000000000000", "Too many digits");
1518
1519
// Exponents.
1520
parse_ok::<Ieee32>("0x1p3", "0x1.000000p3");
1521
parse_ok::<Ieee32>("0x1p-3", "0x1.000000p-3");
1522
parse_ok::<Ieee32>("0x1.0p3", "0x1.000000p3");
1523
parse_ok::<Ieee32>("0x2.0p3", "0x1.000000p4");
1524
parse_ok::<Ieee32>("0x1.0p127", "0x1.000000p127");
1525
parse_ok::<Ieee32>("0x1.0p-126", "0x1.000000p-126");
1526
parse_ok::<Ieee32>("0x0.1p-122", "0x1.000000p-126");
1527
parse_err::<Ieee32>("0x2.0p127", "Magnitude too large");
1528
1529
// Subnormals.
1530
parse_ok::<Ieee32>("0x1.0p-127", "0x0.800000p-126");
1531
parse_ok::<Ieee32>("0x1.0p-149", "0x0.000002p-126");
1532
parse_ok::<Ieee32>("0x0.000002p-126", "0x0.000002p-126");
1533
parse_err::<Ieee32>("0x0.100001p-126", "Subnormal underflow");
1534
parse_err::<Ieee32>("0x1.8p-149", "Subnormal underflow");
1535
parse_err::<Ieee32>("0x1.0p-150", "Magnitude too small");
1536
1537
// NaNs and Infs.
1538
parse_ok::<Ieee32>("Inf", "+Inf");
1539
parse_ok::<Ieee32>("+Inf", "+Inf");
1540
parse_ok::<Ieee32>("-Inf", "-Inf");
1541
parse_ok::<Ieee32>("NaN", "+NaN");
1542
parse_ok::<Ieee32>("+NaN", "+NaN");
1543
parse_ok::<Ieee32>("-NaN", "-NaN");
1544
parse_ok::<Ieee32>("NaN:0x0", "+NaN");
1545
parse_err::<Ieee32>("NaN:", "Float must be hexadecimal");
1546
parse_err::<Ieee32>("NaN:0", "Float must be hexadecimal");
1547
parse_err::<Ieee32>("NaN:0x", "Invalid NaN payload");
1548
parse_ok::<Ieee32>("NaN:0x000001", "+NaN:0x1");
1549
parse_ok::<Ieee32>("NaN:0x300001", "+NaN:0x300001");
1550
parse_err::<Ieee32>("NaN:0x400001", "Invalid NaN payload");
1551
parse_ok::<Ieee32>("sNaN:0x1", "+sNaN:0x1");
1552
parse_err::<Ieee32>("sNaN:0x0", "Invalid sNaN payload");
1553
parse_ok::<Ieee32>("sNaN:0x200001", "+sNaN:0x200001");
1554
parse_err::<Ieee32>("sNaN:0x400001", "Invalid sNaN payload");
1555
}
1556
1557
#[test]
1558
fn pow2_ieee32() {
1559
assert_eq!(Ieee32::pow2(0).to_string(), "0x1.000000p0");
1560
assert_eq!(Ieee32::pow2(1).to_string(), "0x1.000000p1");
1561
assert_eq!(Ieee32::pow2(-1).to_string(), "0x1.000000p-1");
1562
assert_eq!(Ieee32::pow2(127).to_string(), "0x1.000000p127");
1563
assert_eq!(Ieee32::pow2(-126).to_string(), "0x1.000000p-126");
1564
1565
assert_eq!((-Ieee32::pow2(1)).to_string(), "-0x1.000000p1");
1566
}
1567
1568
#[test]
1569
fn fcvt_to_sint_negative_overflow_ieee32() {
1570
for n in [8, 16] {
1571
assert_eq!(
1572
-((1u32 << (n - 1)) as f32) - 1.0,
1573
Ieee32::fcvt_to_sint_negative_overflow(n).as_f32(),
1574
"n = {n}"
1575
);
1576
}
1577
}
1578
1579
#[test]
1580
fn format_ieee64() {
1581
assert_eq!(Ieee64::with_float(0.0).to_string(), "0.0");
1582
assert_eq!(Ieee64::with_float(-0.0).to_string(), "-0.0");
1583
assert_eq!(Ieee64::with_float(1.0).to_string(), "0x1.0000000000000p0");
1584
assert_eq!(Ieee64::with_float(1.5).to_string(), "0x1.8000000000000p0");
1585
assert_eq!(Ieee64::with_float(0.5).to_string(), "0x1.0000000000000p-1");
1586
assert_eq!(
1587
Ieee64::with_float(f64::EPSILON).to_string(),
1588
"0x1.0000000000000p-52"
1589
);
1590
assert_eq!(
1591
Ieee64::with_float(f64::MIN).to_string(),
1592
"-0x1.fffffffffffffp1023"
1593
);
1594
assert_eq!(
1595
Ieee64::with_float(f64::MAX).to_string(),
1596
"0x1.fffffffffffffp1023"
1597
);
1598
// Smallest positive normal number.
1599
assert_eq!(
1600
Ieee64::with_float(f64::MIN_POSITIVE).to_string(),
1601
"0x1.0000000000000p-1022"
1602
);
1603
// Subnormals.
1604
assert_eq!(
1605
Ieee64::with_float(f64::MIN_POSITIVE / 2.0).to_string(),
1606
"0x0.8000000000000p-1022"
1607
);
1608
assert_eq!(
1609
Ieee64::with_float(f64::MIN_POSITIVE * f64::EPSILON).to_string(),
1610
"0x0.0000000000001p-1022"
1611
);
1612
assert_eq!(Ieee64::with_float(f64::INFINITY).to_string(), "+Inf");
1613
assert_eq!(Ieee64::with_float(f64::NEG_INFINITY).to_string(), "-Inf");
1614
assert_eq!(Ieee64::with_float(f64::NAN).to_string(), "+NaN");
1615
assert_eq!(Ieee64::with_float(-f64::NAN).to_string(), "-NaN");
1616
// Construct some qNaNs with payloads.
1617
assert_eq!(
1618
Ieee64::with_bits(0x7ff8000000000001).to_string(),
1619
"+NaN:0x1"
1620
);
1621
assert_eq!(
1622
Ieee64::with_bits(0x7ffc000000000001).to_string(),
1623
"+NaN:0x4000000000001"
1624
);
1625
// Signaling NaNs.
1626
assert_eq!(
1627
Ieee64::with_bits(0x7ff0000000000001).to_string(),
1628
"+sNaN:0x1"
1629
);
1630
assert_eq!(
1631
Ieee64::with_bits(0x7ff4000000000001).to_string(),
1632
"+sNaN:0x4000000000001"
1633
);
1634
}
1635
1636
#[test]
1637
fn parse_ieee64() {
1638
parse_ok::<Ieee64>("0.0", "0.0");
1639
parse_ok::<Ieee64>("-0.0", "-0.0");
1640
parse_ok::<Ieee64>("0x0", "0.0");
1641
parse_ok::<Ieee64>("0x0.0", "0.0");
1642
parse_ok::<Ieee64>("0x.0", "0.0");
1643
parse_ok::<Ieee64>("0x0.", "0.0");
1644
parse_ok::<Ieee64>("0x1", "0x1.0000000000000p0");
1645
parse_ok::<Ieee64>("-0x1", "-0x1.0000000000000p0");
1646
parse_ok::<Ieee64>("0x10", "0x1.0000000000000p4");
1647
parse_ok::<Ieee64>("0x10.0", "0x1.0000000000000p4");
1648
parse_err::<Ieee64>("0.", "Float must be hexadecimal");
1649
parse_err::<Ieee64>(".0", "Float must be hexadecimal");
1650
parse_err::<Ieee64>("0", "Float must be hexadecimal");
1651
parse_err::<Ieee64>("-0", "Float must be hexadecimal");
1652
parse_err::<Ieee64>(".", "Float must be hexadecimal");
1653
parse_err::<Ieee64>("", "Float must be hexadecimal");
1654
parse_err::<Ieee64>("-", "Float must be hexadecimal");
1655
parse_err::<Ieee64>("0x", "No digits");
1656
parse_err::<Ieee64>("0x..", "Multiple radix points");
1657
1658
// Check significant bits.
1659
parse_ok::<Ieee64>("0x0.fffffffffffff8", "0x1.fffffffffffffp-1");
1660
parse_ok::<Ieee64>("0x1.fffffffffffff", "0x1.fffffffffffffp0");
1661
parse_ok::<Ieee64>("0x3.ffffffffffffe", "0x1.fffffffffffffp1");
1662
parse_ok::<Ieee64>("0x7.ffffffffffffc", "0x1.fffffffffffffp2");
1663
parse_ok::<Ieee64>("0xf.ffffffffffff8", "0x1.fffffffffffffp3");
1664
parse_err::<Ieee64>("0x3.fffffffffffff", "Too many significant bits");
1665
parse_err::<Ieee64>("0x001.fffffe000000000000000000000000", "Too many digits");
1666
1667
// Exponents.
1668
parse_ok::<Ieee64>("0x1p3", "0x1.0000000000000p3");
1669
parse_ok::<Ieee64>("0x1p-3", "0x1.0000000000000p-3");
1670
parse_ok::<Ieee64>("0x1.0p3", "0x1.0000000000000p3");
1671
parse_ok::<Ieee64>("0x2.0p3", "0x1.0000000000000p4");
1672
parse_ok::<Ieee64>("0x1.0p1023", "0x1.0000000000000p1023");
1673
parse_ok::<Ieee64>("0x1.0p-1022", "0x1.0000000000000p-1022");
1674
parse_ok::<Ieee64>("0x0.1p-1018", "0x1.0000000000000p-1022");
1675
parse_err::<Ieee64>("0x2.0p1023", "Magnitude too large");
1676
1677
// Subnormals.
1678
parse_ok::<Ieee64>("0x1.0p-1023", "0x0.8000000000000p-1022");
1679
parse_ok::<Ieee64>("0x1.0p-1074", "0x0.0000000000001p-1022");
1680
parse_ok::<Ieee64>("0x0.0000000000001p-1022", "0x0.0000000000001p-1022");
1681
parse_err::<Ieee64>("0x0.10000000000008p-1022", "Subnormal underflow");
1682
parse_err::<Ieee64>("0x1.8p-1074", "Subnormal underflow");
1683
parse_err::<Ieee64>("0x1.0p-1075", "Magnitude too small");
1684
1685
// NaNs and Infs.
1686
parse_ok::<Ieee64>("Inf", "+Inf");
1687
parse_ok::<Ieee64>("-Inf", "-Inf");
1688
parse_ok::<Ieee64>("NaN", "+NaN");
1689
parse_ok::<Ieee64>("-NaN", "-NaN");
1690
parse_ok::<Ieee64>("NaN:0x0", "+NaN");
1691
parse_err::<Ieee64>("NaN:", "Float must be hexadecimal");
1692
parse_err::<Ieee64>("NaN:0", "Float must be hexadecimal");
1693
parse_err::<Ieee64>("NaN:0x", "Invalid NaN payload");
1694
parse_ok::<Ieee64>("NaN:0x000001", "+NaN:0x1");
1695
parse_ok::<Ieee64>("NaN:0x4000000000001", "+NaN:0x4000000000001");
1696
parse_err::<Ieee64>("NaN:0x8000000000001", "Invalid NaN payload");
1697
parse_ok::<Ieee64>("sNaN:0x1", "+sNaN:0x1");
1698
parse_err::<Ieee64>("sNaN:0x0", "Invalid sNaN payload");
1699
parse_ok::<Ieee64>("sNaN:0x4000000000001", "+sNaN:0x4000000000001");
1700
parse_err::<Ieee64>("sNaN:0x8000000000001", "Invalid sNaN payload");
1701
}
1702
1703
#[test]
1704
fn pow2_ieee64() {
1705
assert_eq!(Ieee64::pow2(0).to_string(), "0x1.0000000000000p0");
1706
assert_eq!(Ieee64::pow2(1).to_string(), "0x1.0000000000000p1");
1707
assert_eq!(Ieee64::pow2(-1).to_string(), "0x1.0000000000000p-1");
1708
assert_eq!(Ieee64::pow2(1023).to_string(), "0x1.0000000000000p1023");
1709
assert_eq!(Ieee64::pow2(-1022).to_string(), "0x1.0000000000000p-1022");
1710
1711
assert_eq!((-Ieee64::pow2(1)).to_string(), "-0x1.0000000000000p1");
1712
}
1713
1714
#[test]
1715
fn fcvt_to_sint_negative_overflow_ieee64() {
1716
for n in [8, 16, 32] {
1717
assert_eq!(
1718
-((1u64 << (n - 1)) as f64) - 1.0,
1719
Ieee64::fcvt_to_sint_negative_overflow(n).as_f64(),
1720
"n = {n}"
1721
);
1722
}
1723
}
1724
1725
#[test]
1726
fn format_ieee128() {
1727
assert_eq!(
1728
Ieee128::with_bits(0x00000000000000000000000000000000).to_string(), // 0.0
1729
"0.0"
1730
);
1731
assert_eq!(
1732
Ieee128::with_bits(0x80000000000000000000000000000000).to_string(), // -0.0
1733
"-0.0"
1734
);
1735
assert_eq!(
1736
Ieee128::with_bits(0x3fff0000000000000000000000000000).to_string(), // 1.0
1737
"0x1.0000000000000000000000000000p0"
1738
);
1739
assert_eq!(
1740
Ieee128::with_bits(0x3fff8000000000000000000000000000).to_string(), // 1.5
1741
"0x1.8000000000000000000000000000p0"
1742
);
1743
assert_eq!(
1744
Ieee128::with_bits(0x3ffe0000000000000000000000000000).to_string(), // 0.5
1745
"0x1.0000000000000000000000000000p-1"
1746
);
1747
assert_eq!(
1748
Ieee128::with_bits(0x3f8f0000000000000000000000000000).to_string(), // `f128::EPSILON`
1749
"0x1.0000000000000000000000000000p-112"
1750
);
1751
assert_eq!(
1752
Ieee128::with_bits(0xfffeffffffffffffffffffffffffffff).to_string(), // `f128::MIN`
1753
"-0x1.ffffffffffffffffffffffffffffp16383"
1754
);
1755
assert_eq!(
1756
Ieee128::with_bits(0x7ffeffffffffffffffffffffffffffff).to_string(), // `f128::MAX`
1757
"0x1.ffffffffffffffffffffffffffffp16383"
1758
);
1759
// Smallest positive normal number.
1760
assert_eq!(
1761
Ieee128::with_bits(0x00010000000000000000000000000000).to_string(), // `f128::MIN_POSITIVE`
1762
"0x1.0000000000000000000000000000p-16382"
1763
);
1764
// Subnormals.
1765
assert_eq!(
1766
Ieee128::with_bits(0x00008000000000000000000000000000).to_string(), // `f128::MIN_POSITIVE / 2.0`
1767
"0x0.8000000000000000000000000000p-16382"
1768
);
1769
assert_eq!(
1770
Ieee128::with_bits(0x00000000000000000000000000000001).to_string(), // `f128::MIN_POSITIVE * f128::EPSILON`
1771
"0x0.0000000000000000000000000001p-16382"
1772
);
1773
assert_eq!(
1774
Ieee128::with_bits(0x7fff0000000000000000000000000000).to_string(), // `f128::INFINITY`
1775
"+Inf"
1776
);
1777
assert_eq!(
1778
Ieee128::with_bits(0xffff0000000000000000000000000000).to_string(), // `f128::NEG_INFINITY`
1779
"-Inf"
1780
);
1781
assert_eq!(
1782
Ieee128::with_bits(0x7fff8000000000000000000000000000).to_string(), // `f128::NAN`
1783
"+NaN"
1784
);
1785
assert_eq!(
1786
Ieee128::with_bits(0xffff8000000000000000000000000000).to_string(), // `-f128::NAN`
1787
"-NaN"
1788
);
1789
// Construct some qNaNs with payloads.
1790
assert_eq!(
1791
Ieee128::with_bits(0x7fff8000000000000000000000000001).to_string(),
1792
"+NaN:0x1"
1793
);
1794
assert_eq!(
1795
Ieee128::with_bits(0x7fffc000000000000000000000000001).to_string(),
1796
"+NaN:0x4000000000000000000000000001"
1797
);
1798
// Signaling NaNs.
1799
assert_eq!(
1800
Ieee128::with_bits(0x7fff0000000000000000000000000001).to_string(),
1801
"+sNaN:0x1"
1802
);
1803
assert_eq!(
1804
Ieee128::with_bits(0x7fff4000000000000000000000000001).to_string(),
1805
"+sNaN:0x4000000000000000000000000001"
1806
);
1807
}
1808
1809
#[test]
1810
fn parse_ieee128() {
1811
parse_ok::<Ieee128>("0.0", "0.0");
1812
parse_ok::<Ieee128>("-0.0", "-0.0");
1813
parse_ok::<Ieee128>("0x0", "0.0");
1814
parse_ok::<Ieee128>("0x0.0", "0.0");
1815
parse_ok::<Ieee128>("0x.0", "0.0");
1816
parse_ok::<Ieee128>("0x0.", "0.0");
1817
parse_ok::<Ieee128>("0x1", "0x1.0000000000000000000000000000p0");
1818
parse_ok::<Ieee128>("-0x1", "-0x1.0000000000000000000000000000p0");
1819
parse_ok::<Ieee128>("0x10", "0x1.0000000000000000000000000000p4");
1820
parse_ok::<Ieee128>("0x10.0", "0x1.0000000000000000000000000000p4");
1821
parse_err::<Ieee128>("0.", "Float must be hexadecimal");
1822
parse_err::<Ieee128>(".0", "Float must be hexadecimal");
1823
parse_err::<Ieee128>("0", "Float must be hexadecimal");
1824
parse_err::<Ieee128>("-0", "Float must be hexadecimal");
1825
parse_err::<Ieee128>(".", "Float must be hexadecimal");
1826
parse_err::<Ieee128>("", "Float must be hexadecimal");
1827
parse_err::<Ieee128>("-", "Float must be hexadecimal");
1828
parse_err::<Ieee128>("0x", "No digits");
1829
parse_err::<Ieee128>("0x..", "Multiple radix points");
1830
1831
// Check significant bits.
1832
parse_ok::<Ieee128>(
1833
"0x0.ffffffffffffffffffffffffffff8",
1834
"0x1.ffffffffffffffffffffffffffffp-1",
1835
);
1836
parse_ok::<Ieee128>(
1837
"0x1.ffffffffffffffffffffffffffff",
1838
"0x1.ffffffffffffffffffffffffffffp0",
1839
);
1840
parse_ok::<Ieee128>(
1841
"0x3.fffffffffffffffffffffffffffe",
1842
"0x1.ffffffffffffffffffffffffffffp1",
1843
);
1844
parse_ok::<Ieee128>(
1845
"0x7.fffffffffffffffffffffffffffc",
1846
"0x1.ffffffffffffffffffffffffffffp2",
1847
);
1848
parse_ok::<Ieee128>(
1849
"0xf.fffffffffffffffffffffffffff8",
1850
"0x1.ffffffffffffffffffffffffffffp3",
1851
);
1852
parse_err::<Ieee128>(
1853
"0x3.ffffffffffffffffffffffffffff",
1854
"Too many significant bits",
1855
);
1856
parse_err::<Ieee128>("0x001.fffffe000000000000000000000000", "Too many digits");
1857
1858
// Exponents.
1859
parse_ok::<Ieee128>("0x1p3", "0x1.0000000000000000000000000000p3");
1860
parse_ok::<Ieee128>("0x1p-3", "0x1.0000000000000000000000000000p-3");
1861
parse_ok::<Ieee128>("0x1.0p3", "0x1.0000000000000000000000000000p3");
1862
parse_ok::<Ieee128>("0x2.0p3", "0x1.0000000000000000000000000000p4");
1863
parse_ok::<Ieee128>("0x1.0p16383", "0x1.0000000000000000000000000000p16383");
1864
parse_ok::<Ieee128>("0x1.0p-16382", "0x1.0000000000000000000000000000p-16382");
1865
parse_ok::<Ieee128>("0x0.1p-16378", "0x1.0000000000000000000000000000p-16382");
1866
parse_err::<Ieee128>("0x2.0p16383", "Magnitude too large");
1867
1868
// Subnormals.
1869
parse_ok::<Ieee128>("0x1.0p-16383", "0x0.8000000000000000000000000000p-16382");
1870
parse_ok::<Ieee128>("0x1.0p-16494", "0x0.0000000000000000000000000001p-16382");
1871
parse_ok::<Ieee128>(
1872
"0x0.0000000000000000000000000001p-16382",
1873
"0x0.0000000000000000000000000001p-16382",
1874
);
1875
parse_err::<Ieee128>(
1876
"0x0.10000000000000000000000000008p-16382",
1877
"Subnormal underflow",
1878
);
1879
parse_err::<Ieee128>("0x1.8p-16494", "Subnormal underflow");
1880
parse_err::<Ieee128>("0x1.0p-16495", "Magnitude too small");
1881
1882
// NaNs and Infs.
1883
parse_ok::<Ieee128>("Inf", "+Inf");
1884
parse_ok::<Ieee128>("-Inf", "-Inf");
1885
parse_ok::<Ieee128>("NaN", "+NaN");
1886
parse_ok::<Ieee128>("-NaN", "-NaN");
1887
parse_ok::<Ieee128>("NaN:0x0", "+NaN");
1888
parse_err::<Ieee128>("NaN:", "Float must be hexadecimal");
1889
parse_err::<Ieee128>("NaN:0", "Float must be hexadecimal");
1890
parse_err::<Ieee128>("NaN:0x", "Invalid NaN payload");
1891
parse_ok::<Ieee128>("NaN:0x000001", "+NaN:0x1");
1892
parse_ok::<Ieee128>(
1893
"NaN:0x4000000000000000000000000001",
1894
"+NaN:0x4000000000000000000000000001",
1895
);
1896
parse_err::<Ieee128>("NaN:0x8000000000000000000000000001", "Invalid NaN payload");
1897
parse_ok::<Ieee128>("sNaN:0x1", "+sNaN:0x1");
1898
parse_err::<Ieee128>("sNaN:0x0", "Invalid sNaN payload");
1899
parse_ok::<Ieee128>(
1900
"sNaN:0x4000000000000000000000000001",
1901
"+sNaN:0x4000000000000000000000000001",
1902
);
1903
parse_err::<Ieee128>(
1904
"sNaN:0x8000000000000000000000000001",
1905
"Invalid sNaN payload",
1906
);
1907
}
1908
1909
#[test]
1910
fn pow2_ieee128() {
1911
assert_eq!(
1912
Ieee128::pow2(0).to_string(),
1913
"0x1.0000000000000000000000000000p0"
1914
);
1915
assert_eq!(
1916
Ieee128::pow2(1).to_string(),
1917
"0x1.0000000000000000000000000000p1"
1918
);
1919
assert_eq!(
1920
Ieee128::pow2(-1).to_string(),
1921
"0x1.0000000000000000000000000000p-1"
1922
);
1923
assert_eq!(
1924
Ieee128::pow2(16383).to_string(),
1925
"0x1.0000000000000000000000000000p16383"
1926
);
1927
assert_eq!(
1928
Ieee128::pow2(-16382).to_string(),
1929
"0x1.0000000000000000000000000000p-16382"
1930
);
1931
1932
assert_eq!(
1933
(-Ieee128::pow2(1)).to_string(),
1934
"-0x1.0000000000000000000000000000p1"
1935
);
1936
}
1937
1938
#[test]
1939
fn fcvt_to_sint_negative_overflow_ieee128() {
1940
// FIXME(#8312): Replace with commented out version once Rust f128 support is stabilised.
1941
// for n in [8, 16, 32, 64] {
1942
// assert_eq!(
1943
// -((1u128 << (n - 1)) as f128) - 1.0,
1944
// Ieee128::fcvt_to_sint_negative_overflow(n).as_f128(),
1945
// "n = {n}"
1946
// );
1947
// }
1948
for (n, expected) in [
1949
(8, "-0x1.0200000000000000000000000000p7"),
1950
(16, "-0x1.0002000000000000000000000000p15"),
1951
(32, "-0x1.0000000200000000000000000000p31"),
1952
(64, "-0x1.0000000000000002000000000000p63"),
1953
] {
1954
assert_eq!(
1955
expected,
1956
Ieee128::fcvt_to_sint_negative_overflow(n).to_string(),
1957
"n = {n}"
1958
);
1959
}
1960
}
1961
}
1962
1963