Implement IEEE immediates for binary32 and binary64.
Clarify the textual encoding of floating point numbers. Don't allow decimal floating point since conversion to/from binary can produce rounding problems on some (buggy) systems.
This commit is contained in:
@@ -212,6 +212,9 @@ indicate the different kinds of immediate operands on an instruction.
|
|||||||
signed two's complement integer. Instruction encodings may limit the valid
|
signed two's complement integer. Instruction encodings may limit the valid
|
||||||
range.
|
range.
|
||||||
|
|
||||||
|
In the textual format, :type:`imm64` immediates appear as decimal or
|
||||||
|
hexadecimal literals using the same syntax as C.
|
||||||
|
|
||||||
.. type:: ieee32
|
.. type:: ieee32
|
||||||
|
|
||||||
A 32-bit immediate floating point number in the IEEE 754-2008 binary32
|
A 32-bit immediate floating point number in the IEEE 754-2008 binary32
|
||||||
@@ -229,6 +232,38 @@ indicate the different kinds of immediate operands on an instruction.
|
|||||||
bits of the operand are interpreted as if the SIMD vector was loaded from
|
bits of the operand are interpreted as if the SIMD vector was loaded from
|
||||||
memory containing the immediate.
|
memory containing the immediate.
|
||||||
|
|
||||||
|
The two IEEE floating point immediate types :type:`ieee32` and :type:`ieee64`
|
||||||
|
are displayed as hexadecimal floating point literals in the textual IL format.
|
||||||
|
Decimal floating point literals are not allowed because some computer systems
|
||||||
|
can round differently when converting to binary. The hexadecimal floating point
|
||||||
|
format is mostly the same as the one used by C99, but extended to represent all
|
||||||
|
NaN bit patterns:
|
||||||
|
|
||||||
|
Normal numbers
|
||||||
|
Compatible with C99: ``-0x1.Tpe`` where ``T`` are the trailing
|
||||||
|
significand bits encoded as hexadecimal, and ``e`` is the unbiased exponent
|
||||||
|
as a decimal number. :type:`ieee32` has 23 trailing significand bits. They
|
||||||
|
are padded with an extra LSB to produce 6 hexadecimal digits. This is not
|
||||||
|
necessary for :type:`ieee64` which has 52 trailing significand bits
|
||||||
|
forming 13 hexadecimal digits with no padding.
|
||||||
|
|
||||||
|
Subnormal numbers
|
||||||
|
Compatible with C99: ``-0x0.Tpemin`` where ``T`` are the trailing
|
||||||
|
significand bits encoded as hexadecimal, and ``emin`` is the minimum exponent
|
||||||
|
as a decimal number.
|
||||||
|
|
||||||
|
Infinities
|
||||||
|
Either ``-Inf`` or ``Inf``.
|
||||||
|
|
||||||
|
Quiet NaNs
|
||||||
|
Quiet NaNs have the MSB of the trailing significand set. If the remaining
|
||||||
|
bits of the trailing significand are all zero, the value is displayed as
|
||||||
|
``-qNaN`` or ``qNaN``. Otherwise, ``-qNaN:0xT`` where ``T`` are the
|
||||||
|
trailing significand bits encoded as hexadecimal.
|
||||||
|
|
||||||
|
Signaling NaNs
|
||||||
|
Displayed as ``-sNaN:0xT``.
|
||||||
|
|
||||||
Control flow
|
Control flow
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//! module in the meta language.
|
//! module in the meta language.
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
/// 64-bit immediate integer operand.
|
/// 64-bit immediate integer operand.
|
||||||
///
|
///
|
||||||
@@ -36,9 +37,123 @@ impl Display for Imm64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// An IEEE binary32 immediate floating point value.
|
||||||
|
///
|
||||||
|
/// All bit patterns are allowed.
|
||||||
|
pub struct Ieee32(f32);
|
||||||
|
|
||||||
|
/// An IEEE binary64 immediate floating point value.
|
||||||
|
///
|
||||||
|
/// All bit patterns are allowed.
|
||||||
|
pub struct Ieee64(f64);
|
||||||
|
|
||||||
|
// Format a floating point number in a way that is reasonably human-readable, and that can be
|
||||||
|
// converted back to binary without any rounding issues. The hexadecimal formatting of normal and
|
||||||
|
// subnormal numbers is compatible with C99 and the printf "%a" format specifier. The NaN and Inf
|
||||||
|
// formats are not supported by C99.
|
||||||
|
//
|
||||||
|
// The encoding parameters are:
|
||||||
|
//
|
||||||
|
// w - exponent field width in bits
|
||||||
|
// t - trailing significand field width in bits
|
||||||
|
//
|
||||||
|
fn format_float(bits: u64, w: u8, t: u8, f: &mut Formatter) -> fmt::Result {
|
||||||
|
assert!(w > 0 && w <= 16, "Invalid exponent range");
|
||||||
|
assert!(1 + w + t <= 64, "Too large IEEE format for u64");
|
||||||
|
|
||||||
|
let max_e_bits = (1u64 << w) - 1;
|
||||||
|
let t_bits = bits & ((1u64 << t) - 1); // Trailing significand.
|
||||||
|
let e_bits = (bits >> t) & max_e_bits; // Biased exponent.
|
||||||
|
let sign_bit = (bits >> w + t) & 1;
|
||||||
|
|
||||||
|
let bias: i32 = (1 << (w - 1)) - 1;
|
||||||
|
let e = e_bits as i32 - bias; // Unbiased exponent.
|
||||||
|
let emin = 1 - bias; // Minimum exponent.
|
||||||
|
|
||||||
|
// How many hexadecimal digits are needed for the trailing significand?
|
||||||
|
let digits = (t + 3) / 4;
|
||||||
|
// Trailing significand left-aligned in `digits` hexadecimal digits.
|
||||||
|
let left_t_bits = t_bits << (4 * digits - t);
|
||||||
|
|
||||||
|
// All formats share the leading sign.
|
||||||
|
if sign_bit != 0 {
|
||||||
|
try!(write!(f, "-"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if e_bits == 0 {
|
||||||
|
if t_bits == 0 {
|
||||||
|
// Zero.
|
||||||
|
write!(f, "0.0")
|
||||||
|
} else {
|
||||||
|
// Subnormal.
|
||||||
|
write!(f, "0x0.{0:01$x}p{2}", left_t_bits, digits as usize, emin)
|
||||||
|
}
|
||||||
|
} else if e_bits == max_e_bits {
|
||||||
|
if t_bits == 0 {
|
||||||
|
// Infinity.
|
||||||
|
write!(f, "Inf")
|
||||||
|
} else {
|
||||||
|
// NaN.
|
||||||
|
let payload = t_bits & ((1 << (t - 1)) - 1);
|
||||||
|
if t_bits & (1 << (t - 1)) != 0 {
|
||||||
|
// Quiet NaN.
|
||||||
|
if payload != 0 {
|
||||||
|
write!(f, "qNaN:0x{:x}", payload)
|
||||||
|
} else {
|
||||||
|
write!(f, "qNaN")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Signaling NaN.
|
||||||
|
write!(f, "sNaN:0x{:x}", payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal number.
|
||||||
|
write!(f, "0x1.{0:01$x}p{2}", left_t_bits, digits as usize, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ieee32 {
|
||||||
|
pub fn new(x: f32) -> Ieee32 {
|
||||||
|
Ieee32(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct Ieee32 immediate from raw bits.
|
||||||
|
pub fn new_from_bits(x: u32) -> Ieee32 {
|
||||||
|
Ieee32(unsafe { mem::transmute(x) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Ieee32 {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let bits: u32 = unsafe { mem::transmute(self.0) };
|
||||||
|
format_float(bits as u64, 8, 23, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ieee64 {
|
||||||
|
pub fn new(x: f64) -> Ieee64 {
|
||||||
|
Ieee64(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct Ieee64 immediate from raw bits.
|
||||||
|
pub fn new_from_bits(x: u64) -> Ieee64 {
|
||||||
|
Ieee64(unsafe { mem::transmute(x) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Ieee64 {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let bits: u64 = unsafe { mem::transmute(self.0) };
|
||||||
|
format_float(bits, 11, 52, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::{f32, f64};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format_imm64() {
|
fn format_imm64() {
|
||||||
@@ -50,4 +165,73 @@ mod tests {
|
|||||||
assert_eq!(format!("{}", Imm64(0xffff)), "0xffff");
|
assert_eq!(format!("{}", Imm64(0xffff)), "0xffff");
|
||||||
assert_eq!(format!("{}", Imm64(0x10000)), "0x0001_0000");
|
assert_eq!(format!("{}", Imm64(0x10000)), "0x0001_0000");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_ieee32() {
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(0.0)), "0.0");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(-0.0)), "-0.0");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(1.0)), "0x1.000000p0");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(1.5)), "0x1.800000p0");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(0.5)), "0x1.000000p-1");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::EPSILON)), "0x1.000000p-23");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::MIN)), "-0x1.fffffep127");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::MAX)), "0x1.fffffep127");
|
||||||
|
// Smallest positive normal number.
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::MIN_POSITIVE)),
|
||||||
|
"0x1.000000p-126");
|
||||||
|
// Subnormals.
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::MIN_POSITIVE / 2.0)),
|
||||||
|
"0x0.800000p-126");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::MIN_POSITIVE * f32::EPSILON)),
|
||||||
|
"0x0.000002p-126");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::INFINITY)), "Inf");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::NEG_INFINITY)), "-Inf");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(f32::NAN)), "qNaN");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new(-f32::NAN)), "-qNaN");
|
||||||
|
// Construct some qNaNs with payloads.
|
||||||
|
assert_eq!(format!("{}", Ieee32::new_from_bits(0x7fc00001)), "qNaN:0x1");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new_from_bits(0x7ff00001)),
|
||||||
|
"qNaN:0x300001");
|
||||||
|
// Signaling NaNs.
|
||||||
|
assert_eq!(format!("{}", Ieee32::new_from_bits(0x7f800001)), "sNaN:0x1");
|
||||||
|
assert_eq!(format!("{}", Ieee32::new_from_bits(0x7fa00001)),
|
||||||
|
"sNaN:0x200001");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_ieee64() {
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(0.0)), "0.0");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(-0.0)), "-0.0");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(1.0)), "0x1.0000000000000p0");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(1.5)), "0x1.8000000000000p0");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(0.5)), "0x1.0000000000000p-1");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::EPSILON)),
|
||||||
|
"0x1.0000000000000p-52");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::MIN)),
|
||||||
|
"-0x1.fffffffffffffp1023");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::MAX)),
|
||||||
|
"0x1.fffffffffffffp1023");
|
||||||
|
// Smallest positive normal number.
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::MIN_POSITIVE)),
|
||||||
|
"0x1.0000000000000p-1022");
|
||||||
|
// Subnormals.
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::MIN_POSITIVE / 2.0)),
|
||||||
|
"0x0.8000000000000p-1022");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::MIN_POSITIVE * f64::EPSILON)),
|
||||||
|
"0x0.0000000000001p-1022");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::INFINITY)), "Inf");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::NEG_INFINITY)), "-Inf");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(f64::NAN)), "qNaN");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new(-f64::NAN)), "-qNaN");
|
||||||
|
// Construct some qNaNs with payloads.
|
||||||
|
assert_eq!(format!("{}", Ieee64::new_from_bits(0x7ff8000000000001)),
|
||||||
|
"qNaN:0x1");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new_from_bits(0x7ffc000000000001)),
|
||||||
|
"qNaN:0x4000000000001");
|
||||||
|
// Signaling NaNs.
|
||||||
|
assert_eq!(format!("{}", Ieee64::new_from_bits(0x7ff0000000000001)),
|
||||||
|
"sNaN:0x1");
|
||||||
|
assert_eq!(format!("{}", Ieee64::new_from_bits(0x7ff4000000000001)),
|
||||||
|
"sNaN:0x4000000000001");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user