diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
commit | d1b2d29528b7794b41e66fc2136e395a02f8529b (patch) | |
tree | a4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/rustc_apfloat/tests | |
parent | Releasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/rustc_apfloat/tests')
-rw-r--r-- | vendor/rustc_apfloat/tests/downstream.rs | 410 | ||||
-rw-r--r-- | vendor/rustc_apfloat/tests/ieee.rs | 4927 | ||||
-rw-r--r-- | vendor/rustc_apfloat/tests/ppc.rs | 470 |
3 files changed, 5807 insertions, 0 deletions
diff --git a/vendor/rustc_apfloat/tests/downstream.rs b/vendor/rustc_apfloat/tests/downstream.rs new file mode 100644 index 000000000..fc60a7c05 --- /dev/null +++ b/vendor/rustc_apfloat/tests/downstream.rs @@ -0,0 +1,410 @@ +//! Tests added to `rustc_apfloat`, that were not ported from the C++ code. + +use rustc_apfloat::ieee::{Double, Single, X87DoubleExtended}; +use rustc_apfloat::Float; + +// `f32 -> i128 -> f32` previously-crashing bit-patterns (found by fuzzing). +pub const FUZZ_IEEE32_ROUNDTRIP_THROUGH_I128_CASES: &[u32] = &[ + 0xff000000, // -1.7014118e+38 + 0xff00e203, // -1.713147e+38 + 0xff00e900, // -1.7135099e+38 + 0xff7fffff, // -3.4028235e+38 + 0xff800000, // -inf +]; + +// `f64 -> i128 -> f64` previously-crashing bit-patterns (found by fuzzing). +pub const FUZZ_IEEE64_ROUNDTRIP_THROUGH_I128_CASES: &[u64] = &[ + 0xc7e5d58020ffedff, // -2.3217876724230413e+38 + 0xc7e7fffefefeff00, // -2.5521161229511617e+38 + 0xc7e8030000653636, // -2.5533639056589687e+38 + 0xea3501e2e8950007, // -4.116505897277026e+203 + 0xf3ff0620ca000600, // -5.553072340247723e+250 + 0xffc909842600d4ff, // -3.516340112093497e+307 + 0xfff0000000000000, // -inf +]; + +#[test] +fn fuzz_roundtrip_through_i128() { + for &bits in FUZZ_IEEE32_ROUNDTRIP_THROUGH_I128_CASES { + assert_eq!( + Single::from_i128(Single::from_bits(bits.into()).to_i128(128).value) + .value + .to_bits(), + (f32::from_bits(bits) as i128 as f32).to_bits().into() + ); + } + for &bits in FUZZ_IEEE64_ROUNDTRIP_THROUGH_I128_CASES { + assert_eq!( + Double::from_i128(Double::from_bits(bits.into()).to_i128(128).value) + .value + .to_bits(), + (f64::from_bits(bits) as i128 as f64).to_bits().into() + ); + } +} + +// `f32` FMA bit-patterns which used to produce the wrong output (found by fuzzing). +pub const FUZZ_IEEE32_FMA_CASES_WITH_EXPECTED_OUTPUTS: &[((u32, u32, u32), u32)] = &[ + ((0x00001000 /* 5.74e-42 */, 0x0000001a /* 3.6e-44 */, 0xffff1a00 /* NaN */), 0xffff1a00 /* NaN */), + ((0x000080aa /* 4.6156e-41 */, 0xaaff0000 /* -4.52971e-13 */, 0xff9e007f /* NaN */), 0xffde007f /* NaN */), + ((0x0000843f /* 4.7441e-41 */, 0x0084ff80 /* 1.2213942e-38 */, 0xffff8000 /* NaN */), 0xffff8000 /* NaN */), + ((0x00009eaa /* 5.6918e-41 */, 0x201d7f1e /* 1.3340477e-19 */, 0xffff0001 /* NaN */), 0xffff0001 /* NaN */), + ((0x020400ff /* 9.698114e-38 */, 0x7f7f2200 /* 3.3912968e+38 */, 0xffffffff /* NaN */), 0xffffffff /* NaN */), + ((0x02060320 /* 9.845662e-38 */, 0x20002521 /* 1.0854307e-19 */, 0x7f800000 /* inf */), 0x7f800000 /* inf */), + ( + (0x04000080 /* 1.5046557e-36 */, 0xff7fff00 /* -3.4027717e+38 */, 0xff800000 /* -inf */), + 0xff800000, /* -inf */ + ), + ( + (0x04007faa /* 1.5104948e-36 */, 0xff200000 /* -2.1267648e+38 */, 0xffff0000 /* NaN */), + 0xffff0000, /* NaN */ + ), + ((0x1e0603ff /* 7.094727e-21 */, 0x00100000 /* 1.469368e-39 */, 0xffffff4f /* NaN */), 0xffffff4f /* NaN */), + ((0x200004aa /* 1.0843565e-19 */, 0x00202020 /* 2.95026e-39 */, 0x7fff00ff /* NaN */), 0x7fff00ff /* NaN */), + ( + (0x20005eaa /* 1.0873343e-19 */, 0x9e9e9e3a /* -1.6794342e-20 */, 0xff9e009e /* NaN */), + 0xffde009e, /* NaN */ + ), + ((0x20007faa /* 1.0884262e-19 */, 0x9e00611e /* -6.796347e-21 */, 0x7faa0600 /* NaN */), 0x7fea0600 /* NaN */), + ( + (0x20007faa /* 1.0884262e-19 */, 0xaa069e1e /* -1.1956449e-13 */, 0xffffecff /* NaN */), + 0xffffecff, /* NaN */ + ), + ((0x20025eaa /* 1.104275e-19 */, 0x9e01033a /* -6.82987e-21 */, 0xff9e009e /* NaN */), 0xffde009e /* NaN */), + ((0x3314f400 /* 3.4680852e-8 */, 0x00ff7903 /* 2.3461462e-38 */, 0xffffffdb /* NaN */), 0xffffffdb /* NaN */), + ((0x3314f400 /* 3.4680852e-8 */, 0x00ff7903 /* 2.3461462e-38 */, 0xfffffff6 /* NaN */), 0xfffffff6 /* NaN */), + ((0x3a218275 /* 0.0006161102 */, 0x3a3a3a3a /* 0.00071040133 */, 0x7f8a063a /* NaN */), 0x7fca063a /* NaN */), + ((0x40000001 /* 2.0000002 */, 0xfefffffe /* -1.7014116e+38 */, 0xfffe40ff /* NaN */), 0xfffe40ff /* NaN */), + ((0x50007faa /* 8623401000 */, 0x000011fb /* 6.45e-42 */, 0xff800000 /* -inf */), 0xff800000 /* -inf */), + ((0x64007f8b /* 9.481495e+21 */, 0xfa9a8702 /* -4.01176e+35 */, 0xff820000 /* NaN */), 0xffc20000 /* NaN */), + ((0x6a017faa /* 3.9138577e+25 */, 0x00000070 /* 1.57e-43 */, 0xff80db03 /* NaN */), 0xffc0db03 /* NaN */), + ((0x6a017faa /* 3.9138577e+25 */, 0x00000070 /* 1.57e-43 */, 0xff80db23 /* NaN */), 0xffc0db23 /* NaN */), + ( + (0x6e000000 /* 9.9035203e+27 */, 0xdf008000 /* -9259401000000000000 */, 0x7f800000 /* inf */), + 0x7f800000, /* inf */ + ), + ((0x7f7fff00 /* 3.4027717e+38 */, 0x02000080 /* 9.404098e-38 */, 0x7fc00000 /* NaN */), 0x7fc00000 /* NaN */), + ( + (0xb3eb00ff /* -1.09432214e-7 */, 0x00ffefe2 /* 2.3504105e-38 */, 0xfffffee9 /* NaN */), + 0xfffffee9, /* NaN */ + ), + ( + (0xdf0603ff /* -9656842000000000000 */, 0x808000ff /* -1.1755301e-38 */, 0xff9b0000 /* NaN */), + 0xffdb0000, /* NaN */ + ), + ( + ( + 0xf1001101, /* -634154200000000000000000000000 */ + 0x7f400000, /* 255211780000000000000000000000000000000 */ + 0x7f800000, /* inf */ + ), + 0x7f800000, /* inf */ + ), + ((0xf5000080 /* -1.6226175e+32 */, 0xc9ffff00 /* -2097120 */, 0xffff7fff /* NaN */), 0xffff7fff /* NaN */), + ( + (0xf5ffffff /* -6.4903707e+32 */, 0xff000b09 /* -1.7019848e+38 */, 0xff800000 /* -inf */), + 0xff800000, /* -inf */ + ), + ( + (0xf70029e8 /* -2.5994686e+33 */, 0xf7ffff7f /* -1.0384514e+34 */, 0xffff7fff /* NaN */), + 0xffff7fff, /* NaN */ + ), + ( + (0xff007faa /* -1.7080405e+38 */, 0xd3fface5 /* -2196234700000 */, 0xffff7f00 /* NaN */), + 0xffff7f00, /* NaN */ + ), + ( + (0xff200000 /* -2.1267648e+38 */, 0xe380ffff /* -4.7592594e+21 */, 0xff800000 /* -inf */), + 0xff800000, /* -inf */ + ), + ((0xff6d0000 /* -3.1502704e+38 */, 0xc12005ff /* -10.001464 */, 0xff800000 /* -inf */), 0xff800000 /* -inf */), +]; + +// `f64` FMA bit-patterns which used to produce the wrong output (found by fuzzing). +pub const FUZZ_IEEE64_FMA_CASES_WITH_EXPECTED_OUTPUTS: &[((u64, u64, u64), u64)] = &[ + ( + ( + 0x000000000000001e, /* 1.5e-322 */ + 0x00000000ffdf0000, /* 2.120927281e-314 */ + 0xffffff8000000000, /* NaN */ + ), + 0xffffff8000000000, /* NaN */ + ), + ( + ( + 0x000000007fffffff, /* 1.060997895e-314 */ + 0xff00000000200000, /* -5.486124071348364e+303 */ + 0xfffd0000000000e9, /* NaN */ + ), + 0xfffd0000000000e9, /* NaN */ + ), + ( + ( + 0x0000020000e30000, /* 1.086469195027e-311 */ + 0xff00000011000000, /* -5.48612441622957e+303 */ + 0xfffd00000000e0e9, /* NaN */ + ), + 0xfffd00000000e0e9, /* NaN */ + ), + ( + ( + 0x0000040000006400, /* 2.1729237025965e-311 */ + 0x000000e5ff000000, /* 4.88050742876e-312 */ + 0xffffffe300000000, /* NaN */ + ), + 0xffffffe300000000, /* NaN */ + ), + ( + ( + 0x00006a0000000000, /* 5.75824777836336e-310 */ + 0x005015000018f9f1, /* 3.5783707339010265e-307 */ + 0x7fffffde00000000, /* NaN */ + ), + 0x7fffffde00000000, /* NaN */ + ), + ( + ( + 0x00007ffa01000373, /* 6.95208343930866e-310 */ + 0x0005000000ff107f, /* 6.95335589042254e-309 */ + 0xffffffff00000005, /* NaN */ + ), + 0xffffffff00000005, /* NaN */ + ), + ( + ( + 0x0000ff8000000000, /* 1.387955006954565e-309 */ + 0x0000000001000000, /* 8.289046e-317 */ + 0xfff0000000000000, /* -inf */ + ), + 0xfff0000000000000, /* -inf */ + ), + ( + ( + 0x0002a000f6290000, /* 3.650532203442106e-309 */ + 0x400013fffd000000, /* 2.009765602648258 */ + 0xfffdfe0000ff9aff, /* NaN */ + ), + 0xfffdfe0000ff9aff, /* NaN */ + ), + ( + ( + 0x0006000000001700, /* 8.344026969431096e-309 */ + 0xd9000000da080000, /* -5.164503950933907e+120 */ + 0xfffffee5000000fd, /* NaN */ + ), + 0xfffffee5000000fd, /* NaN */ + ), + ( + ( + 0x0006000040000013, /* 8.344032274391576e-309 */ + 0xfafe036500061100, /* -2.7893890583525793e+284 */ + 0xffff7fff00001011, /* NaN */ + ), + 0xffff7fff00001011, /* NaN */ + ), + ( + ( + 0x00f1000000640000, /* 3.873408578194326e-304 */ + 0xffe6005e00000000, /* -1.2359946076651026e+308 */ + 0xfffd007000000000, /* NaN */ + ), + 0xfffd007000000000, /* NaN */ + ), + ( + ( + 0x05203a0080ff0513, /* 5.456081264530354e-284 */ + 0xf90000000000f7ff, /* -6.924462078599005e+274 */ + 0xfff0000000000000, /* -inf */ + ), + 0xfff0000000000000, /* -inf */ + ), + ( + ( + 0x0540400001000513, /* 2.1855837639726535e-283 */ + 0xee05130640000100, /* -9.522265158052987e+221 */ + 0x7fff00001004fa01, /* NaN */ + ), + 0x7fff00001004fa01, /* NaN */ + ), + ( + ( + 0x0540400001000513, /* 2.1855837639726535e-283 */ + 0xffd8000000000000, /* -6.741349255733685e+307 */ + 0xfff0001000000000, /* NaN */ + ), + 0xfff8001000000000, /* NaN */ + ), + ( + ( + 0x054040000100e213, /* 2.1855837639996873e-283 */ + 0xfbd8000000000000, /* -3.6544927542749997e+288 */ + 0xfff0ff1000000000, /* NaN */ + ), + 0xfff8ff1000000000, /* NaN */ + ), + ( + ( + 0x060000000000ff04, /* 8.814425663530262e-280 */ + 0x00000020ffff0606, /* 7.00258294846e-313 */ + 0xffffffde00001300, /* NaN */ + ), + 0xffffffde00001300, /* NaN */ + ), + ( + ( + 0x1306400001000513, /* 5.042468007014986e-217 */ + 0x00001004fa03ee05, /* 8.7022551317144e-311 */ + 0xfffc80f7ffff7fff, /* NaN */ + ), + 0xfffc80f7ffff7fff, /* NaN */ + ), + ( + ( + 0x1306400001000513, /* 5.042468007014986e-217 */ + 0xa5001004fa01ee05, /* -1.810368898568446e-130 */ + 0xfffa80f7ff1b7fff, /* NaN */ + ), + 0xfffa80f7ff1b7fff, /* NaN */ + ), + ( + ( + 0x4006400005130100, /* 2.7812500378059895 */ + 0x0000ff4000000000, /* 1.38659692964835e-309 */ + 0x7fffffec4200044b, /* NaN */ + ), + 0x7fffffec4200044b, /* NaN */ + ), + ( + ( + 0x4100000001000000, /* 131072.00048828125 */ + 0x0000fffffff00000, /* 1.390671156386347e-309 */ + 0xfffffe00000040ff, /* NaN */ + ), + 0xfffffe00000040ff, /* NaN */ + ), + ( + ( + 0x7a7a7a7a7a7a0000, /* 9.61276249042562e+281 */ + 0xff7a7a7a7a7a7a7a, /* -1.1621116772547446e+306 */ + 0xfffd007000ef0000, /* NaN */ + ), + 0xfffd007000ef0000, /* NaN */ + ), + ( + ( + 0x7f000012007ff010, /* 5.4862182545686e+303 */ + 0x7f0000120091f010, /* 5.486218256005604e+303 */ + 0xfff0000000000000, /* -inf */ + ), + 0xfff0000000000000, /* -inf */ + ), + ( + ( + 0x7f0022000a8000f6, /* 5.531663399192155e+303 */ + 0xff00ebfef0800300, /* -5.802213559159178e+303 */ + 0x7ff0000000000000, /* inf */ + ), + 0x7ff0000000000000, /* inf */ + ), + ( + ( + 0x7f06400001000513, /* 7.62914130360521e+303 */ + 0xff001004fb88f7ff, /* -5.507580309563204e+303 */ + 0xfffa01ee0513ffff, /* NaN */ + ), + 0xfffa01ee0513ffff, /* NaN */ + ), + ( + ( + 0xbbbbbb7f01000513, /* -5.872565540268704e-21 */ + 0x0100bbbbbbbbbbbb, /* 7.625298445452731e-304 */ + 0xffffff4000004000, /* NaN */ + ), + 0xffffff4000004000, /* NaN */ + ), + ( + ( + 0xbc00000000400000, /* -1.0842021734952464e-19 */ + 0x00bc000000004000, /* 3.987332354453194e-305 */ + 0xfff0000000e20000, /* NaN */ + ), + 0xfff8000000e20000, /* NaN */ + ), + ( + ( + 0xddff000004000000, /* -6.048387862754913e+144 */ + 0xff00000000000000, /* -5.486124068793689e+303 */ + 0xffffff0000000000, /* NaN */ + ), + 0xffffff0000000000, /* NaN */ + ), + ( + ( + 0xe100051b060c0513, /* -1.759578741202065e+159 */ + 0xfbfeee0513064110, /* -1.8838766970066999e+289 */ + 0xffff7fdf00001004, /* NaN */ + ), + 0xffff7fdf00001004, /* NaN */ + ), + ( + ( + 0xf0000000007ff010, /* -3.1050361903821855e+231 */ + 0x7f06010800180000, /* 7.54480183807128e+303 */ + 0x7ff0000000000000, /* inf */ + ), + 0x7ff0000000000000, /* inf */ + ), + ( + ( + 0xf4ffff05021d7d12, /* -3.753309156386366e+255 */ + 0xfd100000e8030000, /* -2.5546778042386733e+294 */ + 0xfff0000000000000, /* -inf */ + ), + 0xfff0000000000000, /* -inf */ + ), + ( + ( + 0xff0000fff05f0001, /* -5.48746313513839e+303 */ + 0xff0000fff0800000, /* -5.487463137772898e+303 */ + 0xfff0000000000000, /* -inf */ + ), + 0xfff0000000000000, /* -inf */ + ), +]; + +#[test] +fn fuzz_fma_with_expected_outputs() { + for &((a_bits, b_bits, c_bits), expected_bits) in FUZZ_IEEE32_FMA_CASES_WITH_EXPECTED_OUTPUTS { + let (a, b, c) = + (Single::from_bits(a_bits.into()), Single::from_bits(b_bits.into()), Single::from_bits(c_bits.into())); + assert_eq!(a.mul_add(b, c).value.to_bits(), expected_bits.into()); + } + for &((a_bits, b_bits, c_bits), expected_bits) in FUZZ_IEEE64_FMA_CASES_WITH_EXPECTED_OUTPUTS { + let (a, b, c) = + (Double::from_bits(a_bits.into()), Double::from_bits(b_bits.into()), Double::from_bits(c_bits.into())); + assert_eq!(a.mul_add(b, c).value.to_bits(), expected_bits.into()); + } +} + +// x87 80-bit "extended precision"/`long double` bit-patterns which used to +// produce the wrong output when negated (found by fuzzing - though fuzzing also +// found many examples in all ops, as the root issue was the handling of the +// bit-level encoding itself, but negation was the easiest op to test here). +pub const FUZZ_X87_F80_NEG_CASES_WITH_EXPECTED_OUTPUTS: &[(u128, u128)] = &[ + (0x01010101010100000000 /* NaN */, 0xffff0101010100000000 /* NaN */), + ( + 0x0000ff7f2300ff000000, /* 6.71098449692300485303E-4932 */ + 0x8001ff7f2300ff000000, /* -6.71098449692300485303E-4932 */ + ), + ( + 0x00008000000000000000, /* 3.36210314311209350626E-4932 */ + 0x80018000000000000000, /* -3.36210314311209350626E-4932 */ + ), +]; + +#[test] +fn fuzz_x87_f80_neg_with_expected_outputs() { + for &(bits, expected_bits) in FUZZ_X87_F80_NEG_CASES_WITH_EXPECTED_OUTPUTS { + assert_eq!((-X87DoubleExtended::from_bits(bits)).to_bits(), expected_bits); + } +} diff --git a/vendor/rustc_apfloat/tests/ieee.rs b/vendor/rustc_apfloat/tests/ieee.rs new file mode 100644 index 000000000..8af64dc5a --- /dev/null +++ b/vendor/rustc_apfloat/tests/ieee.rs @@ -0,0 +1,4927 @@ +#[macro_use] +extern crate rustc_apfloat; + +use core::cmp::Ordering; +use rustc_apfloat::ieee::{BFloat, Double, Float8E4M3FN, Float8E5M2, Half, Quad, Single, X87DoubleExtended}; +use rustc_apfloat::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO}; +use rustc_apfloat::{Float, FloatConvert, Round, Status}; + +// FIXME(eddyb) maybe include this in `rustc_apfloat` itself? +macro_rules! define_for_each_float_type { + ($($ty:ty),+ $(,)?) => { + macro_rules! for_each_float_type { + // FIXME(eddyb) use generic closures if they're ever added to Rust. + (for<$ty_var:ident: Float> $e:expr) => {{ + $({ + type $ty_var = $ty; + $e; + })+ + }} + } + } +} +define_for_each_float_type! { + Half, + Single, + Double, + Quad, + + BFloat, + Float8E5M2, + Float8E4M3FN, + X87DoubleExtended, + + // NOTE(eddyb) tests for this are usually in `ppc.rs` but this works too. + rustc_apfloat::ppc::DoubleDouble, +} + +trait SingleExt { + fn from_f32(input: f32) -> Self; + fn to_f32(self) -> f32; +} + +impl SingleExt for Single { + fn from_f32(input: f32) -> Self { + Self::from_bits(input.to_bits() as u128) + } + + fn to_f32(self) -> f32 { + f32::from_bits(self.to_bits() as u32) + } +} + +trait DoubleExt { + fn from_f64(input: f64) -> Self; + fn to_f64(self) -> f64; +} + +impl DoubleExt for Double { + fn from_f64(input: f64) -> Self { + Self::from_bits(input.to_bits() as u128) + } + + fn to_f64(self) -> f64 { + f64::from_bits(self.to_bits() as u64) + } +} + +// NOTE(eddyb) these match the C++ `convertToFloat`/`convertToDouble` methods, +// after their generalization to allow an optional lossless conversion to their +// expected semantics (from e.g. `IEEEhalf`/`BFloat`, for `convertToSingle`). +// FIXME(eddyb) should the methods have e.g. `_lossless_via_convert` in their names? +fn assert_lossless_conversion<S: FloatConvert<T>, T: Float>(src: S) -> T { + let mut loses_info = false; + let status; + let r = unpack!(status=, src.convert(&mut loses_info)); + assert!(!status.intersects(Status::INEXACT) && !loses_info, "Unexpected imprecision"); + r +} + +trait ToF32LosslessViaConvertToSingle: FloatConvert<Single> { + fn to_f32(self) -> f32 { + assert_lossless_conversion(self).to_f32() + } +} +impl ToF32LosslessViaConvertToSingle for Half {} +impl ToF32LosslessViaConvertToSingle for BFloat {} +impl ToF32LosslessViaConvertToSingle for Float8E5M2 {} +impl ToF32LosslessViaConvertToSingle for Float8E4M3FN {} + +trait ToF64LosslessViaConvertToDouble: FloatConvert<Double> { + fn to_f64(self) -> f64 { + assert_lossless_conversion(self).to_f64() + } +} +impl ToF64LosslessViaConvertToDouble for Single {} +// HACK(eddyb) take advantage of the transitivity of "are conversions lossless". +impl<T: ToF32LosslessViaConvertToSingle + FloatConvert<Double>> ToF64LosslessViaConvertToDouble for T {} + +#[test] +fn is_signaling() { + // We test qNaN, -qNaN, +sNaN, -sNaN with and without payloads. + let payload = 4; + assert!(!Single::qnan(None).is_signaling()); + assert!(!(-Single::qnan(None)).is_signaling()); + assert!(!Single::qnan(Some(payload)).is_signaling()); + assert!(!(-Single::qnan(Some(payload))).is_signaling()); + assert!(Single::snan(None).is_signaling()); + assert!((-Single::snan(None)).is_signaling()); + assert!(Single::snan(Some(payload)).is_signaling()); + assert!((-Single::snan(Some(payload))).is_signaling()); +} + +#[test] +fn next() { + // 1. Test Special Cases Values. + // + // Test all special values for nextUp and nextDown perscribed by IEEE-754R + // 2008. These are: + // 1. +inf + // 2. -inf + // 3. largest + // 4. -largest + // 5. smallest + // 6. -smallest + // 7. qNaN + // 8. sNaN + // 9. +0 + // 10. -0 + + let mut status; + + // nextUp(+inf) = +inf. + let test = unpack!(status=, Quad::INFINITY.next_up()); + let expected = Quad::INFINITY; + assert_eq!(status, Status::OK); + assert!(test.is_infinite()); + assert!(!test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(+inf) = -nextUp(-inf) = -(-largest) = largest + let test = unpack!(status=, Quad::INFINITY.next_down()); + let expected = Quad::largest(); + assert_eq!(status, Status::OK); + assert!(!test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextUp(-inf) = -largest + let test = unpack!(status=, (-Quad::INFINITY).next_up()); + let expected = -Quad::largest(); + assert_eq!(status, Status::OK); + assert!(test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(-inf) = -nextUp(+inf) = -(+inf) = -inf. + let test = unpack!(status=, (-Quad::INFINITY).next_down()); + let expected = -Quad::INFINITY; + assert_eq!(status, Status::OK); + assert!(test.is_infinite() && test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextUp(largest) = +inf + let test = unpack!(status=, Quad::largest().next_up()); + let expected = Quad::INFINITY; + assert_eq!(status, Status::OK); + assert!(test.is_infinite() && !test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(largest) = -nextUp(-largest) + // = -(-largest + inc) + // = largest - inc. + let test = unpack!(status=, Quad::largest().next_down()); + let expected = "0x1.fffffffffffffffffffffffffffep+16383".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_infinite() && !test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextUp(-largest) = -largest + inc. + let test = unpack!(status=, (-Quad::largest()).next_up()); + let expected = "-0x1.fffffffffffffffffffffffffffep+16383".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(-largest) = -nextUp(largest) = -(inf) = -inf. + let test = unpack!(status=, (-Quad::largest()).next_down()); + let expected = -Quad::INFINITY; + assert_eq!(status, Status::OK); + assert!(test.is_infinite() && test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextUp(smallest) = smallest + inc. + let test = unpack!(status=, "0x0.0000000000000000000000000001p-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "0x0.0000000000000000000000000002p-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(smallest) = -nextUp(-smallest) = -(-0) = +0. + let test = unpack!(status=, "0x0.0000000000000000000000000001p-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = Quad::ZERO; + assert_eq!(status, Status::OK); + assert!(test.is_pos_zero()); + assert!(test.bitwise_eq(expected)); + + // nextUp(-smallest) = -0. + let test = unpack!(status=, "-0x0.0000000000000000000000000001p-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = -Quad::ZERO; + assert_eq!(status, Status::OK); + assert!(test.is_neg_zero()); + assert!(test.bitwise_eq(expected)); + + // nextDown(-smallest) = -nextUp(smallest) = -smallest - inc. + let test = unpack!(status=, "-0x0.0000000000000000000000000001p-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "-0x0.0000000000000000000000000002p-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextUp(qNaN) = qNaN + let test = unpack!(status=, Quad::qnan(None).next_up()); + let expected = Quad::qnan(None); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(qNaN) = qNaN + let test = unpack!(status=, Quad::qnan(None).next_down()); + let expected = Quad::qnan(None); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextUp(sNaN) = qNaN + let test = unpack!(status=, Quad::snan(None).next_up()); + let expected = Quad::qnan(None); + assert_eq!(status, Status::INVALID_OP); + assert!(test.bitwise_eq(expected)); + + // nextDown(sNaN) = qNaN + let test = unpack!(status=, Quad::snan(None).next_down()); + let expected = Quad::qnan(None); + assert_eq!(status, Status::INVALID_OP); + assert!(test.bitwise_eq(expected)); + + // nextUp(+0) = +smallest + let test = unpack!(status=, Quad::ZERO.next_up()); + let expected = Quad::SMALLEST; + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(+0) = -nextUp(-0) = -smallest + let test = unpack!(status=, Quad::ZERO.next_down()); + let expected = -Quad::SMALLEST; + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextUp(-0) = +smallest + let test = unpack!(status=, (-Quad::ZERO).next_up()); + let expected = Quad::SMALLEST; + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(-0) = -nextUp(0) = -smallest + let test = unpack!(status=, (-Quad::ZERO).next_down()); + let expected = -Quad::SMALLEST; + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // 2. Binade Boundary Tests. + + // 2a. Test denormal <-> normal binade boundaries. + // * nextUp(+Largest Denormal) -> +Smallest Normal. + // * nextDown(-Largest Denormal) -> -Smallest Normal. + // * nextUp(-Smallest Normal) -> -Largest Denormal. + // * nextDown(+Smallest Normal) -> +Largest Denormal. + + // nextUp(+Largest Denormal) -> +Smallest Normal. + let test = unpack!(status=, "0x0.ffffffffffffffffffffffffffffp-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "0x1.0000000000000000000000000000p-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_denormal()); + assert!(test.bitwise_eq(expected)); + + // nextDown(-Largest Denormal) -> -Smallest Normal. + let test = unpack!(status=, "-0x0.ffffffffffffffffffffffffffffp-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "-0x1.0000000000000000000000000000p-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_denormal()); + assert!(test.bitwise_eq(expected)); + + // nextUp(-Smallest Normal) -> -Largest Denormal. + let test = unpack!(status=, "-0x1.0000000000000000000000000000p-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "-0x0.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.is_denormal()); + assert!(test.bitwise_eq(expected)); + + // nextDown(+Smallest Normal) -> +Largest Denormal. + let test = unpack!(status=, "+0x1.0000000000000000000000000000p-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "+0x0.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.is_denormal()); + assert!(test.bitwise_eq(expected)); + + // 2b. Test normal <-> normal binade boundaries. + // * nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1. + // * nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1. + // * nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary. + // * nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary. + + // nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1. + let test = unpack!(status=, "-0x1p+1".parse::<Quad>().unwrap().next_up()); + let expected = "-0x1.ffffffffffffffffffffffffffffp+0".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1. + let test = unpack!(status=, "0x1p+1".parse::<Quad>().unwrap().next_down()); + let expected = "0x1.ffffffffffffffffffffffffffffp+0".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary. + let test = unpack!(status=, "0x1.ffffffffffffffffffffffffffffp+0" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "0x1p+1".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary. + let test = unpack!(status=, "-0x1.ffffffffffffffffffffffffffffp+0" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "-0x1p+1".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // 2c. Test using next at binade boundaries with a direction away from the + // binade boundary. Away from denormal <-> normal boundaries. + // + // This is to make sure that even though we are at a binade boundary, since + // we are rounding away, we do not trigger the binade boundary code. Thus we + // test: + // * nextUp(-Largest Denormal) -> -Largest Denormal + inc. + // * nextDown(+Largest Denormal) -> +Largest Denormal - inc. + // * nextUp(+Smallest Normal) -> +Smallest Normal + inc. + // * nextDown(-Smallest Normal) -> -Smallest Normal - inc. + + // nextUp(-Largest Denormal) -> -Largest Denormal + inc. + let test = unpack!(status=, "-0x0.ffffffffffffffffffffffffffffp-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "-0x0.fffffffffffffffffffffffffffep-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.is_denormal()); + assert!(test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(+Largest Denormal) -> +Largest Denormal - inc. + let test = unpack!(status=, "0x0.ffffffffffffffffffffffffffffp-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "0x0.fffffffffffffffffffffffffffep-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.is_denormal()); + assert!(!test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextUp(+Smallest Normal) -> +Smallest Normal + inc. + let test = unpack!(status=, "0x1.0000000000000000000000000000p-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "0x1.0000000000000000000000000001p-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_denormal()); + assert!(!test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(-Smallest Normal) -> -Smallest Normal - inc. + let test = unpack!(status=, "-0x1.0000000000000000000000000000p-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "-0x1.0000000000000000000000000001p-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_denormal()); + assert!(test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // 2d. Test values which cause our exponent to go to min exponent. This + // is to ensure that guards in the code to check for min exponent + // trigger properly. + // * nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 + // * nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) -> + // -0x1p-16381 + // * nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16382 + // * nextDown(0x1p-16382) -> 0x1.ffffffffffffffffffffffffffffp-16382 + + // nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 + let test = unpack!(status=, "-0x1p-16381".parse::<Quad>().unwrap().next_up()); + let expected = "-0x1.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) -> + // -0x1p-16381 + let test = unpack!(status=, "-0x1.ffffffffffffffffffffffffffffp-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "-0x1p-16381".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16381 + let test = unpack!(status=, "0x1.ffffffffffffffffffffffffffffp-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "0x1p-16381".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // nextDown(0x1p-16381) -> 0x1.ffffffffffffffffffffffffffffp-16382 + let test = unpack!(status=, "0x1p-16381".parse::<Quad>().unwrap().next_down()); + let expected = "0x1.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(expected)); + + // 3. Now we test both denormal/normal computation which will not cause us + // to go across binade boundaries. Specifically we test: + // * nextUp(+Denormal) -> +Denormal. + // * nextDown(+Denormal) -> +Denormal. + // * nextUp(-Denormal) -> -Denormal. + // * nextDown(-Denormal) -> -Denormal. + // * nextUp(+Normal) -> +Normal. + // * nextDown(+Normal) -> +Normal. + // * nextUp(-Normal) -> -Normal. + // * nextDown(-Normal) -> -Normal. + + // nextUp(+Denormal) -> +Denormal. + let test = unpack!(status=, "0x0.ffffffffffffffffffffffff000cp-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "0x0.ffffffffffffffffffffffff000dp-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.is_denormal()); + assert!(!test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(+Denormal) -> +Denormal. + let test = unpack!(status=, "0x0.ffffffffffffffffffffffff000cp-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "0x0.ffffffffffffffffffffffff000bp-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.is_denormal()); + assert!(!test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextUp(-Denormal) -> -Denormal. + let test = unpack!(status=, "-0x0.ffffffffffffffffffffffff000cp-16382" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "-0x0.ffffffffffffffffffffffff000bp-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.is_denormal()); + assert!(test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(-Denormal) -> -Denormal + let test = unpack!(status=, "-0x0.ffffffffffffffffffffffff000cp-16382" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "-0x0.ffffffffffffffffffffffff000dp-16382".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(test.is_denormal()); + assert!(test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextUp(+Normal) -> +Normal. + let test = unpack!(status=, "0x1.ffffffffffffffffffffffff000cp-16000" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "0x1.ffffffffffffffffffffffff000dp-16000".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_denormal()); + assert!(!test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(+Normal) -> +Normal. + let test = unpack!(status=, "0x1.ffffffffffffffffffffffff000cp-16000" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "0x1.ffffffffffffffffffffffff000bp-16000".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_denormal()); + assert!(!test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextUp(-Normal) -> -Normal. + let test = unpack!(status=, "-0x1.ffffffffffffffffffffffff000cp-16000" + .parse::<Quad>() + .unwrap() + .next_up()); + let expected = "-0x1.ffffffffffffffffffffffff000bp-16000".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_denormal()); + assert!(test.is_negative()); + assert!(test.bitwise_eq(expected)); + + // nextDown(-Normal) -> -Normal. + let test = unpack!(status=, "-0x1.ffffffffffffffffffffffff000cp-16000" + .parse::<Quad>() + .unwrap() + .next_down()); + let expected = "-0x1.ffffffffffffffffffffffff000dp-16000".parse::<Quad>().unwrap(); + assert_eq!(status, Status::OK); + assert!(!test.is_denormal()); + assert!(test.is_negative()); + assert!(test.bitwise_eq(expected)); +} + +#[test] +fn fma() { + { + let mut f1 = Single::from_f32(14.5); + let f2 = Single::from_f32(-14.5); + let f3 = Single::from_f32(225.0); + f1 = f1.mul_add(f2, f3).value; + assert_eq!(14.75, f1.to_f32()); + } + + { + let val2 = Single::from_f32(2.0); + let mut f1 = Single::from_f32(1.17549435e-38); + let mut f2 = Single::from_f32(1.17549435e-38); + f1 /= val2; + f2 /= val2; + let f3 = Single::from_f32(12.0); + f1 = f1.mul_add(f2, f3).value; + assert_eq!(12.0, f1.to_f32()); + } + + // Test for correct zero sign when answer is exactly zero. + // fma(1.0, -1.0, 1.0) -> +ve 0. + { + let mut f1 = Double::from_f64(1.0); + let f2 = Double::from_f64(-1.0); + let f3 = Double::from_f64(1.0); + f1 = f1.mul_add(f2, f3).value; + assert!(!f1.is_negative() && f1.is_zero()); + } + + // Test for correct zero sign when answer is exactly zero and rounding towards + // negative. + // fma(1.0, -1.0, 1.0) -> +ve 0. + { + let mut f1 = Double::from_f64(1.0); + let f2 = Double::from_f64(-1.0); + let f3 = Double::from_f64(1.0); + f1 = f1.mul_add_r(f2, f3, Round::TowardNegative).value; + assert!(f1.is_negative() && f1.is_zero()); + } + + // Test for correct (in this case -ve) sign when adding like signed zeros. + // Test fma(0.0, -0.0, -0.0) -> -ve 0. + { + let mut f1 = Double::from_f64(0.0); + let f2 = Double::from_f64(-0.0); + let f3 = Double::from_f64(-0.0); + f1 = f1.mul_add(f2, f3).value; + assert!(f1.is_negative() && f1.is_zero()); + } + + // Test -ve sign preservation when small negative results underflow. + { + let mut f1 = "-0x1p-1074".parse::<Double>().unwrap(); + let f2 = "+0x1p-1074".parse::<Double>().unwrap(); + let f3 = Double::from_f64(0.0); + f1 = f1.mul_add(f2, f3).value; + assert!(f1.is_negative() && f1.is_zero()); + } + + // Test x87 extended precision case from http://llvm.org/PR20728. + { + let mut m1 = X87DoubleExtended::from_u128(1).value; + let m2 = X87DoubleExtended::from_u128(1).value; + let a = X87DoubleExtended::from_u128(3).value; + + let mut loses_info = false; + m1 = m1.mul_add(m2, a).value; + let r: Single = m1.convert(&mut loses_info).value; + assert!(!loses_info); + assert_eq!(4.0, r.to_f32()); + } + + // Regression test that failed an assertion. + { + let mut f1 = Single::from_f32(-8.85242279E-41); + let f2 = Single::from_f32(2.0); + let f3 = Single::from_f32(8.85242279E-41); + f1 = f1.mul_add(f2, f3).value; + assert_eq!(-8.85242279E-41, f1.to_f32()); + } + + // Test using only a single instance of APFloat. + { + let mut f = Double::from_f64(1.5); + + f = f.mul_add(f, f).value; + assert_eq!(3.75, f.to_f64()); + } +} + +#[test] +fn min_num() { + let f1 = Double::from_f64(1.0); + let f2 = Double::from_f64(2.0); + let nan = Double::NAN; + + assert_eq!(1.0, f1.min(f2).to_f64()); + assert_eq!(1.0, f2.min(f1).to_f64()); + assert_eq!(1.0, f1.min(nan).to_f64()); + assert_eq!(1.0, nan.min(f1).to_f64()); +} + +#[test] +fn max_num() { + let f1 = Double::from_f64(1.0); + let f2 = Double::from_f64(2.0); + let nan = Double::NAN; + + assert_eq!(2.0, f1.max(f2).to_f64()); + assert_eq!(2.0, f2.max(f1).to_f64()); + assert_eq!(1.0, f1.max(nan).to_f64()); + assert_eq!(1.0, nan.max(f1).to_f64()); +} + +#[test] +fn minimum() { + let f1 = Double::from_f64(1.0); + let f2 = Double::from_f64(2.0); + let zp = Double::from_f64(0.0); + let zn = Double::from_f64(-0.0); + let nan = Double::NAN; + + assert_eq!(1.0, f1.minimum(f2).to_f64()); + assert_eq!(1.0, f2.minimum(f1).to_f64()); + assert_eq!(-0.0, zp.minimum(zn).to_f64()); + assert_eq!(-0.0, zn.minimum(zp).to_f64()); + assert!(f1.minimum(nan).to_f64().is_nan()); + assert!(nan.minimum(f1).to_f64().is_nan()); +} + +#[test] +fn maximum() { + let f1 = Double::from_f64(1.0); + let f2 = Double::from_f64(2.0); + let zp = Double::from_f64(0.0); + let zn = Double::from_f64(-0.0); + let nan = Double::NAN; + + assert_eq!(2.0, f1.maximum(f2).to_f64()); + assert_eq!(2.0, f2.maximum(f1).to_f64()); + assert_eq!(0.0, zp.maximum(zn).to_f64()); + assert_eq!(0.0, zn.maximum(zp).to_f64()); + assert!(f1.maximum(nan).to_f64().is_nan()); + assert!(nan.maximum(f1).to_f64().is_nan()); +} + +#[test] +fn denormal() { + // Test single precision + { + assert!(!Single::from_u128(0).value.is_denormal()); + + let mut t = "1.17549435082228750797e-38".parse::<Single>().unwrap(); + assert!(!t.is_denormal()); + + t /= Single::from_u128(2).value; + assert!(t.is_denormal()); + } + + // Test double precision + { + assert!(!Double::from_u128(0).value.is_denormal()); + + let mut t = "2.22507385850720138309e-308".parse::<Double>().unwrap(); + assert!(!t.is_denormal()); + + t /= Double::from_u128(2).value; + assert!(t.is_denormal()); + } + + // Test Intel double-ext + { + assert!(!X87DoubleExtended::from_u128(0).value.is_denormal()); + + let mut t = "3.36210314311209350626e-4932".parse::<X87DoubleExtended>().unwrap(); + assert!(!t.is_denormal()); + + t /= X87DoubleExtended::from_u128(2).value; + assert!(t.is_denormal()); + } + + // Test quadruple precision + { + assert!(!Quad::from_u128(0).value.is_denormal()); + + let mut t = "3.36210314311209350626267781732175260e-4932".parse::<Quad>().unwrap(); + assert!(!t.is_denormal()); + + t /= Quad::from_u128(2).value; + assert!(t.is_denormal()); + } +} + +#[test] +fn is_smallest_normalized() { + for_each_float_type!(for<F: Float> test::<F>()); + fn test<F: Float>() { + assert!(!F::ZERO.is_smallest_normalized()); + assert!(!(-F::ZERO).is_smallest_normalized()); + + assert!(!F::INFINITY.is_smallest_normalized()); + assert!(!(-F::INFINITY).is_smallest_normalized()); + + assert!(!F::qnan(None).is_smallest_normalized()); + assert!(!F::snan(None).is_smallest_normalized()); + + assert!(!F::largest().is_smallest_normalized()); + assert!(!(-F::largest()).is_smallest_normalized()); + + assert!(!F::SMALLEST.is_smallest_normalized()); + assert!(!(-F::SMALLEST).is_smallest_normalized()); + + assert!(!F::from_bits(!0u128 >> (128 - F::BITS)).is_smallest_normalized()); + + let pos_smallest_normalized = F::smallest_normalized(); + let neg_smallest_normalized = -F::smallest_normalized(); + assert!(pos_smallest_normalized.is_smallest_normalized()); + assert!(neg_smallest_normalized.is_smallest_normalized()); + + for mut val in [pos_smallest_normalized, neg_smallest_normalized] { + let old_sign = val.is_negative(); + + let mut status; + + // Step down, make sure it's still not smallest normalized. + val = unpack!(status=, val.next_down()); + assert_eq!(Status::OK, status); + assert_eq!(old_sign, val.is_negative()); + assert!(!val.is_smallest_normalized()); + assert_eq!(old_sign, val.is_negative()); + + // Step back up should restore it to being smallest normalized. + val = unpack!(status=, val.next_up()); + assert_eq!(Status::OK, status); + assert!(val.is_smallest_normalized()); + assert_eq!(old_sign, val.is_negative()); + + // Step beyond should no longer smallest normalized. + val = unpack!(status=, val.next_up()); + assert_eq!(Status::OK, status); + assert!(!val.is_smallest_normalized()); + assert_eq!(old_sign, val.is_negative()); + } + } +} + +#[test] +fn decimal_strings_without_null_terminators() { + // Make sure that we can parse strings without null terminators. + // rdar://14323230. + assert_eq!("0.00"[..3].parse::<Double>().unwrap().to_f64(), 0.0); + assert_eq!("0.01"[..3].parse::<Double>().unwrap().to_f64(), 0.0); + assert_eq!("0.09"[..3].parse::<Double>().unwrap().to_f64(), 0.0); + assert_eq!("0.095"[..4].parse::<Double>().unwrap().to_f64(), 0.09); + assert_eq!("0.00e+3"[..7].parse::<Double>().unwrap().to_f64(), 0.00); + assert_eq!("0e+3"[..4].parse::<Double>().unwrap().to_f64(), 0.00); +} + +#[test] +fn from_zero_decimal_string() { + assert_eq!(0.0, "0".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0.".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0.".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0.".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, ".0".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+.0".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-.0".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0.0".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0.0".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0.0".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "00000.".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+00000.".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-00000.".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, ".00000".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+.00000".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-.00000".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0000.00000".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0000.00000".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0000.00000".parse::<Double>().unwrap().to_f64()); +} + +#[test] +fn from_zero_decimal_single_exponent_string() { + assert_eq!(0.0, "0e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0e1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0e+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0e-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0e-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0e-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0.e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0.e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0.e1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0.e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0.e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0.e+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0.e-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0.e-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0.e-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, ".0e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+.0e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-.0e1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, ".0e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+.0e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-.0e+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, ".0e-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+.0e-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-.0e-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0.0e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0.0e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0.0e1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0.0e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0.0e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0.0e+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0.0e-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0.0e-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0.0e-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "000.0000e1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+000.0000e+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-000.0000e+1".parse::<Double>().unwrap().to_f64()); +} + +#[test] +fn from_zero_decimal_large_exponent_string() { + assert_eq!(0.0, "0e1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0e1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0e1234".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0e+1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0e+1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0e+1234".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0e-1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0e-1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0e-1234".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "000.0000e1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "000.0000e-1234".parse::<Double>().unwrap().to_f64()); +} + +#[test] +fn from_zero_hexadecimal_string() { + assert_eq!(0.0, "0x0p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0p1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x0p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0p+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x0p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0p-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x0.p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0.p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0.p1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x0.p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0.p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0.p+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x0.p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0.p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0.p-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x.0p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x.0p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x.0p1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x.0p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x.0p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x.0p+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x.0p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x.0p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x.0p-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x0.0p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0.0p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0.0p1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x0.0p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0.0p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0.0p+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x0.0p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "+0x0.0p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0.0p-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.0, "0x00000.p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "0x0000.00000p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "0x.00000p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "0x0.p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "0x0p1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.0, "-0x0p1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "0x00000.p1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "0x0000.00000p1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "0x.00000p1234".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.0, "0x0.p1234".parse::<Double>().unwrap().to_f64()); +} + +#[test] +fn from_decimal_string() { + assert_eq!(1.0, "1".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.0, "2.".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.5, ".5".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "1.0".parse::<Double>().unwrap().to_f64()); + assert_eq!(-2.0, "-2".parse::<Double>().unwrap().to_f64()); + assert_eq!(-4.0, "-4.".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.5, "-.5".parse::<Double>().unwrap().to_f64()); + assert_eq!(-1.5, "-1.5".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.25e12, "1.25e12".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.25e+12, "1.25e+12".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.25e-12, "1.25e-12".parse::<Double>().unwrap().to_f64()); + assert_eq!(1024.0, "1024.".parse::<Double>().unwrap().to_f64()); + assert_eq!(1024.05, "1024.05000".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.05, ".05000".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.0, "2.".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.0e2, "2.e2".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.0e+2, "2.e+2".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.0e-2, "2.e-2".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.05e2, "002.05000e2".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.05e+2, "002.05000e+2".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.05e-2, "002.05000e-2".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.05e12, "002.05000e12".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.05e+12, "002.05000e+12".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.05e-12, "002.05000e-12".parse::<Double>().unwrap().to_f64()); + + assert_eq!(1.0, "1e".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "+1e".parse::<Double>().unwrap().to_f64()); + assert_eq!(-1.0, "-1e".parse::<Double>().unwrap().to_f64()); + + assert_eq!(1.0, "1.e".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "+1.e".parse::<Double>().unwrap().to_f64()); + assert_eq!(-1.0, "-1.e".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.1, ".1e".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.1, "+.1e".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.1, "-.1e".parse::<Double>().unwrap().to_f64()); + + assert_eq!(1.1, "1.1e".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.1, "+1.1e".parse::<Double>().unwrap().to_f64()); + assert_eq!(-1.1, "-1.1e".parse::<Double>().unwrap().to_f64()); + + assert_eq!(1.0, "1e+".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "1e-".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.1, ".1e".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.1, ".1e+".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.1, ".1e-".parse::<Double>().unwrap().to_f64()); + + assert_eq!(1.0, "1.0e".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "1.0e+".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "1.0e-".parse::<Double>().unwrap().to_f64()); + + // These are "carefully selected" to overflow the fast log-base + // calculations in the implementation. + assert!("99e99999".parse::<Double>().unwrap().is_infinite()); + assert!("-99e99999".parse::<Double>().unwrap().is_infinite()); + assert!("1e-99999".parse::<Double>().unwrap().is_pos_zero()); + assert!("-1e-99999".parse::<Double>().unwrap().is_neg_zero()); + + assert_eq!(2.71828, "2.71828".parse::<Double>().unwrap().to_f64()); +} + +#[test] +fn from_string_specials() { + let precision = 53; + let payload_bits = precision - 2; + let payload_mask = (1 << payload_bits) - 1; + + let mut nan_payloads = [ + 0, + 1, + 123, + 0xDEADBEEF, + -2i32 as u128, + 1 << payload_bits, // overflow bit + 1 << (payload_bits - 1), // signaling bit + 1 << (payload_bits - 2), // highest possible bit + ]; + + // Convert payload integer to decimal string representation. + let nan_payload_dec_strings: Vec<_> = nan_payloads.iter().map(|payload| format!("{payload}")).collect(); + + // Convert payload integer to hexadecimal string representation. + let nan_payload_hex_strings: Vec<_> = nan_payloads.iter().map(|payload| format!("{payload:#x}")).collect(); + + // Fix payloads to expected result. + for payload in &mut nan_payloads { + *payload &= payload_mask; + } + + // Signaling NaN must have a non-zero payload. In case a zero payload is + // requested, a default arbitrary payload is set instead. Save this payload + // for testing. + let snan_default_payload = Double::snan(None).to_bits() & payload_mask; + + // Negative sign prefix (or none - for positive). + let signs = ["", "-"]; + + // "Signaling" prefix (or none - for "Quiet"). + let nan_types = ["", "s", "S"]; + + let nan_strings = ["nan", "NaN"]; + for nan_str in nan_strings { + for type_str in nan_types { + let signaling = matches!(type_str, "s" | "S"); + + for j in 0..nan_payloads.len() { + let payload = if signaling && nan_payloads[j] == 0 { + snan_default_payload + } else { + nan_payloads[j] + }; + let payload_dec = &nan_payload_dec_strings[j]; + let payload_hex = &nan_payload_hex_strings[j]; + + for sign_str in signs { + let negative = sign_str == "-"; + + let prefix = format!("{sign_str}{type_str}{nan_str}"); + + let test_strings = [ + // Test without any paylod. + (payload == 0).then(|| prefix.clone()), + // Test with the payload as a suffix. + Some(format!("{prefix}{payload_dec}")), + Some(format!("{prefix}{payload_hex}")), + // Test with the payload inside parentheses. + Some(format!("{prefix}({payload_dec})")), + Some(format!("{prefix}({payload_hex})")), + ] + .into_iter() + .flatten(); + + for test_str in test_strings { + let f = test_str + .parse::<Double>() + .map_err(|e| format!("{test_str:?}: {e:?}")) + .unwrap(); + assert!(f.is_nan()); + assert_eq!(signaling, f.is_signaling()); + assert_eq!(negative, f.is_negative()); + assert_eq!(payload, f.to_bits() & payload_mask); + } + } + } + } + } + + let inf_strings = ["inf", "INFINITY", "+Inf", "-inf", "-INFINITY", "-Inf"]; + for &inf_str in &inf_strings { + let negative = inf_str.starts_with('-'); + + let f = inf_str.parse::<Double>().unwrap(); + assert!(f.is_infinite()); + assert_eq!(negative, f.is_negative()); + assert_eq!(0, f.to_bits() & payload_mask); + } +} + +#[test] +fn from_to_string_specials() { + assert_eq!("+Inf", "+Inf".parse::<Double>().unwrap().to_string()); + assert_eq!("+Inf", "INFINITY".parse::<Double>().unwrap().to_string()); + assert_eq!("+Inf", "inf".parse::<Double>().unwrap().to_string()); + assert_eq!("-Inf", "-Inf".parse::<Double>().unwrap().to_string()); + assert_eq!("-Inf", "-INFINITY".parse::<Double>().unwrap().to_string()); + assert_eq!("-Inf", "-inf".parse::<Double>().unwrap().to_string()); + assert_eq!("NaN", "NaN".parse::<Double>().unwrap().to_string()); + assert_eq!("NaN", "nan".parse::<Double>().unwrap().to_string()); + assert_eq!("NaN", "-NaN".parse::<Double>().unwrap().to_string()); + assert_eq!("NaN", "-nan".parse::<Double>().unwrap().to_string()); +} + +#[test] +fn from_hexadecimal_string() { + assert_eq!(1.0, "0x1p0".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "+0x1p0".parse::<Double>().unwrap().to_f64()); + assert_eq!(-1.0, "-0x1p0".parse::<Double>().unwrap().to_f64()); + + assert_eq!(1.0, "0x1p+0".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "+0x1p+0".parse::<Double>().unwrap().to_f64()); + assert_eq!(-1.0, "-0x1p+0".parse::<Double>().unwrap().to_f64()); + + assert_eq!(1.0, "0x1p-0".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "+0x1p-0".parse::<Double>().unwrap().to_f64()); + assert_eq!(-1.0, "-0x1p-0".parse::<Double>().unwrap().to_f64()); + + assert_eq!(2.0, "0x1p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.0, "+0x1p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-2.0, "-0x1p1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(2.0, "0x1p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(2.0, "+0x1p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-2.0, "-0x1p+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.5, "0x1p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.5, "+0x1p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.5, "-0x1p-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(3.0, "0x1.8p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(3.0, "+0x1.8p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-3.0, "-0x1.8p1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(3.0, "0x1.8p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(3.0, "+0x1.8p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-3.0, "-0x1.8p+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.75, "0x1.8p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.75, "+0x1.8p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.75, "-0x1.8p-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(8192.0, "0x1000.000p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(8192.0, "+0x1000.000p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-8192.0, "-0x1000.000p1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(8192.0, "0x1000.000p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(8192.0, "+0x1000.000p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-8192.0, "-0x1000.000p+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(2048.0, "0x1000.000p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(2048.0, "+0x1000.000p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-2048.0, "-0x1000.000p-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(8192.0, "0x1000p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(8192.0, "+0x1000p1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-8192.0, "-0x1000p1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(8192.0, "0x1000p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(8192.0, "+0x1000p+1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-8192.0, "-0x1000p+1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(2048.0, "0x1000p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(2048.0, "+0x1000p-1".parse::<Double>().unwrap().to_f64()); + assert_eq!(-2048.0, "-0x1000p-1".parse::<Double>().unwrap().to_f64()); + + assert_eq!(16384.0, "0x10p10".parse::<Double>().unwrap().to_f64()); + assert_eq!(16384.0, "+0x10p10".parse::<Double>().unwrap().to_f64()); + assert_eq!(-16384.0, "-0x10p10".parse::<Double>().unwrap().to_f64()); + + assert_eq!(16384.0, "0x10p+10".parse::<Double>().unwrap().to_f64()); + assert_eq!(16384.0, "+0x10p+10".parse::<Double>().unwrap().to_f64()); + assert_eq!(-16384.0, "-0x10p+10".parse::<Double>().unwrap().to_f64()); + + assert_eq!(0.015625, "0x10p-10".parse::<Double>().unwrap().to_f64()); + assert_eq!(0.015625, "+0x10p-10".parse::<Double>().unwrap().to_f64()); + assert_eq!(-0.015625, "-0x10p-10".parse::<Double>().unwrap().to_f64()); + + assert_eq!(1.0625, "0x1.1p0".parse::<Double>().unwrap().to_f64()); + assert_eq!(1.0, "0x1p0".parse::<Double>().unwrap().to_f64()); + + assert_eq!( + "0x1p-150".parse::<Double>().unwrap().to_f64(), + "+0x800000000000000001.p-221".parse::<Double>().unwrap().to_f64() + ); + assert_eq!(2251799813685248.5, "0x80000000000004000000.010p-28".parse::<Double>().unwrap().to_f64()); +} + +#[test] +fn to_string() { + let to_string = |d: f64, precision: usize, width: usize| { + let x = Double::from_f64(d); + if precision == 0 { + format!("{:1$}", x, width) + } else { + format!("{:2$.1$}", x, precision, width) + } + }; + assert_eq!("10", to_string(10.0, 6, 3)); + assert_eq!("1.0E+1", to_string(10.0, 6, 0)); + assert_eq!("10100", to_string(1.01E+4, 5, 2)); + assert_eq!("1.01E+4", to_string(1.01E+4, 4, 2)); + assert_eq!("1.01E+4", to_string(1.01E+4, 5, 1)); + assert_eq!("0.0101", to_string(1.01E-2, 5, 2)); + assert_eq!("0.0101", to_string(1.01E-2, 4, 2)); + assert_eq!("1.01E-2", to_string(1.01E-2, 5, 1)); + assert_eq!("0.78539816339744828", to_string(0.78539816339744830961, 0, 3)); + assert_eq!("4.9406564584124654E-324", to_string(4.9406564584124654e-324, 0, 3)); + assert_eq!("873.18340000000001", to_string(873.1834, 0, 1)); + assert_eq!("8.7318340000000001E+2", to_string(873.1834, 0, 0)); + assert_eq!("1.7976931348623157E+308", to_string(1.7976931348623157E+308, 0, 0)); + + let to_string = |d: f64, precision: usize, width: usize| { + let x = Double::from_f64(d); + if precision == 0 { + format!("{:#1$}", x, width) + } else { + format!("{:#2$.1$}", x, precision, width) + } + }; + assert_eq!("10", to_string(10.0, 6, 3)); + assert_eq!("1.000000e+01", to_string(10.0, 6, 0)); + assert_eq!("10100", to_string(1.01E+4, 5, 2)); + assert_eq!("1.0100e+04", to_string(1.01E+4, 4, 2)); + assert_eq!("1.01000e+04", to_string(1.01E+4, 5, 1)); + assert_eq!("0.0101", to_string(1.01E-2, 5, 2)); + assert_eq!("0.0101", to_string(1.01E-2, 4, 2)); + assert_eq!("1.01000e-02", to_string(1.01E-2, 5, 1)); + assert_eq!("0.78539816339744828", to_string(0.78539816339744830961, 0, 3)); + assert_eq!("4.94065645841246540e-324", to_string(4.9406564584124654e-324, 0, 3)); + assert_eq!("873.18340000000001", to_string(873.1834, 0, 1)); + assert_eq!("8.73183400000000010e+02", to_string(873.1834, 0, 0)); + assert_eq!("1.79769313486231570e+308", to_string(1.7976931348623157E+308, 0, 0)); + assert_eq!("NaN", X87DoubleExtended::from_bits(1 << 64).to_string()); +} + +#[test] +fn to_integer() { + let mut is_exact = false; + + assert_eq!( + Status::OK.and(10), + "10".parse::<Double>() + .unwrap() + .to_u128_r(5, Round::TowardZero, &mut is_exact,) + ); + assert!(is_exact); + + assert_eq!( + Status::INVALID_OP.and(0), + "-10" + .parse::<Double>() + .unwrap() + .to_u128_r(5, Round::TowardZero, &mut is_exact,) + ); + assert!(!is_exact); + + assert_eq!( + Status::INVALID_OP.and(31), + "32".parse::<Double>() + .unwrap() + .to_u128_r(5, Round::TowardZero, &mut is_exact,) + ); + assert!(!is_exact); + + assert_eq!( + Status::INEXACT.and(7), + "7.9" + .parse::<Double>() + .unwrap() + .to_u128_r(5, Round::TowardZero, &mut is_exact,) + ); + assert!(!is_exact); + + assert_eq!( + Status::OK.and(-10), + "-10" + .parse::<Double>() + .unwrap() + .to_i128_r(5, Round::TowardZero, &mut is_exact,) + ); + assert!(is_exact); + + assert_eq!( + Status::INVALID_OP.and(-16), + "-17" + .parse::<Double>() + .unwrap() + .to_i128_r(5, Round::TowardZero, &mut is_exact,) + ); + assert!(!is_exact); + + assert_eq!( + Status::INVALID_OP.and(15), + "16".parse::<Double>() + .unwrap() + .to_i128_r(5, Round::TowardZero, &mut is_exact,) + ); + assert!(!is_exact); +} + +#[test] +fn nan() { + fn nanbits_from_u128<F: Float>(signaling: bool, negative: bool, payload: u128) -> u128 { + let x = if signaling { + F::snan(Some(payload)) + } else { + F::qnan(Some(payload)) + }; + if negative { + (-x).to_bits() + } else { + x.to_bits() + } + } + + let tests_single = [ + // expected SNaN Neg payload + (0x7fc00000, false, false, 0x00000000), + (0xffc00000, false, true, 0x00000000), + (0x7fc0ae72, false, false, 0x0000ae72), + (0x7fffae72, false, false, 0xffffae72), + (0x7fdaae72, false, false, 0x00daae72), + (0x7fa00000, true, false, 0x00000000), + (0xffa00000, true, true, 0x00000000), + (0x7f80ae72, true, false, 0x0000ae72), + (0x7fbfae72, true, false, 0xffffae72), + (0x7f9aae72, true, false, 0x001aae72), + ]; + let tests_double = [ + // expected SNaN Neg payload + (0x7ff8000000000000, false, false, 0x0000000000000000), + (0xfff8000000000000, false, true, 0x0000000000000000), + (0x7ff800000000ae72, false, false, 0x000000000000ae72), + (0x7fffffffffffae72, false, false, 0xffffffffffffae72), + (0x7ffdaaaaaaaaae72, false, false, 0x000daaaaaaaaae72), + (0x7ff4000000000000, true, false, 0x0000000000000000), + (0xfff4000000000000, true, true, 0x0000000000000000), + (0x7ff000000000ae72, true, false, 0x000000000000ae72), + (0x7ff7ffffffffae72, true, false, 0xffffffffffffae72), + (0x7ff1aaaaaaaaae72, true, false, 0x0001aaaaaaaaae72), + ]; + for (expected, signaling, negative, payload) in tests_single { + assert_eq!(expected, nanbits_from_u128::<Single>(signaling, negative, payload)); + } + for (expected, signaling, negative, payload) in tests_double { + assert_eq!(expected, nanbits_from_u128::<Double>(signaling, negative, payload)); + } +} + +#[test] +fn string_decimal_error() { + assert_eq!("Invalid string length", "".parse::<Double>().unwrap_err().0); + assert_eq!("String has no digits", "+".parse::<Double>().unwrap_err().0); + assert_eq!("String has no digits", "-".parse::<Double>().unwrap_err().0); + + assert_eq!("Invalid character in significand", "\0".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in significand", "1\0".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in significand", "1\02".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in significand", "1\02e1".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in exponent", "1e\0".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in exponent", "1e1\0".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in exponent", "1e1\02".parse::<Double>().unwrap_err().0); + + assert_eq!("Invalid character in significand", "1.0f".parse::<Double>().unwrap_err().0); + + assert_eq!("String contains multiple dots", "..".parse::<Double>().unwrap_err().0); + assert_eq!("String contains multiple dots", "..0".parse::<Double>().unwrap_err().0); + assert_eq!("String contains multiple dots", "1.0.0".parse::<Double>().unwrap_err().0); +} + +#[test] +fn string_decimal_significand_error() { + assert_eq!("Significand has no digits", ".".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+.".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-.".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", "e".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+e".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-e".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", "e1".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+e1".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-e1".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", ".e1".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+.e1".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-.e1".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", ".e".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+.e".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-.e".parse::<Double>().unwrap_err().0); +} + +#[test] +fn string_hexadecimal_error() { + assert_eq!("Invalid string", "0x".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid string", "+0x".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid string", "-0x".parse::<Double>().unwrap_err().0); + + assert_eq!("Hex strings require an exponent", "0x0".parse::<Double>().unwrap_err().0); + assert_eq!("Hex strings require an exponent", "+0x0".parse::<Double>().unwrap_err().0); + assert_eq!("Hex strings require an exponent", "-0x0".parse::<Double>().unwrap_err().0); + + assert_eq!("Hex strings require an exponent", "0x0.".parse::<Double>().unwrap_err().0); + assert_eq!("Hex strings require an exponent", "+0x0.".parse::<Double>().unwrap_err().0); + assert_eq!("Hex strings require an exponent", "-0x0.".parse::<Double>().unwrap_err().0); + + assert_eq!("Hex strings require an exponent", "0x.0".parse::<Double>().unwrap_err().0); + assert_eq!("Hex strings require an exponent", "+0x.0".parse::<Double>().unwrap_err().0); + assert_eq!("Hex strings require an exponent", "-0x.0".parse::<Double>().unwrap_err().0); + + assert_eq!("Hex strings require an exponent", "0x0.0".parse::<Double>().unwrap_err().0); + assert_eq!("Hex strings require an exponent", "+0x0.0".parse::<Double>().unwrap_err().0); + assert_eq!("Hex strings require an exponent", "-0x0.0".parse::<Double>().unwrap_err().0); + + assert_eq!("Invalid character in significand", "0x\0".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in significand", "0x1\0".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in significand", "0x1\02".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in significand", "0x1\02p1".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in exponent", "0x1p\0".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in exponent", "0x1p1\0".parse::<Double>().unwrap_err().0); + assert_eq!("Invalid character in exponent", "0x1p1\02".parse::<Double>().unwrap_err().0); + + assert_eq!("Invalid character in exponent", "0x1p0f".parse::<Double>().unwrap_err().0); + + assert_eq!("String contains multiple dots", "0x..p1".parse::<Double>().unwrap_err().0); + assert_eq!("String contains multiple dots", "0x..0p1".parse::<Double>().unwrap_err().0); + assert_eq!("String contains multiple dots", "0x1.0.0p1".parse::<Double>().unwrap_err().0); +} + +#[test] +fn string_hexadecimal_significand_error() { + assert_eq!("Significand has no digits", "0x.".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+0x.".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-0x.".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", "0xp".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+0xp".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-0xp".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", "0xp+".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+0xp+".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-0xp+".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", "0xp-".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+0xp-".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-0xp-".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", "0x.p".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+0x.p".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-0x.p".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", "0x.p+".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+0x.p+".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-0x.p+".parse::<Double>().unwrap_err().0); + + assert_eq!("Significand has no digits", "0x.p-".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "+0x.p-".parse::<Double>().unwrap_err().0); + assert_eq!("Significand has no digits", "-0x.p-".parse::<Double>().unwrap_err().0); +} + +#[test] +fn string_hexadecimal_exponent_error() { + assert_eq!("Exponent has no digits", "0x1p".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1p".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1p".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x1p+".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1p+".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1p+".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x1p-".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1p-".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1p-".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x1.p".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1.p".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1.p".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x1.p+".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1.p+".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1.p+".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x1.p-".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1.p-".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1.p-".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x.1p".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x.1p".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x.1p".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x.1p+".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x.1p+".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x.1p+".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x.1p-".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x.1p-".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x.1p-".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x1.1p".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1.1p".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1.1p".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x1.1p+".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1.1p+".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1.1p+".parse::<Double>().unwrap_err().0); + + assert_eq!("Exponent has no digits", "0x1.1p-".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "+0x1.1p-".parse::<Double>().unwrap_err().0); + assert_eq!("Exponent has no digits", "-0x1.1p-".parse::<Double>().unwrap_err().0); +} + +#[test] +fn exact_inverse() { + // Trivial operation. + assert!(Double::from_f64(2.0) + .get_exact_inverse() + .unwrap() + .bitwise_eq(Double::from_f64(0.5))); + assert!(Single::from_f32(2.0) + .get_exact_inverse() + .unwrap() + .bitwise_eq(Single::from_f32(0.5))); + assert!("2.0" + .parse::<Quad>() + .unwrap() + .get_exact_inverse() + .unwrap() + .bitwise_eq("0.5".parse::<Quad>().unwrap())); + assert!("2.0" + .parse::<X87DoubleExtended>() + .unwrap() + .get_exact_inverse() + .unwrap() + .bitwise_eq("0.5".parse::<X87DoubleExtended>().unwrap())); + + // FLT_MIN + assert!(Single::from_f32(1.17549435e-38) + .get_exact_inverse() + .unwrap() + .bitwise_eq(Single::from_f32(8.5070592e+37))); + + // Large float, inverse is a denormal. + assert!(Single::from_f32(1.7014118e38).get_exact_inverse().is_none()); + // Zero + assert!(Double::from_f64(0.0).get_exact_inverse().is_none()); + // Denormalized float + assert!(Single::from_f32(1.40129846e-45).get_exact_inverse().is_none()); +} + +#[test] +fn round_to_integral() { + let t = Double::from_f64(-0.5); + assert_eq!(-0.0, t.round_to_integral(Round::TowardZero).value.to_f64()); + assert_eq!(-1.0, t.round_to_integral(Round::TowardNegative).value.to_f64()); + assert_eq!(-0.0, t.round_to_integral(Round::TowardPositive).value.to_f64()); + assert_eq!(-0.0, t.round_to_integral(Round::NearestTiesToEven).value.to_f64()); + + let s = Double::from_f64(3.14); + assert_eq!(3.0, s.round_to_integral(Round::TowardZero).value.to_f64()); + assert_eq!(3.0, s.round_to_integral(Round::TowardNegative).value.to_f64()); + assert_eq!(4.0, s.round_to_integral(Round::TowardPositive).value.to_f64()); + assert_eq!(3.0, s.round_to_integral(Round::NearestTiesToEven).value.to_f64()); + + let r = Double::largest(); + assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardZero).value.to_f64()); + assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardNegative).value.to_f64()); + assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardPositive).value.to_f64()); + assert_eq!(r.to_f64(), r.round_to_integral(Round::NearestTiesToEven).value.to_f64()); + + let p = Double::ZERO.round_to_integral(Round::TowardZero).value; + assert_eq!(0.0, p.to_f64()); + let p = (-Double::ZERO).round_to_integral(Round::TowardZero).value; + assert_eq!(-0.0, p.to_f64()); + let p = Double::NAN.round_to_integral(Round::TowardZero).value; + assert!(p.to_f64().is_nan()); + let p = Double::INFINITY.round_to_integral(Round::TowardZero).value; + assert!(p.to_f64().is_infinite() && p.to_f64() > 0.0); + let p = (-Double::INFINITY).round_to_integral(Round::TowardZero).value; + assert!(p.to_f64().is_infinite() && p.to_f64() < 0.0); + + let mut status; + + let p = unpack!(status=, Double::NAN.round_to_integral(Round::TowardZero)); + assert!(p.is_nan()); + assert!(!p.is_negative()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, (-Double::NAN).round_to_integral(Round::TowardZero)); + assert!(p.is_nan()); + assert!(p.is_negative()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, Double::snan(None).round_to_integral(Round::TowardZero)); + assert!(p.is_nan()); + assert!(!p.is_signaling()); + assert!(!p.is_negative()); + assert_eq!(Status::INVALID_OP, status); + + let p = unpack!(status=, (-Double::snan(None)).round_to_integral(Round::TowardZero)); + assert!(p.is_nan()); + assert!(!p.is_signaling()); + assert!(p.is_negative()); + assert_eq!(Status::INVALID_OP, status); + + let p = unpack!(status=, Double::INFINITY.round_to_integral(Round::TowardZero)); + assert!(p.is_infinite()); + assert!(!p.is_negative()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, (-Double::INFINITY).round_to_integral(Round::TowardZero)); + assert!(p.is_infinite()); + assert!(p.is_negative()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, Double::ZERO.round_to_integral(Round::TowardZero)); + assert!(p.is_zero()); + assert!(!p.is_negative()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, Double::ZERO.round_to_integral(Round::TowardNegative)); + assert!(p.is_zero()); + assert!(!p.is_negative()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, (-Double::ZERO).round_to_integral(Round::TowardZero)); + assert!(p.is_zero()); + assert!(p.is_negative()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, (-Double::ZERO).round_to_integral(Round::TowardNegative)); + assert!(p.is_zero()); + assert!(p.is_negative()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, Double::from_f64(1E-100).round_to_integral(Round::TowardNegative)); + assert!(p.is_zero()); + assert!(!p.is_negative()); + assert_eq!(Status::INEXACT, status); + + let p = unpack!(status=, Double::from_f64(1E-100).round_to_integral(Round::TowardPositive)); + assert_eq!(1.0, p.to_f64()); + assert!(!p.is_negative()); + assert_eq!(Status::INEXACT, status); + + let p = unpack!(status=, Double::from_f64(-1E-100).round_to_integral(Round::TowardNegative)); + assert!(p.is_negative()); + assert_eq!(-1.0, p.to_f64()); + assert_eq!(Status::INEXACT, status); + + let p = unpack!(status=, Double::from_f64(-1E-100).round_to_integral(Round::TowardPositive)); + assert!(p.is_zero()); + assert!(p.is_negative()); + assert_eq!(Status::INEXACT, status); + + let p = unpack!(status=, Double::from_f64(10.0).round_to_integral(Round::TowardZero)); + assert_eq!(10.0, p.to_f64()); + assert_eq!(Status::OK, status); + + let p = unpack!(status=, Double::from_f64(10.5).round_to_integral(Round::TowardZero)); + assert_eq!(10.0, p.to_f64()); + assert_eq!(Status::INEXACT, status); + + let p = unpack!(status=, Double::from_f64(10.5).round_to_integral(Round::TowardPositive)); + assert_eq!(11.0, p.to_f64()); + assert_eq!(Status::INEXACT, status); + + let p = unpack!(status=, Double::from_f64(10.5).round_to_integral(Round::TowardNegative)); + assert_eq!(10.0, p.to_f64()); + assert_eq!(Status::INEXACT, status); + + let p = unpack!(status=, Double::from_f64(10.5).round_to_integral(Round::NearestTiesToAway)); + assert_eq!(11.0, p.to_f64()); + assert_eq!(Status::INEXACT, status); + + let p = unpack!(status=, Double::from_f64(10.5).round_to_integral(Round::NearestTiesToEven)); + assert_eq!(10.0, p.to_f64()); + assert_eq!(Status::INEXACT, status); +} + +#[test] +fn is_integer() { + let t = Double::from_f64(-0.0); + assert!(t.is_integer()); + let t = Double::from_f64(3.14159); + assert!(!t.is_integer()); + let t = Double::NAN; + assert!(!t.is_integer()); + let t = Double::INFINITY; + assert!(!t.is_integer()); + let t = -Double::INFINITY; + assert!(!t.is_integer()); + let t = Double::largest(); + assert!(t.is_integer()); +} + +#[test] +fn largest() { + assert_eq!(3.402823466e+38, Single::largest().to_f32()); + assert_eq!(1.7976931348623158e+308, Double::largest().to_f64()); + assert_eq!(448.0, Float8E4M3FN::largest().to_f64()); +} + +#[test] +fn smallest() { + let test = Single::SMALLEST; + let expected = "0x0.000002p-126".parse::<Single>().unwrap(); + assert!(!test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(test.is_denormal()); + assert!(test.bitwise_eq(expected)); + + let test = -Single::SMALLEST; + let expected = "-0x0.000002p-126".parse::<Single>().unwrap(); + assert!(test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(test.is_denormal()); + assert!(test.bitwise_eq(expected)); + + let test = Quad::SMALLEST; + let expected = "0x0.0000000000000000000000000001p-16382".parse::<Quad>().unwrap(); + assert!(!test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(test.is_denormal()); + assert!(test.bitwise_eq(expected)); + + let test = -Quad::SMALLEST; + let expected = "-0x0.0000000000000000000000000001p-16382".parse::<Quad>().unwrap(); + assert!(test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(test.is_denormal()); + assert!(test.bitwise_eq(expected)); +} + +#[test] +fn smallest_normalized() { + let test = Single::smallest_normalized(); + let expected = "0x1p-126".parse::<Single>().unwrap(); + assert!(!test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(!test.is_denormal()); + assert!(test.bitwise_eq(expected)); + assert!(test.is_smallest_normalized()); + + let test = -Single::smallest_normalized(); + let expected = "-0x1p-126".parse::<Single>().unwrap(); + assert!(test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(!test.is_denormal()); + assert!(test.bitwise_eq(expected)); + assert!(test.is_smallest_normalized()); + + let test = Double::smallest_normalized(); + let expected = "0x1p-1022".parse::<Double>().unwrap(); + assert!(!test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(!test.is_denormal()); + assert!(test.bitwise_eq(expected)); + assert!(test.is_smallest_normalized()); + + let test = -Double::smallest_normalized(); + let expected = "-0x1p-1022".parse::<Double>().unwrap(); + assert!(test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(!test.is_denormal()); + assert!(test.bitwise_eq(expected)); + assert!(test.is_smallest_normalized()); + + let test = Quad::smallest_normalized(); + let expected = "0x1p-16382".parse::<Quad>().unwrap(); + assert!(!test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(!test.is_denormal()); + assert!(test.bitwise_eq(expected)); + assert!(test.is_smallest_normalized()); + + let test = -Quad::smallest_normalized(); + let expected = "-0x1p-16382".parse::<Quad>().unwrap(); + assert!(test.is_negative()); + assert!(test.is_finite_non_zero()); + assert!(!test.is_denormal()); + assert!(test.bitwise_eq(expected)); + assert!(test.is_smallest_normalized()); +} + +#[test] +fn zero() { + assert_eq!(0.0, Single::from_f32(0.0).to_f32()); + assert_eq!(-0.0, Single::from_f32(-0.0).to_f32()); + assert!(Single::from_f32(-0.0).is_negative()); + + assert_eq!(0.0, Double::from_f64(0.0).to_f64()); + assert_eq!(-0.0, Double::from_f64(-0.0).to_f64()); + assert!(Double::from_f64(-0.0).is_negative()); + + fn test<F: Float>(sign: bool, bits: u128) { + let test = if sign { -F::ZERO } else { F::ZERO }; + let pattern = if sign { "-0x0p+0" } else { "0x0p+0" }; + let expected = pattern.parse::<F>().unwrap(); + assert!(test.is_zero()); + assert_eq!(sign, test.is_negative()); + assert!(test.bitwise_eq(expected)); + assert_eq!(bits, test.to_bits()); + } + test::<Half>(false, 0); + test::<Half>(true, 0x8000); + test::<Single>(false, 0); + test::<Single>(true, 0x80000000); + test::<Double>(false, 0); + test::<Double>(true, 0x8000000000000000); + test::<Quad>(false, 0); + test::<Quad>(true, 0x8000000000000000_0000000000000000); + test::<X87DoubleExtended>(false, 0); + test::<X87DoubleExtended>(true, 0x8000_0000000000000000); + test::<Float8E5M2>(false, 0); + test::<Float8E5M2>(true, 0x80); + test::<Float8E4M3FN>(false, 0); + test::<Float8E4M3FN>(true, 0x80); +} + +#[test] +fn copy_sign() { + assert!(Double::from_f64(-42.0).bitwise_eq(Double::from_f64(42.0).copy_sign(Double::from_f64(-1.0),),)); + assert!(Double::from_f64(42.0).bitwise_eq(Double::from_f64(-42.0).copy_sign(Double::from_f64(1.0),),)); + assert!(Double::from_f64(-42.0).bitwise_eq(Double::from_f64(-42.0).copy_sign(Double::from_f64(-1.0),),)); + assert!(Double::from_f64(42.0).bitwise_eq(Double::from_f64(42.0).copy_sign(Double::from_f64(1.0),),)); +} + +#[test] +fn convert() { + let mut loses_info = false; + let mut status; + + let test = "1.0".parse::<Double>().unwrap(); + let test: Single = test.convert(&mut loses_info).value; + assert_eq!(1.0, test.to_f32()); + assert!(!loses_info); + + let mut test = "0x1p-53".parse::<X87DoubleExtended>().unwrap(); + let one = "1.0".parse::<X87DoubleExtended>().unwrap(); + test += one; + let test: Double = test.convert(&mut loses_info).value; + assert_eq!(1.0, test.to_f64()); + assert!(loses_info); + + let mut test = "0x1p-53".parse::<Quad>().unwrap(); + let one = "1.0".parse::<Quad>().unwrap(); + test += one; + let test: Double = test.convert(&mut loses_info).value; + assert_eq!(1.0, test.to_f64()); + assert!(loses_info); + + let test = "0xf.fffffffp+28".parse::<X87DoubleExtended>().unwrap(); + let test: Double = test.convert(&mut loses_info).value; + assert_eq!(4294967295.0, test.to_f64()); + assert!(!loses_info); + + let test = Single::snan(None); + let test: X87DoubleExtended = unpack!(status=, test.convert(&mut loses_info)); + // Conversion quiets the SNAN, so now 2 bits of the 64-bit significand should be set. + assert!(test.bitwise_eq(X87DoubleExtended::qnan(Some(0x6000000000000000)))); + assert!(!loses_info); + assert_eq!(status, Status::INVALID_OP); + + let test = Single::qnan(None); + let x87_qnan = X87DoubleExtended::qnan(None); + let test: X87DoubleExtended = test.convert(&mut loses_info).value; + assert!(test.bitwise_eq(x87_qnan)); + assert!(!loses_info); + + // NOTE(eddyb) these were mistakenly noops upstream, here they're already + // fixed (by instead converting from `Double` to `X87DoubleExtended`), + // see also upstream issue https://github.com/llvm/llvm-project/issues/63842. + let test = Double::snan(None); + let test: X87DoubleExtended = test.convert(&mut loses_info).value; + // Conversion quiets the SNAN, so now 2 bits of the 64-bit significand should be set. + assert!(test.bitwise_eq(X87DoubleExtended::qnan(Some(0x6000000000000000)))); + assert!(!loses_info); + + let test = Double::qnan(None); + let test: X87DoubleExtended = test.convert(&mut loses_info).value; + assert!(test.bitwise_eq(x87_qnan)); + assert!(!loses_info); + + // The payload is lost in truncation, but we retain NaN by setting the quiet bit. + let test = Double::snan(Some(1)); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0x7fc00000, test.to_bits()); + assert!(loses_info); + assert_eq!(status, Status::INVALID_OP); + + // The payload is lost in truncation. QNaN remains QNaN. + let test = Double::qnan(Some(1)); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0x7fc00000, test.to_bits()); + assert!(loses_info); + assert_eq!(status, Status::OK); + + // Test that subnormals are handled correctly in double to float conversion + let test = "0x0.0000010000000p-1022".parse::<Double>().unwrap(); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0.0, test.to_f32()); + assert!(loses_info); + + let test = "0x0.0000010000001p-1022".parse::<Double>().unwrap(); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0.0, test.to_f32()); + assert!(loses_info); + + let test = "-0x0.0000010000001p-1022".parse::<Double>().unwrap(); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0.0, test.to_f32()); + assert!(loses_info); + + let test = "0x0.0000020000000p-1022".parse::<Double>().unwrap(); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0.0, test.to_f32()); + assert!(loses_info); + + let test = "0x0.0000020000001p-1022".parse::<Double>().unwrap(); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0.0, test.to_f32()); + assert!(loses_info); + + // Test subnormal conversion to bfloat + let test = "0x0.01p-126".parse::<Single>().unwrap(); + let test: BFloat = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0.0, test.to_f32()); + assert!(loses_info); + + let test = "0x0.02p-126".parse::<Single>().unwrap(); + let test: BFloat = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0x01, test.to_bits()); + assert!(!loses_info); + + let test = "0x0.01p-126".parse::<Single>().unwrap(); + let test: BFloat = unpack!(status=, test.convert_r(Round::NearestTiesToAway, &mut loses_info)); + assert_eq!(0x01, test.to_bits()); + assert!(loses_info); +} + +#[test] +fn is_negative() { + let t = "0x1p+0".parse::<Single>().unwrap(); + assert!(!t.is_negative()); + let t = "-0x1p+0".parse::<Single>().unwrap(); + assert!(t.is_negative()); + + assert!(!Single::INFINITY.is_negative()); + assert!((-Single::INFINITY).is_negative()); + + assert!(!Single::ZERO.is_negative()); + assert!((-Single::ZERO).is_negative()); + + assert!(!Single::NAN.is_negative()); + assert!((-Single::NAN).is_negative()); + + assert!(!Single::snan(None).is_negative()); + assert!((-Single::snan(None)).is_negative()); +} + +#[test] +fn is_normal() { + let t = "0x1p+0".parse::<Single>().unwrap(); + assert!(t.is_normal()); + + assert!(!Single::INFINITY.is_normal()); + assert!(!Single::ZERO.is_normal()); + assert!(!Single::NAN.is_normal()); + assert!(!Single::snan(None).is_normal()); + assert!(!"0x1p-149".parse::<Single>().unwrap().is_normal()); +} + +#[test] +fn is_finite() { + let t = "0x1p+0".parse::<Single>().unwrap(); + assert!(t.is_finite()); + assert!(!Single::INFINITY.is_finite()); + assert!(Single::ZERO.is_finite()); + assert!(!Single::NAN.is_finite()); + assert!(!Single::snan(None).is_finite()); + assert!("0x1p-149".parse::<Single>().unwrap().is_finite()); +} + +#[test] +fn is_infinite() { + let t = "0x1p+0".parse::<Single>().unwrap(); + assert!(!t.is_infinite()); + + let pos_inf = Single::INFINITY; + let neg_inf = -Single::INFINITY; + + assert!(pos_inf.is_infinite()); + assert!(pos_inf.is_pos_infinity()); + assert!(!pos_inf.is_neg_infinity()); + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_pos_infinity()); + assert!(neg_inf.is_neg_infinity()); + + assert!(!Single::ZERO.is_infinite()); + assert!(!Single::NAN.is_infinite()); + assert!(!Single::snan(None).is_infinite()); + assert!(!"0x1p-149".parse::<Single>().unwrap().is_infinite()); +} + +#[test] +fn is_nan() { + let t = "0x1p+0".parse::<Single>().unwrap(); + assert!(!t.is_nan()); + assert!(!Single::INFINITY.is_nan()); + assert!(!Single::ZERO.is_nan()); + assert!(Single::NAN.is_nan()); + assert!(Single::snan(None).is_nan()); + assert!(!"0x1p-149".parse::<Single>().unwrap().is_nan()); +} + +#[test] +fn is_finite_non_zero() { + // Test positive/negative normal value. + assert!("0x1p+0".parse::<Single>().unwrap().is_finite_non_zero()); + assert!("-0x1p+0".parse::<Single>().unwrap().is_finite_non_zero()); + + // Test positive/negative denormal value. + assert!("0x1p-149".parse::<Single>().unwrap().is_finite_non_zero()); + assert!("-0x1p-149".parse::<Single>().unwrap().is_finite_non_zero()); + + // Test +/- Infinity. + assert!(!Single::INFINITY.is_finite_non_zero()); + assert!(!(-Single::INFINITY).is_finite_non_zero()); + + // Test +/- Zero. + assert!(!Single::ZERO.is_finite_non_zero()); + assert!(!(-Single::ZERO).is_finite_non_zero()); + + // Test +/- qNaN. +/- dont mean anything with qNaN but paranoia can't hurt in + // this instance. + assert!(!Single::NAN.is_finite_non_zero()); + assert!(!(-Single::NAN).is_finite_non_zero()); + + // Test +/- sNaN. +/- dont mean anything with sNaN but paranoia can't hurt in + // this instance. + assert!(!Single::snan(None).is_finite_non_zero()); + assert!(!(-Single::snan(None)).is_finite_non_zero()); +} + +#[test] +fn add() { + // Test Special Cases against each other and normal values. + + let p_inf = Single::INFINITY; + let m_inf = -Single::INFINITY; + let p_zero = Single::ZERO; + let m_zero = -Single::ZERO; + let qnan = Single::NAN; + let snan = "snan123".parse::<Single>().unwrap(); + let p_normal_value = "0x1p+0".parse::<Single>().unwrap(); + let m_normal_value = "-0x1p+0".parse::<Single>().unwrap(); + let p_largest_value = Single::largest(); + let m_largest_value = -Single::largest(); + let p_smallest_value = Single::SMALLEST; + let m_smallest_value = -Single::SMALLEST; + let p_smallest_normalized = Single::smallest_normalized(); + let m_smallest_normalized = -Single::smallest_normalized(); + + let overflow_status = Status::OVERFLOW | Status::INEXACT; + + let special_cases = [ + (p_inf, p_inf, "inf", Status::OK, Category::Infinity), + (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, p_zero, "inf", Status::OK, Category::Infinity), + (p_inf, m_zero, "inf", Status::OK, Category::Infinity), + (p_inf, qnan, "nan", Status::OK, Category::NaN), + (p_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_normal_value, "inf", Status::OK, Category::Infinity), + (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_largest_value, "inf", Status::OK, Category::Infinity), + (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), + (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), + (p_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), + (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_inf, "-inf", Status::OK, Category::Infinity), + (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), + (m_inf, m_zero, "-inf", Status::OK, Category::Infinity), + (m_inf, qnan, "nan", Status::OK, Category::NaN), + (m_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), + (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), + (m_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), + (p_zero, p_inf, "inf", Status::OK, Category::Infinity), + (p_zero, m_inf, "-inf", Status::OK, Category::Infinity), + (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), + (p_zero, qnan, "nan", Status::OK, Category::NaN), + (p_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_zero, p_normal_value, "0x1p+0", Status::OK, Category::Normal), + (p_zero, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), + (p_zero, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_zero, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (p_zero, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), + (p_zero, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), + (p_zero, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), + (p_zero, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), + (m_zero, p_inf, "inf", Status::OK, Category::Infinity), + (m_zero, m_inf, "-inf", Status::OK, Category::Infinity), + (m_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), + (m_zero, m_zero, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, qnan, "nan", Status::OK, Category::NaN), + (m_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_zero, p_normal_value, "0x1p+0", Status::OK, Category::Normal), + (m_zero, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), + (m_zero, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (m_zero, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_zero, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), + (m_zero, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), + (m_zero, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), + (m_zero, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), + (qnan, p_inf, "nan", Status::OK, Category::NaN), + (qnan, m_inf, "nan", Status::OK, Category::NaN), + (qnan, p_zero, "nan", Status::OK, Category::NaN), + (qnan, m_zero, "nan", Status::OK, Category::NaN), + (qnan, qnan, "nan", Status::OK, Category::NaN), + (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), + (qnan, p_normal_value, "nan", Status::OK, Category::NaN), + (qnan, m_normal_value, "nan", Status::OK, Category::NaN), + (qnan, p_largest_value, "nan", Status::OK, Category::NaN), + (qnan, m_largest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), + (snan, p_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, qnan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, snan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_inf, "inf", Status::OK, Category::Infinity), + (p_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), + (p_normal_value, p_zero, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, m_zero, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, qnan, "nan", Status::OK, Category::NaN), + (p_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_normal_value, "0x1p+1", Status::OK, Category::Normal), + (p_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_normal_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_normal_value, p_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), + (p_normal_value, m_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), + (p_normal_value, p_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), + (p_normal_value, m_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), + (m_normal_value, p_inf, "inf", Status::OK, Category::Infinity), + (m_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), + (m_normal_value, p_zero, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, m_zero, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, qnan, "nan", Status::OK, Category::NaN), + (m_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero), + (m_normal_value, m_normal_value, "-0x1p+1", Status::OK, Category::Normal), + (m_normal_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_normal_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_normal_value, p_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_normal_value, m_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_normal_value, p_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_normal_value, m_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), + (p_largest_value, p_inf, "inf", Status::OK, Category::Infinity), + (p_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), + (p_largest_value, p_zero, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, m_zero, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, qnan, "nan", Status::OK, Category::NaN), + (p_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, m_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, p_largest_value, "inf", overflow_status, Category::Infinity), + (p_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, p_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, m_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, p_smallest_normalized, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, m_smallest_normalized, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, p_inf, "inf", Status::OK, Category::Infinity), + (m_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), + (m_largest_value, p_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, m_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, qnan, "nan", Status::OK, Category::NaN), + (m_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, m_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero), + (m_largest_value, m_largest_value, "-inf", overflow_status, Category::Infinity), + (m_largest_value, p_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, m_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, p_smallest_normalized, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, m_smallest_normalized, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), + (p_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), + (p_smallest_value, p_zero, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, m_zero, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_value, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), + (p_smallest_value, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (p_smallest_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_value, p_smallest_value, "0x1p-148", Status::OK, Category::Normal), + (p_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, p_smallest_normalized, "0x1.000002p-126", Status::OK, Category::Normal), + (p_smallest_value, m_smallest_normalized, "-0x1.fffffcp-127", Status::OK, Category::Normal), + (m_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), + (m_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), + (m_smallest_value, p_zero, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, m_zero, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_value, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), + (m_smallest_value, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_smallest_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_smallest_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_value, m_smallest_value, "-0x1p-148", Status::OK, Category::Normal), + (m_smallest_value, p_smallest_normalized, "0x1.fffffcp-127", Status::OK, Category::Normal), + (m_smallest_value, m_smallest_normalized, "-0x1.000002p-126", Status::OK, Category::Normal), + (p_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), + (p_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), + (p_smallest_normalized, p_zero, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, m_zero, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_normalized, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), + (p_smallest_normalized, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (p_smallest_normalized, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_normalized, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_normalized, p_smallest_value, "0x1.000002p-126", Status::OK, Category::Normal), + (p_smallest_normalized, m_smallest_value, "0x1.fffffcp-127", Status::OK, Category::Normal), + (p_smallest_normalized, p_smallest_normalized, "0x1p-125", Status::OK, Category::Normal), + (p_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), + (m_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), + (m_smallest_normalized, p_zero, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, m_zero, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_normalized, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), + (m_smallest_normalized, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_smallest_normalized, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_smallest_normalized, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_smallest_normalized, p_smallest_value, "-0x1.fffffcp-127", Status::OK, Category::Normal), + (m_smallest_normalized, m_smallest_value, "-0x1.000002p-126", Status::OK, Category::Normal), + (m_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, m_smallest_normalized, "-0x1p-125", Status::OK, Category::Normal), + ]; + + for case @ &(x, y, e_result, e_status, e_category) in &special_cases[..] { + let status; + let result = unpack!(status=, x + y); + assert_eq!(e_status, status); + assert_eq!(e_category, result.category()); + assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()), "result = {result:?}, case = {case:?}"); + } +} + +#[test] +fn subtract() { + // Test Special Cases against each other and normal values. + + let p_inf = Single::INFINITY; + let m_inf = -Single::INFINITY; + let p_zero = Single::ZERO; + let m_zero = -Single::ZERO; + let qnan = Single::NAN; + let snan = "snan123".parse::<Single>().unwrap(); + let p_normal_value = "0x1p+0".parse::<Single>().unwrap(); + let m_normal_value = "-0x1p+0".parse::<Single>().unwrap(); + let p_largest_value = Single::largest(); + let m_largest_value = -Single::largest(); + let p_smallest_value = Single::SMALLEST; + let m_smallest_value = -Single::SMALLEST; + let p_smallest_normalized = Single::smallest_normalized(); + let m_smallest_normalized = -Single::smallest_normalized(); + + let overflow_status = Status::OVERFLOW | Status::INEXACT; + + let special_cases = [ + (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_inf, "inf", Status::OK, Category::Infinity), + (p_inf, p_zero, "inf", Status::OK, Category::Infinity), + (p_inf, m_zero, "inf", Status::OK, Category::Infinity), + (p_inf, qnan, "nan", Status::OK, Category::NaN), + (p_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_normal_value, "inf", Status::OK, Category::Infinity), + (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_largest_value, "inf", Status::OK, Category::Infinity), + (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), + (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), + (p_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), + (m_inf, p_inf, "-inf", Status::OK, Category::Infinity), + (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), + (m_inf, m_zero, "-inf", Status::OK, Category::Infinity), + (m_inf, qnan, "nan", Status::OK, Category::NaN), + (m_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), + (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), + (m_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), + (p_zero, p_inf, "-inf", Status::OK, Category::Infinity), + (p_zero, m_inf, "inf", Status::OK, Category::Infinity), + (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), + (p_zero, qnan, "nan", Status::OK, Category::NaN), + (p_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_zero, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), + (p_zero, m_normal_value, "0x1p+0", Status::OK, Category::Normal), + (p_zero, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (p_zero, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_zero, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), + (p_zero, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), + (p_zero, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), + (p_zero, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), + (m_zero, p_inf, "-inf", Status::OK, Category::Infinity), + (m_zero, m_inf, "inf", Status::OK, Category::Infinity), + (m_zero, p_zero, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), + (m_zero, qnan, "nan", Status::OK, Category::NaN), + (m_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_zero, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), + (m_zero, m_normal_value, "0x1p+0", Status::OK, Category::Normal), + (m_zero, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_zero, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (m_zero, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), + (m_zero, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), + (m_zero, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), + (m_zero, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), + (qnan, p_inf, "nan", Status::OK, Category::NaN), + (qnan, m_inf, "nan", Status::OK, Category::NaN), + (qnan, p_zero, "nan", Status::OK, Category::NaN), + (qnan, m_zero, "nan", Status::OK, Category::NaN), + (qnan, qnan, "nan", Status::OK, Category::NaN), + (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), + (qnan, p_normal_value, "nan", Status::OK, Category::NaN), + (qnan, m_normal_value, "nan", Status::OK, Category::NaN), + (qnan, p_largest_value, "nan", Status::OK, Category::NaN), + (qnan, m_largest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), + (snan, p_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, qnan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, snan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), + (p_normal_value, m_inf, "inf", Status::OK, Category::Infinity), + (p_normal_value, p_zero, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, m_zero, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, qnan, "nan", Status::OK, Category::NaN), + (p_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, m_normal_value, "0x1p+1", Status::OK, Category::Normal), + (p_normal_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_normal_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_normal_value, p_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), + (p_normal_value, m_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), + (p_normal_value, p_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), + (p_normal_value, m_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), + (m_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), + (m_normal_value, m_inf, "inf", Status::OK, Category::Infinity), + (m_normal_value, p_zero, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, m_zero, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, qnan, "nan", Status::OK, Category::NaN), + (m_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_normal_value, p_normal_value, "-0x1p+1", Status::OK, Category::Normal), + (m_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero), + (m_normal_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_normal_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_normal_value, p_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_normal_value, m_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_normal_value, p_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_normal_value, m_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), + (p_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), + (p_largest_value, m_inf, "inf", Status::OK, Category::Infinity), + (p_largest_value, p_zero, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, m_zero, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, qnan, "nan", Status::OK, Category::NaN), + (p_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, m_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, m_largest_value, "inf", overflow_status, Category::Infinity), + (p_largest_value, p_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, m_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, p_smallest_normalized, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_largest_value, m_smallest_normalized, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), + (m_largest_value, m_inf, "inf", Status::OK, Category::Infinity), + (m_largest_value, p_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, m_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, qnan, "nan", Status::OK, Category::NaN), + (m_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, m_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, p_largest_value, "-inf", overflow_status, Category::Infinity), + (m_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero), + (m_largest_value, p_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, m_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, p_smallest_normalized, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_largest_value, m_smallest_normalized, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), + (p_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), + (p_smallest_value, p_zero, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, m_zero, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_value, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (p_smallest_value, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), + (p_smallest_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, m_smallest_value, "0x1p-148", Status::OK, Category::Normal), + (p_smallest_value, p_smallest_normalized, "-0x1.fffffcp-127", Status::OK, Category::Normal), + (p_smallest_value, m_smallest_normalized, "0x1.000002p-126", Status::OK, Category::Normal), + (m_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), + (m_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), + (m_smallest_value, p_zero, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, m_zero, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_value, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_smallest_value, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), + (m_smallest_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_smallest_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_smallest_value, p_smallest_value, "-0x1p-148", Status::OK, Category::Normal), + (m_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_value, p_smallest_normalized, "-0x1.000002p-126", Status::OK, Category::Normal), + (m_smallest_value, m_smallest_normalized, "0x1.fffffcp-127", Status::OK, Category::Normal), + (p_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), + (p_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), + (p_smallest_normalized, p_zero, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, m_zero, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_normalized, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (p_smallest_normalized, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), + (p_smallest_normalized, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_normalized, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (p_smallest_normalized, p_smallest_value, "0x1.fffffcp-127", Status::OK, Category::Normal), + (p_smallest_normalized, m_smallest_value, "0x1.000002p-126", Status::OK, Category::Normal), + (p_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_normalized, m_smallest_normalized, "0x1p-125", Status::OK, Category::Normal), + (m_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), + (m_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), + (m_smallest_normalized, p_zero, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, m_zero, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_normalized, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), + (m_smallest_normalized, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), + (m_smallest_normalized, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_smallest_normalized, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), + (m_smallest_normalized, p_smallest_value, "-0x1.000002p-126", Status::OK, Category::Normal), + (m_smallest_normalized, m_smallest_value, "-0x1.fffffcp-127", Status::OK, Category::Normal), + (m_smallest_normalized, p_smallest_normalized, "-0x1p-125", Status::OK, Category::Normal), + (m_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + ]; + + for case @ &(x, y, e_result, e_status, e_category) in &special_cases[..] { + let status; + let result = unpack!(status=, x - y); + assert_eq!(e_status, status); + assert_eq!(e_category, result.category()); + assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()), "result = {result:?}, case = {case:?}"); + } +} + +#[test] +fn multiply() { + // Test Special Cases against each other and normal values. + + let p_inf = Single::INFINITY; + let m_inf = -Single::INFINITY; + let p_zero = Single::ZERO; + let m_zero = -Single::ZERO; + let qnan = Single::NAN; + let snan = "snan123".parse::<Single>().unwrap(); + let p_normal_value = "0x1p+0".parse::<Single>().unwrap(); + let m_normal_value = "-0x1p+0".parse::<Single>().unwrap(); + let p_largest_value = Single::largest(); + let m_largest_value = -Single::largest(); + let p_smallest_value = Single::SMALLEST; + let m_smallest_value = -Single::SMALLEST; + let p_smallest_normalized = Single::smallest_normalized(); + let m_smallest_normalized = -Single::smallest_normalized(); + + let max_quad = "0x1.ffffffffffffffffffffffffffffp+16383".parse::<Quad>().unwrap(); + let min_quad = "0x0.0000000000000000000000000001p-16382".parse::<Quad>().unwrap(); + let n_min_quad = "-0x0.0000000000000000000000000001p-16382".parse::<Quad>().unwrap(); + + let overflow_status = Status::OVERFLOW | Status::INEXACT; + let underflow_status = Status::UNDERFLOW | Status::INEXACT; + + let single_special_cases = [ + (p_inf, p_inf, "inf", Status::OK, Category::Infinity), + (p_inf, m_inf, "-inf", Status::OK, Category::Infinity), + (p_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, qnan, "nan", Status::OK, Category::NaN), + (p_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), + (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), + (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), + (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), + (p_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), + (m_inf, p_inf, "-inf", Status::OK, Category::Infinity), + (m_inf, m_inf, "inf", Status::OK, Category::Infinity), + (m_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, qnan, "nan", Status::OK, Category::NaN), + (m_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_normal_value, "inf", Status::OK, Category::Infinity), + (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_largest_value, "inf", Status::OK, Category::Infinity), + (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), + (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), + (m_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), + (p_zero, p_inf, "nan", Status::INVALID_OP, Category::NaN), + (p_zero, m_inf, "nan", Status::INVALID_OP, Category::NaN), + (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_zero, "-0x0p+0", Status::OK, Category::Zero), + (p_zero, qnan, "nan", Status::OK, Category::NaN), + (p_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero), + (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, p_inf, "nan", Status::INVALID_OP, Category::NaN), + (m_zero, m_inf, "nan", Status::INVALID_OP, Category::NaN), + (m_zero, p_zero, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), + (m_zero, qnan, "nan", Status::OK, Category::NaN), + (m_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero), + (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero), + (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (qnan, p_inf, "nan", Status::OK, Category::NaN), + (qnan, m_inf, "nan", Status::OK, Category::NaN), + (qnan, p_zero, "nan", Status::OK, Category::NaN), + (qnan, m_zero, "nan", Status::OK, Category::NaN), + (qnan, qnan, "nan", Status::OK, Category::NaN), + (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), + (qnan, p_normal_value, "nan", Status::OK, Category::NaN), + (qnan, m_normal_value, "nan", Status::OK, Category::NaN), + (qnan, p_largest_value, "nan", Status::OK, Category::NaN), + (qnan, m_largest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), + (snan, p_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, qnan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, snan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_inf, "inf", Status::OK, Category::Infinity), + (p_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), + (p_normal_value, p_zero, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), + (p_normal_value, qnan, "nan", Status::OK, Category::NaN), + (p_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_normal_value, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), + (p_normal_value, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_normal_value, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (p_normal_value, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), + (p_normal_value, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), + (p_normal_value, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), + (p_normal_value, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), + (m_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), + (m_normal_value, m_inf, "inf", Status::OK, Category::Infinity), + (m_normal_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), + (m_normal_value, m_zero, "0x0p+0", Status::OK, Category::Zero), + (m_normal_value, qnan, "nan", Status::OK, Category::NaN), + (m_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_normal_value, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, m_normal_value, "0x1p+0", Status::OK, Category::Normal), + (m_normal_value, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_normal_value, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (m_normal_value, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), + (m_normal_value, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), + (m_normal_value, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), + (m_normal_value, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), + (p_largest_value, p_inf, "inf", Status::OK, Category::Infinity), + (p_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), + (p_largest_value, p_zero, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), + (p_largest_value, qnan, "nan", Status::OK, Category::NaN), + (p_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, m_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, p_largest_value, "inf", overflow_status, Category::Infinity), + (p_largest_value, m_largest_value, "-inf", overflow_status, Category::Infinity), + (p_largest_value, p_smallest_value, "0x1.fffffep-22", Status::OK, Category::Normal), + (p_largest_value, m_smallest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), + (p_largest_value, p_smallest_normalized, "0x1.fffffep+1", Status::OK, Category::Normal), + (p_largest_value, m_smallest_normalized, "-0x1.fffffep+1", Status::OK, Category::Normal), + (m_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), + (m_largest_value, m_inf, "inf", Status::OK, Category::Infinity), + (m_largest_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, m_zero, "0x0p+0", Status::OK, Category::Zero), + (m_largest_value, qnan, "nan", Status::OK, Category::NaN), + (m_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, m_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, p_largest_value, "-inf", overflow_status, Category::Infinity), + (m_largest_value, m_largest_value, "inf", overflow_status, Category::Infinity), + (m_largest_value, p_smallest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), + (m_largest_value, m_smallest_value, "0x1.fffffep-22", Status::OK, Category::Normal), + (m_largest_value, p_smallest_normalized, "-0x1.fffffep+1", Status::OK, Category::Normal), + (m_largest_value, m_smallest_normalized, "0x1.fffffep+1", Status::OK, Category::Normal), + (p_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), + (p_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), + (p_smallest_value, p_zero, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, p_largest_value, "0x1.fffffep-22", Status::OK, Category::Normal), + (p_smallest_value, m_largest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), + (p_smallest_value, p_smallest_value, "0x0p+0", underflow_status, Category::Zero), + (p_smallest_value, m_smallest_value, "-0x0p+0", underflow_status, Category::Zero), + (p_smallest_value, p_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), + (p_smallest_value, m_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), + (m_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), + (m_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), + (m_smallest_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_value, m_zero, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, p_largest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), + (m_smallest_value, m_largest_value, "0x1.fffffep-22", Status::OK, Category::Normal), + (m_smallest_value, p_smallest_value, "-0x0p+0", underflow_status, Category::Zero), + (m_smallest_value, m_smallest_value, "0x0p+0", underflow_status, Category::Zero), + (m_smallest_value, p_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), + (m_smallest_value, m_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), + (p_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), + (p_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), + (p_smallest_normalized, p_zero, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_normalized, m_zero, "-0x0p+0", Status::OK, Category::Zero), + (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, p_largest_value, "0x1.fffffep+1", Status::OK, Category::Normal), + (p_smallest_normalized, m_largest_value, "-0x1.fffffep+1", Status::OK, Category::Normal), + (p_smallest_normalized, p_smallest_value, "0x0p+0", underflow_status, Category::Zero), + (p_smallest_normalized, m_smallest_value, "-0x0p+0", underflow_status, Category::Zero), + (p_smallest_normalized, p_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), + (p_smallest_normalized, m_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), + (m_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), + (m_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), + (m_smallest_normalized, p_zero, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, m_zero, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, p_largest_value, "-0x1.fffffep+1", Status::OK, Category::Normal), + (m_smallest_normalized, m_largest_value, "0x1.fffffep+1", Status::OK, Category::Normal), + (m_smallest_normalized, p_smallest_value, "-0x0p+0", underflow_status, Category::Zero), + (m_smallest_normalized, m_smallest_value, "0x0p+0", underflow_status, Category::Zero), + (m_smallest_normalized, p_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), + (m_smallest_normalized, m_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), + ]; + let quad_special_cases = [ + ( + max_quad, + min_quad, + "0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::NearestTiesToEven, + ), + ( + max_quad, + min_quad, + "0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::TowardPositive, + ), + ( + max_quad, + min_quad, + "0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::TowardNegative, + ), + (max_quad, min_quad, "0x1.ffffffffffffffffffffffffffffp-111", Status::OK, Category::Normal, Round::TowardZero), + ( + max_quad, + min_quad, + "0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::NearestTiesToAway, + ), + ( + max_quad, + n_min_quad, + "-0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::NearestTiesToEven, + ), + ( + max_quad, + n_min_quad, + "-0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::TowardPositive, + ), + ( + max_quad, + n_min_quad, + "-0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::TowardNegative, + ), + ( + max_quad, + n_min_quad, + "-0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::TowardZero, + ), + ( + max_quad, + n_min_quad, + "-0x1.ffffffffffffffffffffffffffffp-111", + Status::OK, + Category::Normal, + Round::NearestTiesToAway, + ), + (max_quad, max_quad, "inf", overflow_status, Category::Infinity, Round::NearestTiesToEven), + (max_quad, max_quad, "inf", overflow_status, Category::Infinity, Round::TowardPositive), + ( + max_quad, + max_quad, + "0x1.ffffffffffffffffffffffffffffp+16383", + Status::INEXACT, + Category::Normal, + Round::TowardNegative, + ), + ( + max_quad, + max_quad, + "0x1.ffffffffffffffffffffffffffffp+16383", + Status::INEXACT, + Category::Normal, + Round::TowardZero, + ), + (max_quad, max_quad, "inf", overflow_status, Category::Infinity, Round::NearestTiesToAway), + (min_quad, min_quad, "0", underflow_status, Category::Zero, Round::NearestTiesToEven), + ( + min_quad, + min_quad, + "0x0.0000000000000000000000000001p-16382", + underflow_status, + Category::Normal, + Round::TowardPositive, + ), + (min_quad, min_quad, "0", underflow_status, Category::Zero, Round::TowardNegative), + (min_quad, min_quad, "0", underflow_status, Category::Zero, Round::TowardZero), + (min_quad, min_quad, "0", underflow_status, Category::Zero, Round::NearestTiesToAway), + (min_quad, n_min_quad, "-0", underflow_status, Category::Zero, Round::NearestTiesToEven), + (min_quad, n_min_quad, "-0", underflow_status, Category::Zero, Round::TowardPositive), + ( + min_quad, + n_min_quad, + "-0x0.0000000000000000000000000001p-16382", + underflow_status, + Category::Normal, + Round::TowardNegative, + ), + (min_quad, n_min_quad, "-0", underflow_status, Category::Zero, Round::TowardZero), + (min_quad, n_min_quad, "-0", underflow_status, Category::Zero, Round::NearestTiesToAway), + ]; + + for case @ &(x, y, e_result, e_status, e_category) in &single_special_cases { + let status; + let result = unpack!(status=, x * y); + assert_eq!(e_status, status); + assert_eq!(e_category, result.category()); + assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()), "result = {result:?}, case = {case:?}"); + } + for case @ &(x, y, e_result, e_status, e_category, round) in &quad_special_cases { + let status; + let result = unpack!(status=, x.mul_r(y, round)); + assert_eq!(e_status, status); + assert_eq!(e_category, result.category()); + assert!(result.bitwise_eq(e_result.parse::<Quad>().unwrap()), "result = {result:?}, case = {case:?}"); + } +} + +#[test] +fn divide() { + // Test Special Cases against each other and normal values. + + let p_inf = Single::INFINITY; + let m_inf = -Single::INFINITY; + let p_zero = Single::ZERO; + let m_zero = -Single::ZERO; + let qnan = Single::NAN; + let snan = "snan123".parse::<Single>().unwrap(); + let p_normal_value = "0x1p+0".parse::<Single>().unwrap(); + let m_normal_value = "-0x1p+0".parse::<Single>().unwrap(); + let p_largest_value = Single::largest(); + let m_largest_value = -Single::largest(); + let p_smallest_value = Single::SMALLEST; + let m_smallest_value = -Single::SMALLEST; + let p_smallest_normalized = Single::smallest_normalized(); + let m_smallest_normalized = -Single::smallest_normalized(); + + let max_quad = "0x1.ffffffffffffffffffffffffffffp+16383".parse::<Quad>().unwrap(); + let min_quad = "0x0.0000000000000000000000000001p-16382".parse::<Quad>().unwrap(); + let n_min_quad = "-0x0.0000000000000000000000000001p-16382".parse::<Quad>().unwrap(); + + let overflow_status = Status::OVERFLOW | Status::INEXACT; + let underflow_status = Status::UNDERFLOW | Status::INEXACT; + + let single_special_cases = [ + (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, p_zero, "inf", Status::OK, Category::Infinity), + (p_inf, m_zero, "-inf", Status::OK, Category::Infinity), + (p_inf, qnan, "nan", Status::OK, Category::NaN), + (p_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), + (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), + (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), + (p_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), + (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), + (p_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), + (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), + (m_inf, m_zero, "inf", Status::OK, Category::Infinity), + (m_inf, qnan, "nan", Status::OK, Category::NaN), + (m_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_normal_value, "inf", Status::OK, Category::Infinity), + (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_largest_value, "inf", Status::OK, Category::Infinity), + (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), + (m_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), + (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), + (m_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), + (p_zero, p_inf, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_inf, "-0x0p+0", Status::OK, Category::Zero), + (p_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_zero, qnan, "nan", Status::OK, Category::NaN), + (p_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero), + (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, p_inf, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_inf, "0x0p+0", Status::OK, Category::Zero), + (m_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_zero, qnan, "nan", Status::OK, Category::NaN), + (m_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero), + (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero), + (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (qnan, p_inf, "nan", Status::OK, Category::NaN), + (qnan, m_inf, "nan", Status::OK, Category::NaN), + (qnan, p_zero, "nan", Status::OK, Category::NaN), + (qnan, m_zero, "nan", Status::OK, Category::NaN), + (qnan, qnan, "nan", Status::OK, Category::NaN), + (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), + (qnan, p_normal_value, "nan", Status::OK, Category::NaN), + (qnan, m_normal_value, "nan", Status::OK, Category::NaN), + (qnan, p_largest_value, "nan", Status::OK, Category::NaN), + (qnan, m_largest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), + (snan, p_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, qnan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, snan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_inf, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), + (p_normal_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), + (p_normal_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), + (p_normal_value, qnan, "nan", Status::OK, Category::NaN), + (p_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_normal_value, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), + (p_normal_value, p_largest_value, "0x1p-128", underflow_status, Category::Normal), + (p_normal_value, m_largest_value, "-0x1p-128", underflow_status, Category::Normal), + (p_normal_value, p_smallest_value, "inf", overflow_status, Category::Infinity), + (p_normal_value, m_smallest_value, "-inf", overflow_status, Category::Infinity), + (p_normal_value, p_smallest_normalized, "0x1p+126", Status::OK, Category::Normal), + (p_normal_value, m_smallest_normalized, "-0x1p+126", Status::OK, Category::Normal), + (m_normal_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), + (m_normal_value, m_inf, "0x0p+0", Status::OK, Category::Zero), + (m_normal_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), + (m_normal_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), + (m_normal_value, qnan, "nan", Status::OK, Category::NaN), + (m_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_normal_value, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, m_normal_value, "0x1p+0", Status::OK, Category::Normal), + (m_normal_value, p_largest_value, "-0x1p-128", underflow_status, Category::Normal), + (m_normal_value, m_largest_value, "0x1p-128", underflow_status, Category::Normal), + (m_normal_value, p_smallest_value, "-inf", overflow_status, Category::Infinity), + (m_normal_value, m_smallest_value, "inf", overflow_status, Category::Infinity), + (m_normal_value, p_smallest_normalized, "-0x1p+126", Status::OK, Category::Normal), + (m_normal_value, m_smallest_normalized, "0x1p+126", Status::OK, Category::Normal), + (p_largest_value, p_inf, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), + (p_largest_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), + (p_largest_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), + (p_largest_value, qnan, "nan", Status::OK, Category::NaN), + (p_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, m_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, p_largest_value, "0x1p+0", Status::OK, Category::Normal), + (p_largest_value, m_largest_value, "-0x1p+0", Status::OK, Category::Normal), + (p_largest_value, p_smallest_value, "inf", overflow_status, Category::Infinity), + (p_largest_value, m_smallest_value, "-inf", overflow_status, Category::Infinity), + (p_largest_value, p_smallest_normalized, "inf", overflow_status, Category::Infinity), + (p_largest_value, m_smallest_normalized, "-inf", overflow_status, Category::Infinity), + (m_largest_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, m_inf, "0x0p+0", Status::OK, Category::Zero), + (m_largest_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), + (m_largest_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), + (m_largest_value, qnan, "nan", Status::OK, Category::NaN), + (m_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, m_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, p_largest_value, "-0x1p+0", Status::OK, Category::Normal), + (m_largest_value, m_largest_value, "0x1p+0", Status::OK, Category::Normal), + (m_largest_value, p_smallest_value, "-inf", overflow_status, Category::Infinity), + (m_largest_value, m_smallest_value, "inf", overflow_status, Category::Infinity), + (m_largest_value, p_smallest_normalized, "-inf", overflow_status, Category::Infinity), + (m_largest_value, m_smallest_normalized, "inf", overflow_status, Category::Infinity), + (p_smallest_value, p_inf, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), + (p_smallest_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), + (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, p_largest_value, "0x0p+0", underflow_status, Category::Zero), + (p_smallest_value, m_largest_value, "-0x0p+0", underflow_status, Category::Zero), + (p_smallest_value, p_smallest_value, "0x1p+0", Status::OK, Category::Normal), + (p_smallest_value, m_smallest_value, "-0x1p+0", Status::OK, Category::Normal), + (p_smallest_value, p_smallest_normalized, "0x1p-23", Status::OK, Category::Normal), + (p_smallest_value, m_smallest_normalized, "-0x1p-23", Status::OK, Category::Normal), + (m_smallest_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_value, m_inf, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), + (m_smallest_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), + (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, p_largest_value, "-0x0p+0", underflow_status, Category::Zero), + (m_smallest_value, m_largest_value, "0x0p+0", underflow_status, Category::Zero), + (m_smallest_value, p_smallest_value, "-0x1p+0", Status::OK, Category::Normal), + (m_smallest_value, m_smallest_value, "0x1p+0", Status::OK, Category::Normal), + (m_smallest_value, p_smallest_normalized, "-0x1p-23", Status::OK, Category::Normal), + (m_smallest_value, m_smallest_normalized, "0x1p-23", Status::OK, Category::Normal), + (p_smallest_normalized, p_inf, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_normalized, m_inf, "-0x0p+0", Status::OK, Category::Zero), + (p_smallest_normalized, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), + (p_smallest_normalized, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), + (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, p_largest_value, "0x0p+0", underflow_status, Category::Zero), + (p_smallest_normalized, m_largest_value, "-0x0p+0", underflow_status, Category::Zero), + (p_smallest_normalized, p_smallest_value, "0x1p+23", Status::OK, Category::Normal), + (p_smallest_normalized, m_smallest_value, "-0x1p+23", Status::OK, Category::Normal), + (p_smallest_normalized, p_smallest_normalized, "0x1p+0", Status::OK, Category::Normal), + (p_smallest_normalized, m_smallest_normalized, "-0x1p+0", Status::OK, Category::Normal), + (m_smallest_normalized, p_inf, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, m_inf, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), + (m_smallest_normalized, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), + (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, p_largest_value, "-0x0p+0", underflow_status, Category::Zero), + (m_smallest_normalized, m_largest_value, "0x0p+0", underflow_status, Category::Zero), + (m_smallest_normalized, p_smallest_value, "-0x1p+23", Status::OK, Category::Normal), + (m_smallest_normalized, m_smallest_value, "0x1p+23", Status::OK, Category::Normal), + (m_smallest_normalized, p_smallest_normalized, "-0x1p+0", Status::OK, Category::Normal), + (m_smallest_normalized, m_smallest_normalized, "0x1p+0", Status::OK, Category::Normal), + ]; + let quad_special_cases = [ + (max_quad, n_min_quad, "-inf", overflow_status, Category::Infinity, Round::NearestTiesToEven), + ( + max_quad, + n_min_quad, + "-0x1.ffffffffffffffffffffffffffffp+16383", + Status::INEXACT, + Category::Normal, + Round::TowardPositive, + ), + (max_quad, n_min_quad, "-inf", overflow_status, Category::Infinity, Round::TowardNegative), + ( + max_quad, + n_min_quad, + "-0x1.ffffffffffffffffffffffffffffp+16383", + Status::INEXACT, + Category::Normal, + Round::TowardZero, + ), + (max_quad, n_min_quad, "-inf", overflow_status, Category::Infinity, Round::NearestTiesToAway), + (min_quad, max_quad, "0", underflow_status, Category::Zero, Round::NearestTiesToEven), + ( + min_quad, + max_quad, + "0x0.0000000000000000000000000001p-16382", + underflow_status, + Category::Normal, + Round::TowardPositive, + ), + (min_quad, max_quad, "0", underflow_status, Category::Zero, Round::TowardNegative), + (min_quad, max_quad, "0", underflow_status, Category::Zero, Round::TowardZero), + (min_quad, max_quad, "0", underflow_status, Category::Zero, Round::NearestTiesToAway), + (n_min_quad, max_quad, "-0", underflow_status, Category::Zero, Round::NearestTiesToEven), + (n_min_quad, max_quad, "-0", underflow_status, Category::Zero, Round::TowardPositive), + ( + n_min_quad, + max_quad, + "-0x0.0000000000000000000000000001p-16382", + underflow_status, + Category::Normal, + Round::TowardNegative, + ), + (n_min_quad, max_quad, "-0", underflow_status, Category::Zero, Round::TowardZero), + (n_min_quad, max_quad, "-0", underflow_status, Category::Zero, Round::NearestTiesToAway), + ]; + + for case @ &(x, y, e_result, e_status, e_category) in &single_special_cases { + let status; + let result = unpack!(status=, x / y); + assert_eq!(e_status, status); + assert_eq!(e_category, result.category()); + assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()), "result = {result:?}, case = {case:?}"); + } + for case @ &(x, y, e_result, e_status, e_category, round) in &quad_special_cases { + let status; + let result = unpack!(status=, x.div_r(y, round)); + assert_eq!(e_status, status); + assert_eq!(e_category, result.category()); + assert!(result.bitwise_eq(e_result.parse::<Quad>().unwrap()), "result = {result:?}, case = {case:?}"); + } +} + +#[test] +fn operator_overloads() { + // This is mostly testing that these operator overloads compile. + let one = "0x1p+0".parse::<Single>().unwrap(); + let two = "0x2p+0".parse::<Single>().unwrap(); + assert!(two.bitwise_eq((one + one).value)); + assert!(one.bitwise_eq((two - one).value)); + assert!(two.bitwise_eq((one * two).value)); + assert!(one.bitwise_eq((two / two).value)); +} + +#[test] +fn comparisons() { + let vals = [ + /* MNan */ -Single::NAN, + /* MInf */ -Single::INFINITY, + /* MBig */ -Single::largest(), + /* MOne */ "-0x1p+0".parse::<Single>().unwrap(), + /* MZer */ -Single::ZERO, + /* PZer */ Single::ZERO, + /* POne */ "0x1p+0".parse::<Single>().unwrap(), + /* PBig */ Single::largest(), + /* PInf */ Single::INFINITY, + /* PNan */ Single::NAN, + ]; + + const LT: Option<Ordering> = Some(Ordering::Less); + const EQ: Option<Ordering> = Some(Ordering::Equal); + const GT: Option<Ordering> = Some(Ordering::Greater); + const UN: Option<Ordering> = None; + + // HACK(eddyb) for some reason the first row (MNan) gets formatted differently. + #[rustfmt::skip] + let relations = [ + // -N -I -B -1 -0 +0 +1 +B +I +N + /* MNan */ [UN, UN, UN, UN, UN, UN, UN, UN, UN, UN], + /* MInf */ [UN, EQ, LT, LT, LT, LT, LT, LT, LT, UN], + /* MBig */ [UN, GT, EQ, LT, LT, LT, LT, LT, LT, UN], + /* MOne */ [UN, GT, GT, EQ, LT, LT, LT, LT, LT, UN], + /* MZer */ [UN, GT, GT, GT, EQ, EQ, LT, LT, LT, UN], + /* PZer */ [UN, GT, GT, GT, EQ, EQ, LT, LT, LT, UN], + /* POne */ [UN, GT, GT, GT, GT, GT, EQ, LT, LT, UN], + /* PBig */ [UN, GT, GT, GT, GT, GT, GT, EQ, LT, UN], + /* PInf */ [UN, GT, GT, GT, GT, GT, GT, GT, EQ, UN], + /* PNan */ [UN, UN, UN, UN, UN, UN, UN, UN, UN, UN], + ]; + for (i, &lhs) in vals.iter().enumerate() { + for (j, &rhs) in vals.iter().enumerate() { + let relation = lhs.partial_cmp(&rhs); + assert_eq!(relation, relations[i][j]); + + // NOTE(eddyb) these checks have been kept from the C++ code which didn't + // appear to have a concept like `Option<Ordering>`, but in Rust they + // should be entirely redundant with the single `assert_eq!` above. + match relation { + LT => { + assert!(!(lhs == rhs)); + assert!(lhs != rhs); + assert!(lhs < rhs); + assert!(!(lhs > rhs)); + assert!(lhs <= rhs); + assert!(!(lhs >= rhs)); + } + EQ => { + assert!(lhs == rhs); + assert!(!(lhs != rhs)); + assert!(!(lhs < rhs)); + assert!(!(lhs > rhs)); + assert!(lhs <= rhs); + assert!(lhs >= rhs); + } + GT => { + assert!(!(lhs == rhs)); + assert!(lhs != rhs); + assert!(!(lhs < rhs)); + assert!(lhs > rhs); + assert!(!(lhs <= rhs)); + assert!(lhs >= rhs); + } + UN => { + assert!(!(lhs == rhs)); + assert!(lhs != rhs); + assert!(!(lhs < rhs)); + assert!(!(lhs > rhs)); + assert!(!(lhs <= rhs)); + assert!(!(lhs >= rhs)); + } + } + } + } +} + +#[test] +fn abs() { + let p_inf = Single::INFINITY; + let m_inf = -Single::INFINITY; + let p_zero = Single::ZERO; + let m_zero = -Single::ZERO; + let p_qnan = Single::NAN; + let m_qnan = -Single::NAN; + let p_snan = Single::snan(None); + let m_snan = -Single::snan(None); + let p_normal_value = "0x1p+0".parse::<Single>().unwrap(); + let m_normal_value = "-0x1p+0".parse::<Single>().unwrap(); + let p_largest_value = Single::largest(); + let m_largest_value = -Single::largest(); + let p_smallest_value = Single::SMALLEST; + let m_smallest_value = -Single::SMALLEST; + let p_smallest_normalized = Single::smallest_normalized(); + let m_smallest_normalized = -Single::smallest_normalized(); + + assert!(p_inf.bitwise_eq(p_inf.abs())); + assert!(p_inf.bitwise_eq(m_inf.abs())); + assert!(p_zero.bitwise_eq(p_zero.abs())); + assert!(p_zero.bitwise_eq(m_zero.abs())); + assert!(p_qnan.bitwise_eq(p_qnan.abs())); + assert!(p_qnan.bitwise_eq(m_qnan.abs())); + assert!(p_snan.bitwise_eq(p_snan.abs())); + assert!(p_snan.bitwise_eq(m_snan.abs())); + assert!(p_normal_value.bitwise_eq(p_normal_value.abs())); + assert!(p_normal_value.bitwise_eq(m_normal_value.abs())); + assert!(p_largest_value.bitwise_eq(p_largest_value.abs())); + assert!(p_largest_value.bitwise_eq(m_largest_value.abs())); + assert!(p_smallest_value.bitwise_eq(p_smallest_value.abs())); + assert!(p_smallest_value.bitwise_eq(m_smallest_value.abs())); + assert!(p_smallest_normalized.bitwise_eq(p_smallest_normalized.abs(),)); + assert!(p_smallest_normalized.bitwise_eq(m_smallest_normalized.abs(),)); +} + +#[test] +fn neg() { + let one = "1.0".parse::<Single>().unwrap(); + let neg_one = "-1.0".parse::<Single>().unwrap(); + let zero = Single::ZERO; + let neg_zero = -Single::ZERO; + let inf = Single::INFINITY; + let neg_inf = -Single::INFINITY; + let qnan = Single::NAN; + let neg_qnan = -Single::NAN; + + assert!(neg_one.bitwise_eq(-one)); + assert!(one.bitwise_eq(-neg_one)); + assert!(neg_zero.bitwise_eq(-zero)); + assert!(zero.bitwise_eq(-neg_zero)); + assert!(neg_inf.bitwise_eq(-inf)); + assert!(inf.bitwise_eq(-neg_inf)); + assert!(neg_inf.bitwise_eq(-inf)); + assert!(inf.bitwise_eq(-neg_inf)); + assert!(neg_qnan.bitwise_eq(-qnan)); + assert!(qnan.bitwise_eq(-neg_qnan)); +} + +#[test] +fn ilogb() { + assert_eq!(-1074, Double::SMALLEST.ilogb()); + assert_eq!(-1074, (-Double::SMALLEST).ilogb()); + assert_eq!(-1023, "0x1.ffffffffffffep-1024".parse::<Double>().unwrap().ilogb()); + assert_eq!(-1023, "0x1.ffffffffffffep-1023".parse::<Double>().unwrap().ilogb()); + assert_eq!(-1023, "-0x1.ffffffffffffep-1023".parse::<Double>().unwrap().ilogb()); + assert_eq!(-51, "0x1p-51".parse::<Double>().unwrap().ilogb()); + assert_eq!(-1023, "0x1.c60f120d9f87cp-1023".parse::<Double>().unwrap().ilogb()); + assert_eq!(-2, "0x0.ffffp-1".parse::<Double>().unwrap().ilogb()); + assert_eq!(-1023, "0x1.fffep-1023".parse::<Double>().unwrap().ilogb()); + assert_eq!(1023, Double::largest().ilogb()); + assert_eq!(1023, (-Double::largest()).ilogb()); + + assert_eq!(0, "0x1p+0".parse::<Single>().unwrap().ilogb()); + assert_eq!(0, "-0x1p+0".parse::<Single>().unwrap().ilogb()); + assert_eq!(42, "0x1p+42".parse::<Single>().unwrap().ilogb()); + assert_eq!(-42, "0x1p-42".parse::<Single>().unwrap().ilogb()); + + assert_eq!(IEK_INF, Single::INFINITY.ilogb()); + assert_eq!(IEK_INF, (-Single::INFINITY).ilogb()); + assert_eq!(IEK_ZERO, Single::ZERO.ilogb()); + assert_eq!(IEK_ZERO, (-Single::ZERO).ilogb()); + assert_eq!(IEK_NAN, Single::NAN.ilogb()); + assert_eq!(IEK_NAN, Single::snan(None).ilogb()); + + assert_eq!(127, Single::largest().ilogb()); + assert_eq!(127, (-Single::largest()).ilogb()); + + assert_eq!(-149, Single::SMALLEST.ilogb()); + assert_eq!(-149, (-Single::SMALLEST).ilogb()); + assert_eq!(-126, Single::smallest_normalized().ilogb()); + assert_eq!(-126, (-Single::smallest_normalized()).ilogb()); +} + +#[test] +fn scalbn() { + assert!("0x1p+0" + .parse::<Single>() + .unwrap() + .bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(0),)); + assert!("0x1p+42" + .parse::<Single>() + .unwrap() + .bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(42),)); + assert!("0x1p-42" + .parse::<Single>() + .unwrap() + .bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(-42),)); + + let p_inf = Single::INFINITY; + let m_inf = -Single::INFINITY; + let p_zero = Single::ZERO; + let m_zero = -Single::ZERO; + let p_qnan = Single::NAN; + let m_qnan = -Single::NAN; + let snan = Single::snan(None); + + assert!(p_inf.bitwise_eq(p_inf.scalbn(0))); + assert!(m_inf.bitwise_eq(m_inf.scalbn(0))); + assert!(p_zero.bitwise_eq(p_zero.scalbn(0))); + assert!(m_zero.bitwise_eq(m_zero.scalbn(0))); + assert!(p_qnan.bitwise_eq(p_qnan.scalbn(0))); + assert!(m_qnan.bitwise_eq(m_qnan.scalbn(0))); + assert!(!snan.scalbn(0).is_signaling()); + + let scalbn_snan = snan.scalbn(1); + assert!(scalbn_snan.is_nan() && !scalbn_snan.is_signaling()); + + // Make sure highest bit of payload is preserved. + let payload = (1 << 50) | (1 << 49) | (1234 << 32) | 1; + + let snan_with_payload = Double::snan(Some(payload)); + let quiet_payload = snan_with_payload.scalbn(1); + assert!(quiet_payload.is_nan() && !quiet_payload.is_signaling()); + assert_eq!(payload, quiet_payload.to_bits() & ((1 << 51) - 1)); + + assert!(p_inf.bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(128),)); + assert!(m_inf.bitwise_eq("-0x1p+0".parse::<Single>().unwrap().scalbn(128),)); + assert!(p_inf.bitwise_eq("0x1p+127".parse::<Single>().unwrap().scalbn(1),)); + assert!(p_zero.bitwise_eq("0x1p-127".parse::<Single>().unwrap().scalbn(-127),)); + assert!(m_zero.bitwise_eq("-0x1p-127".parse::<Single>().unwrap().scalbn(-127),)); + assert!("-0x1p-149" + .parse::<Single>() + .unwrap() + .bitwise_eq("-0x1p-127".parse::<Single>().unwrap().scalbn(-22),)); + assert!(p_zero.bitwise_eq("0x1p-126".parse::<Single>().unwrap().scalbn(-24),)); + + let smallest_f64 = Double::SMALLEST; + let neg_smallest_f64 = -Double::SMALLEST; + + let largest_f64 = Double::largest(); + let neg_largest_f64 = -Double::largest(); + + let largest_denormal_f64 = "0x1.ffffffffffffep-1023".parse::<Double>().unwrap(); + let neg_largest_denormal_f64 = "-0x1.ffffffffffffep-1023".parse::<Double>().unwrap(); + + assert!(smallest_f64.bitwise_eq("0x1p-1074".parse::<Double>().unwrap().scalbn(0),)); + assert!(neg_smallest_f64.bitwise_eq("-0x1p-1074".parse::<Double>().unwrap().scalbn(0),)); + + assert!("0x1p+1023" + .parse::<Double>() + .unwrap() + .bitwise_eq(smallest_f64.scalbn(2097,),)); + + assert!(smallest_f64.scalbn(-2097).is_pos_zero()); + assert!(smallest_f64.scalbn(-2098).is_pos_zero()); + assert!(smallest_f64.scalbn(-2099).is_pos_zero()); + assert!("0x1p+1022" + .parse::<Double>() + .unwrap() + .bitwise_eq(smallest_f64.scalbn(2096,),)); + assert!("0x1p+1023" + .parse::<Double>() + .unwrap() + .bitwise_eq(smallest_f64.scalbn(2097,),)); + assert!(smallest_f64.scalbn(2098).is_infinite()); + assert!(smallest_f64.scalbn(2099).is_infinite()); + + // Test for integer overflows when adding to exponent. + assert!(smallest_f64.scalbn(-ExpInt::max_value()).is_pos_zero()); + assert!(largest_f64.scalbn(ExpInt::max_value()).is_infinite()); + + assert!(largest_denormal_f64.bitwise_eq(largest_denormal_f64.scalbn(0),)); + assert!(neg_largest_denormal_f64.bitwise_eq(neg_largest_denormal_f64.scalbn(0),)); + + assert!("0x1.ffffffffffffep-1022" + .parse::<Double>() + .unwrap() + .bitwise_eq(largest_denormal_f64.scalbn(1))); + assert!("-0x1.ffffffffffffep-1021" + .parse::<Double>() + .unwrap() + .bitwise_eq(neg_largest_denormal_f64.scalbn(2))); + + assert!("0x1.ffffffffffffep+1" + .parse::<Double>() + .unwrap() + .bitwise_eq(largest_denormal_f64.scalbn(1024))); + assert!(largest_denormal_f64.scalbn(-1023).is_pos_zero()); + assert!(largest_denormal_f64.scalbn(-1024).is_pos_zero()); + assert!(largest_denormal_f64.scalbn(-2048).is_pos_zero()); + assert!(largest_denormal_f64.scalbn(2047).is_infinite()); + assert!(largest_denormal_f64.scalbn(2098).is_infinite()); + assert!(largest_denormal_f64.scalbn(2099).is_infinite()); + + assert!("0x1.ffffffffffffep-2" + .parse::<Double>() + .unwrap() + .bitwise_eq(largest_denormal_f64.scalbn(1021))); + assert!("0x1.ffffffffffffep-1" + .parse::<Double>() + .unwrap() + .bitwise_eq(largest_denormal_f64.scalbn(1022))); + assert!("0x1.ffffffffffffep+0" + .parse::<Double>() + .unwrap() + .bitwise_eq(largest_denormal_f64.scalbn(1023))); + assert!("0x1.ffffffffffffep+1023" + .parse::<Double>() + .unwrap() + .bitwise_eq(largest_denormal_f64.scalbn(2046))); + assert!("0x1p+974" + .parse::<Double>() + .unwrap() + .bitwise_eq(smallest_f64.scalbn(2048,),)); + + let random_denormal_f64 = "0x1.c60f120d9f87cp+51".parse::<Double>().unwrap(); + assert!("0x1.c60f120d9f87cp-972" + .parse::<Double>() + .unwrap() + .bitwise_eq(random_denormal_f64.scalbn(-1023))); + assert!("0x1.c60f120d9f87cp-1" + .parse::<Double>() + .unwrap() + .bitwise_eq(random_denormal_f64.scalbn(-52))); + assert!("0x1.c60f120d9f87cp-2" + .parse::<Double>() + .unwrap() + .bitwise_eq(random_denormal_f64.scalbn(-53))); + assert!("0x1.c60f120d9f87cp+0" + .parse::<Double>() + .unwrap() + .bitwise_eq(random_denormal_f64.scalbn(-51))); + + assert!(random_denormal_f64.scalbn(-2097).is_pos_zero()); + assert!(random_denormal_f64.scalbn(-2090).is_pos_zero()); + + assert!("-0x1p-1073" + .parse::<Double>() + .unwrap() + .bitwise_eq(neg_largest_f64.scalbn(-2097),)); + + assert!("-0x1p-1024" + .parse::<Double>() + .unwrap() + .bitwise_eq(neg_largest_f64.scalbn(-2048),)); + + assert!("0x1p-1073" + .parse::<Double>() + .unwrap() + .bitwise_eq(largest_f64.scalbn(-2097,),)); + + assert!("0x1p-1074" + .parse::<Double>() + .unwrap() + .bitwise_eq(largest_f64.scalbn(-2098,),)); + assert!("-0x1p-1074" + .parse::<Double>() + .unwrap() + .bitwise_eq(neg_largest_f64.scalbn(-2098),)); + assert!(neg_largest_f64.scalbn(-2099).is_neg_zero()); + assert!(largest_f64.scalbn(1).is_infinite()); + + assert!("0x1p+0" + .parse::<Double>() + .unwrap() + .bitwise_eq("0x1p+52".parse::<Double>().unwrap().scalbn(-52),)); + + assert!("0x1p-103" + .parse::<Double>() + .unwrap() + .bitwise_eq("0x1p-51".parse::<Double>().unwrap().scalbn(-52),)); +} + +#[test] +fn frexp() { + let p_zero = Double::ZERO; + let m_zero = -Double::ZERO; + let one = Double::from_f64(1.0); + let m_one = Double::from_f64(-1.0); + + let largest_denormal = "0x1.ffffffffffffep-1023".parse::<Double>().unwrap(); + let neg_largest_denormal = "-0x1.ffffffffffffep-1023".parse::<Double>().unwrap(); + + let smallest = Double::SMALLEST; + let neg_smallest = -Double::SMALLEST; + + let largest = Double::largest(); + let neg_largest = -Double::largest(); + + let p_inf = Double::INFINITY; + let m_inf = -Double::INFINITY; + + let p_qnan = Double::NAN; + let m_qnan = -Double::NAN; + let snan = Double::snan(None); + + // Make sure highest bit of payload is preserved. + let payload = (1 << 50) | (1 << 49) | (1234 << 32) | 1; + + let snan_with_payload = Double::snan(Some(payload)); + + let mut exp = 0; + + let frac = p_zero.frexp(&mut exp); + assert_eq!(0, exp); + assert!(frac.is_pos_zero()); + + let frac = m_zero.frexp(&mut exp); + assert_eq!(0, exp); + assert!(frac.is_neg_zero()); + + let frac = one.frexp(&mut exp); + assert_eq!(1, exp); + assert!("0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = m_one.frexp(&mut exp); + assert_eq!(1, exp); + assert!("-0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = largest_denormal.frexp(&mut exp); + assert_eq!(-1022, exp); + assert!("0x1.ffffffffffffep-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = neg_largest_denormal.frexp(&mut exp); + assert_eq!(-1022, exp); + assert!("-0x1.ffffffffffffep-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = smallest.frexp(&mut exp); + assert_eq!(-1073, exp); + assert!("0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = neg_smallest.frexp(&mut exp); + assert_eq!(-1073, exp); + assert!("-0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = largest.frexp(&mut exp); + assert_eq!(1024, exp); + assert!("0x1.fffffffffffffp-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = neg_largest.frexp(&mut exp); + assert_eq!(1024, exp); + assert!("-0x1.fffffffffffffp-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = p_inf.frexp(&mut exp); + assert_eq!(IEK_INF, exp); + assert!(frac.is_infinite() && !frac.is_negative()); + + let frac = m_inf.frexp(&mut exp); + assert_eq!(IEK_INF, exp); + assert!(frac.is_infinite() && frac.is_negative()); + + let frac = p_qnan.frexp(&mut exp); + assert_eq!(IEK_NAN, exp); + assert!(frac.is_nan()); + + let frac = m_qnan.frexp(&mut exp); + assert_eq!(IEK_NAN, exp); + assert!(frac.is_nan()); + + let frac = snan.frexp(&mut exp); + assert_eq!(IEK_NAN, exp); + assert!(frac.is_nan() && !frac.is_signaling()); + + let frac = snan_with_payload.frexp(&mut exp); + assert_eq!(IEK_NAN, exp); + assert!(frac.is_nan() && !frac.is_signaling()); + assert_eq!(payload, frac.to_bits() & ((1 << 51) - 1)); + + let frac = "0x0.ffffp-1".parse::<Double>().unwrap().frexp(&mut exp); + assert_eq!(-1, exp); + assert!("0x1.fffep-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = "0x1p-51".parse::<Double>().unwrap().frexp(&mut exp); + assert_eq!(-50, exp); + assert!("0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); + + let frac = "0x1.c60f120d9f87cp+51".parse::<Double>().unwrap().frexp(&mut exp); + assert_eq!(52, exp); + assert!("0x1.c60f120d9f87cp-1".parse::<Double>().unwrap().bitwise_eq(frac)); +} + +#[test] +fn modulo() { + let mut status; + { + let f1 = "1.5".parse::<Double>().unwrap(); + let f2 = "1.0".parse::<Double>().unwrap(); + let expected = "0.5".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "0.5".parse::<Double>().unwrap(); + let f2 = "1.0".parse::<Double>().unwrap(); + let expected = "0.5".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "0x1.3333333333333p-2".parse::<Double>().unwrap(); // 0.3 + let f2 = "0x1.47ae147ae147bp-7".parse::<Double>().unwrap(); // 0.01 + // 0.009999999999999983 + let expected = "0x1.47ae147ae1471p-7".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "0x1p64".parse::<Double>().unwrap(); // 1.8446744073709552e19 + let f2 = "1.5".parse::<Double>().unwrap(); + let expected = "1.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "0x1p1000".parse::<Double>().unwrap(); + let f2 = "0x1p-1000".parse::<Double>().unwrap(); + let expected = "0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "0.0".parse::<Double>().unwrap(); + let f2 = "1.0".parse::<Double>().unwrap(); + let expected = "0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "1.0".parse::<Double>().unwrap(); + let f2 = "0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).is_nan()); + assert_eq!(status, Status::INVALID_OP); + } + { + let f1 = "0.0".parse::<Double>().unwrap(); + let f2 = "0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).is_nan()); + assert_eq!(status, Status::INVALID_OP); + } + { + let f1 = Double::INFINITY; + let f2 = "1.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).is_nan()); + assert_eq!(status, Status::INVALID_OP); + } + { + let f1 = "-4.0".parse::<Double>().unwrap(); + let f2 = "-2.0".parse::<Double>().unwrap(); + let expected = "-0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "-4.0".parse::<Double>().unwrap(); + let f2 = "2.0".parse::<Double>().unwrap(); + let expected = "-0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + // Test E4M3FN mod where the LHS exponent is maxExponent (8) and the RHS is + // the max value whose exponent is minExponent (-6). This requires special + // logic in the mod implementation to prevent overflow to NaN. + let f1 = "0x1p8".parse::<Float8E4M3FN>().unwrap(); // 256 + let f2 = "0x1.ep-6".parse::<Float8E4M3FN>().unwrap(); // 0.029296875 + let expected = "0x1p-8".parse::<Float8E4M3FN>().unwrap(); // 0.00390625 + assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } +} + +#[test] +fn remainder() { + // Test Special Cases against each other and normal values. + + let p_inf = Single::INFINITY; + let m_inf = -Single::INFINITY; + let p_zero = Single::ZERO; + let m_zero = -Single::ZERO; + let qnan = Single::NAN; + let snan = "snan123".parse::<Single>().unwrap(); + let p_normal_value = "0x1p+0".parse::<Single>().unwrap(); + let m_normal_value = "-0x1p+0".parse::<Single>().unwrap(); + let p_largest_value = Single::largest(); + let m_largest_value = -Single::largest(); + let p_smallest_value = Single::SMALLEST; + let m_smallest_value = -Single::SMALLEST; + let p_smallest_normalized = Single::smallest_normalized(); + let m_smallest_normalized = -Single::smallest_normalized(); + + let p_val1 = "0x1.fffffep+126".parse::<Single>().unwrap(); + let m_val1 = "-0x1.fffffep+126".parse::<Single>().unwrap(); + let p_val2 = "0x1.fffffep-126".parse::<Single>().unwrap(); + let m_val2 = "-0x1.fffffep-126".parse::<Single>().unwrap(); + let p_val3 = "0x1p-125".parse::<Single>().unwrap(); + let m_val3 = "-0x1p-125".parse::<Single>().unwrap(); + let p_val4 = "0x1p+127".parse::<Single>().unwrap(); + let m_val4 = "-0x1p+127".parse::<Single>().unwrap(); + let p_val5 = "1.5".parse::<Single>().unwrap(); + let m_val5 = "-1.5".parse::<Single>().unwrap(); + let p_val6 = "1".parse::<Single>().unwrap(); + let m_val6 = "-1".parse::<Single>().unwrap(); + + let special_cases = [ + (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, qnan, "nan", Status::OK, Category::NaN), + (p_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_inf, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), + (p_inf, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, qnan, "nan", Status::OK, Category::NaN), + (m_inf, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_inf, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), + (m_inf, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), + (p_zero, p_inf, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_inf, "0x0p+0", Status::OK, Category::Zero), + (p_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_zero, qnan, "nan", Status::OK, Category::NaN), + (p_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (p_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (m_zero, p_inf, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_inf, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_zero, qnan, "nan", Status::OK, Category::NaN), + (m_zero, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (m_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (qnan, p_inf, "nan", Status::OK, Category::NaN), + (qnan, m_inf, "nan", Status::OK, Category::NaN), + (qnan, p_zero, "nan", Status::OK, Category::NaN), + (qnan, m_zero, "nan", Status::OK, Category::NaN), + (qnan, qnan, "nan", Status::OK, Category::NaN), + (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), + (qnan, p_normal_value, "nan", Status::OK, Category::NaN), + (qnan, m_normal_value, "nan", Status::OK, Category::NaN), + (qnan, p_largest_value, "nan", Status::OK, Category::NaN), + (qnan, m_largest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), + (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), + (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), + (snan, p_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_inf, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_zero, "nan123", Status::INVALID_OP, Category::NaN), + (snan, qnan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, snan, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_normal_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_largest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_value, "nan123", Status::INVALID_OP, Category::NaN), + (snan, p_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (snan, m_smallest_normalized, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_inf, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, m_inf, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_normal_value, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_normal_value, qnan, "nan", Status::OK, Category::NaN), + (p_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, p_largest_value, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, m_largest_value, "0x1p+0", Status::OK, Category::Normal), + (p_normal_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (p_normal_value, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (m_normal_value, p_inf, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, m_inf, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_normal_value, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_normal_value, qnan, "nan", Status::OK, Category::NaN), + (m_normal_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_normal_value, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (m_normal_value, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (m_normal_value, p_largest_value, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, m_largest_value, "-0x1p+0", Status::OK, Category::Normal), + (m_normal_value, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_normal_value, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_normal_value, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (m_normal_value, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (p_largest_value, p_inf, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, m_inf, "0x1.fffffep+127", Status::OK, Category::Normal), + (p_largest_value, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_largest_value, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_largest_value, qnan, "nan", Status::OK, Category::NaN), + (p_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_largest_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (p_largest_value, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (m_largest_value, p_inf, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, m_inf, "-0x1.fffffep+127", Status::OK, Category::Normal), + (m_largest_value, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_largest_value, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_largest_value, qnan, "nan", Status::OK, Category::NaN), + (m_largest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_largest_value, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, p_largest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, m_largest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (m_largest_value, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, p_inf, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, m_inf, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_smallest_value, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, p_largest_value, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, m_largest_value, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_value, p_smallest_normalized, "0x1p-149", Status::OK, Category::Normal), + (p_smallest_value, m_smallest_normalized, "0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, p_inf, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, m_inf, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_smallest_value, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_value, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, p_largest_value, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, m_largest_value, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_value, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_value, p_smallest_normalized, "-0x1p-149", Status::OK, Category::Normal), + (m_smallest_value, m_smallest_normalized, "-0x1p-149", Status::OK, Category::Normal), + (p_smallest_normalized, p_inf, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, m_inf, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_smallest_normalized, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (p_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, p_largest_value, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, m_largest_value, "0x1p-126", Status::OK, Category::Normal), + (p_smallest_normalized, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_normalized, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (p_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, p_inf, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, m_inf, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, p_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_smallest_normalized, m_zero, "nan", Status::INVALID_OP, Category::NaN), + (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), + (m_smallest_normalized, snan, "nan123", Status::INVALID_OP, Category::NaN), + (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, p_largest_value, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, m_largest_value, "-0x1p-126", Status::OK, Category::Normal), + (m_smallest_normalized, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (m_smallest_normalized, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), + (p_val1, p_val1, "0x0p+0", Status::OK, Category::Zero), + (p_val1, m_val1, "0x0p+0", Status::OK, Category::Zero), + (p_val1, p_val2, "0x0p+0", Status::OK, Category::Zero), + (p_val1, m_val2, "0x0p+0", Status::OK, Category::Zero), + (p_val1, p_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val1, m_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val1, p_val4, "-0x1p+103", Status::OK, Category::Normal), + (p_val1, m_val4, "-0x1p+103", Status::OK, Category::Normal), + (p_val1, p_val5, "0x0p+0", Status::OK, Category::Zero), + (p_val1, m_val5, "0x0p+0", Status::OK, Category::Zero), + (p_val1, p_val6, "0x0p+0", Status::OK, Category::Zero), + (p_val1, m_val6, "0x0p+0", Status::OK, Category::Zero), + (m_val1, p_val1, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, m_val1, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, p_val2, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, m_val2, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, p_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, m_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, p_val4, "0x1p+103", Status::OK, Category::Normal), + (m_val1, m_val4, "0x1p+103", Status::OK, Category::Normal), + (m_val1, p_val5, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, m_val5, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, p_val6, "-0x0p+0", Status::OK, Category::Zero), + (m_val1, m_val6, "-0x0p+0", Status::OK, Category::Zero), + (p_val2, p_val1, "0x1.fffffep-126", Status::OK, Category::Normal), + (p_val2, m_val1, "0x1.fffffep-126", Status::OK, Category::Normal), + (p_val2, p_val2, "0x0p+0", Status::OK, Category::Zero), + (p_val2, m_val2, "0x0p+0", Status::OK, Category::Zero), + (p_val2, p_val3, "-0x0.000002p-126", Status::OK, Category::Normal), + (p_val2, m_val3, "-0x0.000002p-126", Status::OK, Category::Normal), + (p_val2, p_val4, "0x1.fffffep-126", Status::OK, Category::Normal), + (p_val2, m_val4, "0x1.fffffep-126", Status::OK, Category::Normal), + (p_val2, p_val5, "0x1.fffffep-126", Status::OK, Category::Normal), + (p_val2, m_val5, "0x1.fffffep-126", Status::OK, Category::Normal), + (p_val2, p_val6, "0x1.fffffep-126", Status::OK, Category::Normal), + (p_val2, m_val6, "0x1.fffffep-126", Status::OK, Category::Normal), + (m_val2, p_val1, "-0x1.fffffep-126", Status::OK, Category::Normal), + (m_val2, m_val1, "-0x1.fffffep-126", Status::OK, Category::Normal), + (m_val2, p_val2, "-0x0p+0", Status::OK, Category::Zero), + (m_val2, m_val2, "-0x0p+0", Status::OK, Category::Zero), + (m_val2, p_val3, "0x0.000002p-126", Status::OK, Category::Normal), + (m_val2, m_val3, "0x0.000002p-126", Status::OK, Category::Normal), + (m_val2, p_val4, "-0x1.fffffep-126", Status::OK, Category::Normal), + (m_val2, m_val4, "-0x1.fffffep-126", Status::OK, Category::Normal), + (m_val2, p_val5, "-0x1.fffffep-126", Status::OK, Category::Normal), + (m_val2, m_val5, "-0x1.fffffep-126", Status::OK, Category::Normal), + (m_val2, p_val6, "-0x1.fffffep-126", Status::OK, Category::Normal), + (m_val2, m_val6, "-0x1.fffffep-126", Status::OK, Category::Normal), + (p_val3, p_val1, "0x1p-125", Status::OK, Category::Normal), + (p_val3, m_val1, "0x1p-125", Status::OK, Category::Normal), + (p_val3, p_val2, "0x0.000002p-126", Status::OK, Category::Normal), + (p_val3, m_val2, "0x0.000002p-126", Status::OK, Category::Normal), + (p_val3, p_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val3, m_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val3, p_val4, "0x1p-125", Status::OK, Category::Normal), + (p_val3, m_val4, "0x1p-125", Status::OK, Category::Normal), + (p_val3, p_val5, "0x1p-125", Status::OK, Category::Normal), + (p_val3, m_val5, "0x1p-125", Status::OK, Category::Normal), + (p_val3, p_val6, "0x1p-125", Status::OK, Category::Normal), + (p_val3, m_val6, "0x1p-125", Status::OK, Category::Normal), + (m_val3, p_val1, "-0x1p-125", Status::OK, Category::Normal), + (m_val3, m_val1, "-0x1p-125", Status::OK, Category::Normal), + (m_val3, p_val2, "-0x0.000002p-126", Status::OK, Category::Normal), + (m_val3, m_val2, "-0x0.000002p-126", Status::OK, Category::Normal), + (m_val3, p_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val3, m_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val3, p_val4, "-0x1p-125", Status::OK, Category::Normal), + (m_val3, m_val4, "-0x1p-125", Status::OK, Category::Normal), + (m_val3, p_val5, "-0x1p-125", Status::OK, Category::Normal), + (m_val3, m_val5, "-0x1p-125", Status::OK, Category::Normal), + (m_val3, p_val6, "-0x1p-125", Status::OK, Category::Normal), + (m_val3, m_val6, "-0x1p-125", Status::OK, Category::Normal), + (p_val4, p_val1, "0x1p+103", Status::OK, Category::Normal), + (p_val4, m_val1, "0x1p+103", Status::OK, Category::Normal), + (p_val4, p_val2, "0x0.002p-126", Status::OK, Category::Normal), + (p_val4, m_val2, "0x0.002p-126", Status::OK, Category::Normal), + (p_val4, p_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val4, m_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val4, p_val4, "0x0p+0", Status::OK, Category::Zero), + (p_val4, m_val4, "0x0p+0", Status::OK, Category::Zero), + (p_val4, p_val5, "0.5", Status::OK, Category::Normal), + (p_val4, m_val5, "0.5", Status::OK, Category::Normal), + (p_val4, p_val6, "0x0p+0", Status::OK, Category::Zero), + (p_val4, m_val6, "0x0p+0", Status::OK, Category::Zero), + (m_val4, p_val1, "-0x1p+103", Status::OK, Category::Normal), + (m_val4, m_val1, "-0x1p+103", Status::OK, Category::Normal), + (m_val4, p_val2, "-0x0.002p-126", Status::OK, Category::Normal), + (m_val4, m_val2, "-0x0.002p-126", Status::OK, Category::Normal), + (m_val4, p_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val4, m_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val4, p_val4, "-0x0p+0", Status::OK, Category::Zero), + (m_val4, m_val4, "-0x0p+0", Status::OK, Category::Zero), + (m_val4, p_val5, "-0.5", Status::OK, Category::Normal), + (m_val4, m_val5, "-0.5", Status::OK, Category::Normal), + (m_val4, p_val6, "-0x0p+0", Status::OK, Category::Zero), + (m_val4, m_val6, "-0x0p+0", Status::OK, Category::Zero), + (p_val5, p_val1, "1.5", Status::OK, Category::Normal), + (p_val5, m_val1, "1.5", Status::OK, Category::Normal), + (p_val5, p_val2, "0x0.00006p-126", Status::OK, Category::Normal), + (p_val5, m_val2, "0x0.00006p-126", Status::OK, Category::Normal), + (p_val5, p_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val5, m_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val5, p_val4, "1.5", Status::OK, Category::Normal), + (p_val5, m_val4, "1.5", Status::OK, Category::Normal), + (p_val5, p_val5, "0x0p+0", Status::OK, Category::Zero), + (p_val5, m_val5, "0x0p+0", Status::OK, Category::Zero), + (p_val5, p_val6, "-0.5", Status::OK, Category::Normal), + (p_val5, m_val6, "-0.5", Status::OK, Category::Normal), + (m_val5, p_val1, "-1.5", Status::OK, Category::Normal), + (m_val5, m_val1, "-1.5", Status::OK, Category::Normal), + (m_val5, p_val2, "-0x0.00006p-126", Status::OK, Category::Normal), + (m_val5, m_val2, "-0x0.00006p-126", Status::OK, Category::Normal), + (m_val5, p_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val5, m_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val5, p_val4, "-1.5", Status::OK, Category::Normal), + (m_val5, m_val4, "-1.5", Status::OK, Category::Normal), + (m_val5, p_val5, "-0x0p+0", Status::OK, Category::Zero), + (m_val5, m_val5, "-0x0p+0", Status::OK, Category::Zero), + (m_val5, p_val6, "0.5", Status::OK, Category::Normal), + (m_val5, m_val6, "0.5", Status::OK, Category::Normal), + (p_val6, p_val1, "0x1p+0", Status::OK, Category::Normal), + (p_val6, m_val1, "0x1p+0", Status::OK, Category::Normal), + (p_val6, p_val2, "0x0.00004p-126", Status::OK, Category::Normal), + (p_val6, m_val2, "0x0.00004p-126", Status::OK, Category::Normal), + (p_val6, p_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val6, m_val3, "0x0p+0", Status::OK, Category::Zero), + (p_val6, p_val4, "0x1p+0", Status::OK, Category::Normal), + (p_val6, m_val4, "0x1p+0", Status::OK, Category::Normal), + (p_val6, p_val5, "-0.5", Status::OK, Category::Normal), + (p_val6, m_val5, "-0.5", Status::OK, Category::Normal), + (p_val6, p_val6, "0x0p+0", Status::OK, Category::Zero), + (p_val6, m_val6, "0x0p+0", Status::OK, Category::Zero), + (m_val6, p_val1, "-0x1p+0", Status::OK, Category::Normal), + (m_val6, m_val1, "-0x1p+0", Status::OK, Category::Normal), + (m_val6, p_val2, "-0x0.00004p-126", Status::OK, Category::Normal), + (m_val6, m_val2, "-0x0.00004p-126", Status::OK, Category::Normal), + (m_val6, p_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val6, m_val3, "-0x0p+0", Status::OK, Category::Zero), + (m_val6, p_val4, "-0x1p+0", Status::OK, Category::Normal), + (m_val6, m_val4, "-0x1p+0", Status::OK, Category::Normal), + (m_val6, p_val5, "0.5", Status::OK, Category::Normal), + (m_val6, m_val5, "0.5", Status::OK, Category::Normal), + (m_val6, p_val6, "-0x0p+0", Status::OK, Category::Zero), + (m_val6, m_val6, "-0x0p+0", Status::OK, Category::Zero), + ]; + + for case @ &(x, y, e_result, e_status, e_category) in &special_cases { + let status; + let result = unpack!(status=, x.ieee_rem(y)); + assert_eq!(e_status, status); + assert_eq!(e_category, result.category()); + assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()), "result = {result:?}, case = {case:?}"); + } + + let mut status; + { + let f1 = "0x1.3333333333333p-2".parse::<Double>().unwrap(); // 0.3 + let f2 = "0x1.47ae147ae147bp-7".parse::<Double>().unwrap(); // 0.01 + let expected = "-0x1.4p-56".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1.ieee_rem(f2)).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "0x1p64".parse::<Double>().unwrap(); // 1.8446744073709552e19 + let f2 = "1.5".parse::<Double>().unwrap(); + let expected = "-0.5".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1.ieee_rem(f2)).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "0x1p1000".parse::<Double>().unwrap(); + let f2 = "0x1p-1000".parse::<Double>().unwrap(); + let expected = "0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1.ieee_rem(f2)).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = Double::INFINITY; + let f2 = "1.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1.ieee_rem(f2)).is_nan()); + assert_eq!(status, Status::INVALID_OP); + } + { + let f1 = "-4.0".parse::<Double>().unwrap(); + let f2 = "-2.0".parse::<Double>().unwrap(); + let expected = "-0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1.ieee_rem(f2)).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + { + let f1 = "-4.0".parse::<Double>().unwrap(); + let f2 = "2.0".parse::<Double>().unwrap(); + let expected = "-0.0".parse::<Double>().unwrap(); + assert!(unpack!(status=, f1.ieee_rem(f2)).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } +} + +#[test] +fn x87_largest() { + assert!(X87DoubleExtended::largest().is_largest()); +} + +#[test] +fn x87_next() { + assert_eq!("-1.0".parse::<X87DoubleExtended>().unwrap().next_up().value.ilogb(), -1); +} + +#[test] +fn convert_e4m3fn_to_e5m2() { + let mut status; + let mut loses_info = false; + + let test = "1.0".parse::<Float8E4M3FN>().unwrap(); + let test: Float8E5M2 = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(1.0, test.to_f32()); + assert!(!loses_info); + assert_eq!(status, Status::OK); + + let test = "0.0".parse::<Float8E4M3FN>().unwrap(); + let test: Float8E5M2 = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0.0, test.to_f32()); + assert!(!loses_info); + assert_eq!(status, Status::OK); + + let test = "0x1.2p0".parse::<Float8E4M3FN>().unwrap(); // 1.125 + let test: Float8E5M2 = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(/* 0x1.0p0 */ 1.0, test.to_f32()); + assert!(loses_info); + assert_eq!(status, Status::INEXACT); + + let test = "0x1.6p0".parse::<Float8E4M3FN>().unwrap(); // 1.375 + let test: Float8E5M2 = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(/* 0x1.8p0 */ 1.5, test.to_f32()); + assert!(loses_info); + assert_eq!(status, Status::INEXACT); + + // Convert E4M3 denormal to E5M2 normal. Should not be truncated, despite the + // destination format having one fewer significand bit + let test = "0x1.Cp-7".parse::<Float8E4M3FN>().unwrap(); + let test: Float8E5M2 = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(/* 0x1.Cp-7 */ 0.013671875, test.to_f32()); + assert!(!loses_info); + assert_eq!(status, Status::OK); + + // Test convert from NaN + let test = "nan".parse::<Float8E4M3FN>().unwrap(); + let test: Float8E5M2 = unpack!(status=, test.convert(&mut loses_info)); + assert!(test.to_f32().is_nan()); + assert!(!loses_info); + assert_eq!(status, Status::OK); +} + +#[test] +fn convert_e5m2_to_e4m3fn() { + let mut status; + let mut loses_info = false; + + let test = "1.0".parse::<Float8E5M2>().unwrap(); + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(1.0, test.to_f32()); + assert!(!loses_info); + assert_eq!(status, Status::OK); + + let test = "0.0".parse::<Float8E5M2>().unwrap(); + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0.0, test.to_f32()); + assert!(!loses_info); + assert_eq!(status, Status::OK); + + let test = "0x1.Cp8".parse::<Float8E5M2>().unwrap(); // 448 + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(/* 0x1.Cp8 */ 448.0, test.to_f32()); + assert!(!loses_info); + assert_eq!(status, Status::OK); + + // Test overflow + let test = "0x1.0p9".parse::<Float8E5M2>().unwrap(); // 512 + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert!(test.to_f32().is_nan()); + assert!(loses_info); + assert_eq!(status, Status::OVERFLOW | Status::INEXACT); + + // Test underflow + let test = "0x1.0p-10".parse::<Float8E5M2>().unwrap(); + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0., test.to_f32()); + assert!(loses_info); + assert_eq!(status, Status::UNDERFLOW | Status::INEXACT); + + // Test rounding up to smallest denormal number + let test = "0x1.8p-10".parse::<Float8E5M2>().unwrap(); + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(/* 0x1.0p-9 */ 0.001953125, test.to_f32()); + assert!(loses_info); + assert_eq!(status, Status::UNDERFLOW | Status::INEXACT); + + // Testing inexact rounding to denormal number + let test = "0x1.8p-9".parse::<Float8E5M2>().unwrap(); + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(/* 0x1.0p-8 */ 0.00390625, test.to_f32()); + assert!(loses_info); + assert_eq!(status, Status::UNDERFLOW | Status::INEXACT); + + let nan = "nan".parse::<Float8E4M3FN>().unwrap(); + + // Testing convert from Inf + let test = "inf".parse::<Float8E5M2>().unwrap(); + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert!(test.to_f32().is_nan()); + assert!(loses_info); + assert_eq!(status, Status::INEXACT); + assert!(test.bitwise_eq(nan)); + + // Testing convert from quiet NaN + let test = "nan".parse::<Float8E5M2>().unwrap(); + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert!(test.to_f32().is_nan()); + assert!(loses_info); + assert_eq!(status, Status::OK); + assert!(test.bitwise_eq(nan)); + + // Testing convert from signaling NaN + let test = "snan".parse::<Float8E5M2>().unwrap(); + let test: Float8E4M3FN = unpack!(status=, test.convert(&mut loses_info)); + assert!(test.to_f32().is_nan()); + assert!(loses_info); + assert_eq!(status, Status::INVALID_OP); + assert!(test.bitwise_eq(nan)); +} + +#[test] +fn float8e4m3fn_infinity() { + let t = Float8E4M3FN::INFINITY; + assert!(t.is_nan()); + assert!(!t.is_infinite()); +} + +#[test] +fn float8e4m3fn_from_string() { + // Exactly representable + assert_eq!(448.0, "448".parse::<Float8E4M3FN>().unwrap().to_f64()); + // Round down to maximum value + assert_eq!(448.0, "464".parse::<Float8E4M3FN>().unwrap().to_f64()); + // Round up, causing overflow to NaN + assert!("465".parse::<Float8E4M3FN>().unwrap().is_nan()); + // Overflow without rounding + assert!("480".parse::<Float8E4M3FN>().unwrap().is_nan()); + // Inf converted to NaN + assert!("inf".parse::<Float8E4M3FN>().unwrap().is_nan()); + // NaN converted to NaN + assert!("nan".parse::<Float8E4M3FN>().unwrap().is_nan()); +} + +#[test] +fn float8e4m3fn_add() { + let qnan = Float8E4M3FN::NAN; + + let from_str = |s: &str| s.parse::<Float8E4M3FN>().unwrap(); + + let addition_tests = [ + // Test addition operations involving NaN, overflow, and the max E4M3 + // value (448) because E4M3 differs from IEEE-754 types in these regards + (from_str("448"), from_str("16"), "448", Status::INEXACT, Category::Normal, Round::NearestTiesToEven), + ( + from_str("448"), + from_str("18"), + "NaN", + Status::OVERFLOW | Status::INEXACT, + Category::NaN, + Round::NearestTiesToEven, + ), + ( + from_str("448"), + from_str("32"), + "NaN", + Status::OVERFLOW | Status::INEXACT, + Category::NaN, + Round::NearestTiesToEven, + ), + ( + from_str("-448"), + from_str("-32"), + "-NaN", + Status::OVERFLOW | Status::INEXACT, + Category::NaN, + Round::NearestTiesToEven, + ), + (qnan, from_str("-448"), "NaN", Status::OK, Category::NaN, Round::NearestTiesToEven), + (from_str("448"), from_str("-32"), "416", Status::OK, Category::Normal, Round::NearestTiesToEven), + (from_str("448"), from_str("0"), "448", Status::OK, Category::Normal, Round::NearestTiesToEven), + (from_str("448"), from_str("32"), "448", Status::INEXACT, Category::Normal, Round::TowardZero), + (from_str("448"), from_str("448"), "448", Status::INEXACT, Category::Normal, Round::TowardZero), + ]; + + for case @ &(x, y, e_result, e_status, e_category, round) in &addition_tests { + let status; + let result = unpack!(status=, x.add_r(y, round)); + assert_eq!(e_status, status); + assert_eq!(e_category, result.category()); + assert!(result.bitwise_eq(e_result.parse::<Float8E4M3FN>().unwrap()), "result = {result:?}, case = {case:?}"); + } +} + +#[test] +fn float8e4m3fn_divide_by_zero() { + let x = "1".parse::<Float8E4M3FN>().unwrap(); + let zero = "0".parse::<Float8E4M3FN>().unwrap(); + let status; + assert!(unpack!(status=, x / zero).is_nan()); + assert_eq!(status, Status::DIV_BY_ZERO); +} + +#[test] +fn float8e4m3fn_next() { + let mut status; + + // nextUp on positive numbers + for i in 0..127 { + let test = Float8E4M3FN::from_bits(i); + let expected = Float8E4M3FN::from_bits(i + 1); + assert!(unpack!(status=, test.next_up()).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + + // nextUp on negative zero + let test = -Float8E4M3FN::ZERO; + let expected = Float8E4M3FN::SMALLEST; + assert!(unpack!(status=, test.next_up()).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + + // nextUp on negative nonzero numbers + for i in 129..255 { + let test = Float8E4M3FN::from_bits(i); + let expected = Float8E4M3FN::from_bits(i - 1); + assert!(unpack!(status=, test.next_up()).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + + // nextUp on NaN + let test = Float8E4M3FN::qnan(None); + let expected = Float8E4M3FN::qnan(None); + assert!(unpack!(status=, test.next_up()).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + + // nextDown on positive nonzero finite numbers + for i in 1..127 { + let test = Float8E4M3FN::from_bits(i); + let expected = Float8E4M3FN::from_bits(i - 1); + assert!(unpack!(status=, test.next_down()).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + + // nextDown on positive zero + let test = -Float8E4M3FN::ZERO; + let expected = -Float8E4M3FN::SMALLEST; + assert!(unpack!(status=, test.next_down()).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + + // nextDown on negative finite numbers + for i in 128..255 { + let test = Float8E4M3FN::from_bits(i); + let expected = Float8E4M3FN::from_bits(i + 1); + assert!(unpack!(status=, test.next_down()).bitwise_eq(expected)); + assert_eq!(status, Status::OK); + } + + // nextDown on NaN + let test = Float8E4M3FN::qnan(None); + let expected = Float8E4M3FN::qnan(None); + assert!(unpack!(status=, test.next_down()).bitwise_eq(expected)); + assert_eq!(status, Status::OK); +} + +#[test] +fn float8e4m3fn_exhaustive() { + // Test each of the 256 Float8E4M3FN values. + for i in 0..=u8::MAX { + let test = Float8E4M3FN::from_bits(i.into()); + + // isLargest + if i == 126 || i == 254 { + assert!(test.is_largest()); + assert_eq!(test.abs().to_f64(), 448.); + } else { + assert!(!test.is_largest()); + } + + // isSmallest + if i == 1 || i == 129 { + assert!(test.is_smallest()); + assert_eq!(test.abs().to_f64(), /* 0x1p-9 */ 0.001953125); + } else { + assert!(!test.is_smallest()); + } + + // convert to BFloat + let status; + let mut loses_info = false; + let test2: BFloat = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(status, Status::OK); + assert!(!loses_info); + if i == 127 || i == 255 { + assert!(test2.is_nan()); + } else { + assert_eq!(test.to_f32(), test2.to_f32()); + } + + // bitcastToAPInt + assert_eq!(u128::from(i), test.to_bits()); + } +} + +#[test] +fn float8e4m3fn_exhaustive_pair() { + // Test each pair of Float8E4M3FN values. + for i in 0..=u8::MAX { + for j in 0..=u8::MAX { + let x = Float8E4M3FN::from_bits(i.into()); + let y = Float8E4M3FN::from_bits(j.into()); + + let mut loses_info = false; + let x16: Half = x.convert(&mut loses_info).value; + assert!(!loses_info); + let y16: Half = y.convert(&mut loses_info).value; + assert!(!loses_info); + + // Add + let z = (x + y).value; + let z16 = (x16 + y16).value; + assert!(z.bitwise_eq(z16.convert(&mut loses_info).value), "i={i}, j={j}"); + + // Subtract + let z = (x - y).value; + let z16 = (x16 - y16).value; + assert!(z.bitwise_eq(z16.convert(&mut loses_info).value), "i={i}, j={j}"); + + // Multiply + let z = (x * y).value; + let z16 = (x16 * y16).value; + assert!(z.bitwise_eq(z16.convert(&mut loses_info).value), "i={i}, j={j}"); + + // Divide + let z = (x / y).value; + let z16 = (x16 / y16).value; + assert!(z.bitwise_eq(z16.convert(&mut loses_info).value), "i={i}, j={j}"); + + // Mod + let z = (x % y).value; + let z16 = (x16 % y16).value; + assert!(z.bitwise_eq(z16.convert(&mut loses_info).value), "i={i}, j={j}"); + + // Remainder + let z = x.ieee_rem(y).value; + let z16 = x16.ieee_rem(y16).value; + assert!(z.bitwise_eq(z16.convert(&mut loses_info).value), "i={i}, j={j}"); + } + } +} + +#[test] +fn f8_to_string() { + for_each_float_type!(for<F: Float> test::<F>()); + fn test<F: Float>() { + if F::BITS != 8 { + return; + } + + // NOTE(eddyb) this was buggy upstream as it didn't test `F` but `Float8E5M2`, + // https://github.com/llvm/llvm-project/commit/6109e70c72fc5171d25c4467fc3cfe6eb2029f50 + // fixed it upstream so we've effectively backported that commit. + for i in 0..=u8::MAX { + let test = F::from_bits(i.into()); + let str = test.to_string(); + + if test.is_nan() { + assert_eq!(str, "NaN"); + } else { + assert!(test.bitwise_eq(str.parse::<F>().unwrap())); + } + } + } +} + +// HACK(eddyb) C`{FLT,DBL}_TRUE_MIN` / C++ `std::numeric_limits<T>::denorm_min` +// equivalents, for the two tests below, as Rust seems to lack anything like them, +// but their bit-patterns are thankfuly trivial, with the main caveat that they +// can't be `const` (subnormals and NaNs are banned from CTFE `{to,from}_bits`). +fn f64_smallest_subnormal() -> f64 { + f64::from_bits(1) +} +fn f32_smallest_subnormal() -> f32 { + f32::from_bits(1) +} + +#[test] +fn double_to_f64() { + let d_pos_zero = Double::from_f64(0.0); + assert!(Double::from_f64(d_pos_zero.to_f64()).is_pos_zero()); + let d_neg_zero = Double::from_f64(-0.0); + assert!(Double::from_f64(d_neg_zero.to_f64()).is_neg_zero()); + + let d_one = Double::from_f64(1.0); + assert_eq!(1.0, d_one.to_f64()); + let d_pos_largest = Double::largest(); + assert_eq!(f64::MAX, d_pos_largest.to_f64()); + let d_neg_largest = -Double::largest(); + assert_eq!(-f64::MAX, d_neg_largest.to_f64()); + let d_pos_smallest = Double::smallest_normalized(); + assert_eq!(f64::MIN_POSITIVE, d_pos_smallest.to_f64()); + let d_neg_smallest = -Double::smallest_normalized(); + assert_eq!(-f64::MIN_POSITIVE, d_neg_smallest.to_f64()); + + let d_smallest_denorm = Double::SMALLEST; + assert_eq!(f64_smallest_subnormal(), d_smallest_denorm.to_f64()); + let d_largest_denorm = "0x0.FFFFFFFFFFFFFp-1022".parse::<Double>().unwrap(); + assert_eq!(/*0x0.FFFFFFFFFFFFFp-1022*/ 2.225073858507201e-308, d_largest_denorm.to_f64()); + + let d_pos_inf = Double::INFINITY; + assert_eq!(f64::INFINITY, d_pos_inf.to_f64()); + let d_neg_inf = -Double::INFINITY; + assert_eq!(-f64::INFINITY, d_neg_inf.to_f64()); + let d_qnan = Double::qnan(None); + assert!(d_qnan.to_f64().is_nan()); +} + +#[test] +fn single_to_f64() { + let f_pos_zero = Single::from_f32(0.0); + assert!(Double::from_f64(f_pos_zero.to_f64()).is_pos_zero()); + let f_neg_zero = Single::from_f32(-0.0); + assert!(Double::from_f64(f_neg_zero.to_f64()).is_neg_zero()); + + let f_one = Single::from_f32(1.0); + assert_eq!(1.0, f_one.to_f64()); + let f_pos_largest = Single::largest(); + assert_eq!(f32::MAX as f64, f_pos_largest.to_f64()); + let f_neg_largest = -Single::largest(); + assert_eq!(-f32::MAX as f64, f_neg_largest.to_f64()); + let f_pos_smallest = Single::smallest_normalized(); + assert_eq!(f32::MIN_POSITIVE as f64, f_pos_smallest.to_f64()); + let f_neg_smallest = -Single::smallest_normalized(); + assert_eq!(-f32::MIN_POSITIVE as f64, f_neg_smallest.to_f64()); + + let f_smallest_denorm = Single::SMALLEST; + assert_eq!(f32_smallest_subnormal() as f64, f_smallest_denorm.to_f64()); + let f_largest_denorm = "0x0.FFFFFEp-126".parse::<Double>().unwrap(); + assert_eq!(/*0x0.FFFFFEp-126*/ 1.1754942106924411e-38, f_largest_denorm.to_f64()); + + let f_pos_inf = Single::INFINITY; + assert_eq!(f64::INFINITY, f_pos_inf.to_f64()); + let f_neg_inf = -Single::INFINITY; + assert_eq!(-f64::INFINITY, f_neg_inf.to_f64()); + let f_qnan = Single::qnan(None); + assert!(f_qnan.to_f64().is_nan()); + + let h_pos_zero = Half::ZERO; + assert!(Double::from_f64(h_pos_zero.to_f64()).is_pos_zero()); + let h_neg_zero = -Half::ZERO; + assert!(Double::from_f64(h_neg_zero.to_f64()).is_neg_zero()); +} + +#[test] +fn half_to_f64() { + let h_one = "1.0".parse::<Half>().unwrap(); + assert_eq!(1.0, h_one.to_f64()); + let h_pos_largest = Half::largest(); + assert_eq!(65504.0, h_pos_largest.to_f64()); + let h_neg_largest = -Half::largest(); + assert_eq!(-65504.0, h_neg_largest.to_f64()); + let h_pos_smallest = Half::smallest_normalized(); + assert_eq!(/*0x1.p-14*/ 6.103515625e-05, h_pos_smallest.to_f64()); + let h_neg_smallest = -Half::smallest_normalized(); + assert_eq!(/*-0x1.p-14*/ -6.103515625e-05, h_neg_smallest.to_f64()); + + let h_smallest_denorm = Half::SMALLEST; + assert_eq!(/*0x1.p-24*/ 5.960464477539063e-08, h_smallest_denorm.to_f64()); + let h_largest_denorm = "0x1.FFCp-14".parse::<Half>().unwrap(); + assert_eq!(/*0x1.FFCp-14*/ 0.00012201070785522461, h_largest_denorm.to_f64()); + + let h_pos_inf = Half::INFINITY; + assert_eq!(f64::INFINITY, h_pos_inf.to_f64()); + let h_neg_inf = -Half::INFINITY; + assert_eq!(-f64::INFINITY, h_neg_inf.to_f64()); + let h_qnan = Half::qnan(None); + assert!(h_qnan.to_f64().is_nan()); +} + +#[test] +fn bfloat_to_f64() { + let b_pos_zero = Half::ZERO; + assert!(Double::from_f64(b_pos_zero.to_f64()).is_pos_zero()); + let b_neg_zero = -Half::ZERO; + assert!(Double::from_f64(b_neg_zero.to_f64()).is_neg_zero()); + + let b_one = "1.0".parse::<BFloat>().unwrap(); + assert_eq!(1.0, b_one.to_f64()); + let b_pos_largest = BFloat::largest(); + assert_eq!(/*0x1.FEp127*/ 3.3895313892515355e+38, b_pos_largest.to_f64()); + let b_neg_largest = -BFloat::largest(); + assert_eq!(/*-0x1.FEp127*/ -3.3895313892515355e+38, b_neg_largest.to_f64()); + let b_pos_smallest = BFloat::smallest_normalized(); + assert_eq!(/*0x1.p-126*/ 1.1754943508222875e-38, b_pos_smallest.to_f64()); + let b_neg_smallest = -BFloat::smallest_normalized(); + assert_eq!(/*-0x1.p-126*/ -1.1754943508222875e-38, b_neg_smallest.to_f64()); + + let b_smallest_denorm = BFloat::SMALLEST; + assert_eq!(/*0x1.p-133*/ 9.183549615799121e-41, b_smallest_denorm.to_f64()); + let b_largest_denorm = "0x1.FCp-127".parse::<BFloat>().unwrap(); + assert_eq!(/*0x1.FCp-127*/ 1.1663108012064884e-38, b_largest_denorm.to_f64()); + + let b_pos_inf = BFloat::INFINITY; + assert_eq!(f64::INFINITY, b_pos_inf.to_f64()); + let b_neg_inf = -BFloat::INFINITY; + assert_eq!(-f64::INFINITY, b_neg_inf.to_f64()); + let b_qnan = BFloat::qnan(None); + assert!(b_qnan.to_f64().is_nan()); +} + +#[test] +fn float8e5m2_to_f64() { + let one = "1.0".parse::<Float8E5M2>().unwrap(); + assert_eq!(1.0, one.to_f64()); + let two = "2.0".parse::<Float8E5M2>().unwrap(); + assert_eq!(2.0, two.to_f64()); + let pos_largest = Float8E5M2::largest(); + assert_eq!(5.734400e+04, pos_largest.to_f64()); + let neg_largest = -Float8E5M2::largest(); + assert_eq!(-5.734400e+04, neg_largest.to_f64()); + let pos_smallest = Float8E5M2::smallest_normalized(); + assert_eq!(/* 0x1.p-14 */ 6.103515625e-05, pos_smallest.to_f64()); + let neg_smallest = -Float8E5M2::smallest_normalized(); + assert_eq!(/* -0x1.p-14 */ -6.103515625e-05, neg_smallest.to_f64()); + + let smallest_denorm = Float8E5M2::SMALLEST; + assert!(smallest_denorm.is_denormal()); + assert_eq!(/* 0x1p-16 */ 0.0000152587890625, smallest_denorm.to_f64()); + + let pos_inf = Float8E5M2::INFINITY; + assert_eq!(f64::INFINITY, pos_inf.to_f64()); + let neg_inf = -Float8E5M2::INFINITY; + assert_eq!(-f64::INFINITY, neg_inf.to_f64()); + let qnan = Float8E5M2::qnan(None); + assert!(qnan.to_f64().is_nan()); +} + +#[test] +fn float8e4m3fn_to_f64() { + let one = "1.0".parse::<Float8E4M3FN>().unwrap(); + assert_eq!(1.0, one.to_f64()); + let two = "2.0".parse::<Float8E4M3FN>().unwrap(); + assert_eq!(2.0, two.to_f64()); + let pos_largest = Float8E4M3FN::largest(); + assert_eq!(448., pos_largest.to_f64()); + let neg_largest = -Float8E4M3FN::largest(); + assert_eq!(-448., neg_largest.to_f64()); + let pos_smallest = Float8E4M3FN::smallest_normalized(); + assert_eq!(/* 0x1.p-6 */ 0.015625, pos_smallest.to_f64()); + let neg_smallest = -Float8E4M3FN::smallest_normalized(); + assert_eq!(/* -0x1.p-6 */ -0.015625, neg_smallest.to_f64()); + + let smallest_denorm = Float8E4M3FN::SMALLEST; + assert!(smallest_denorm.is_denormal()); + assert_eq!(/* 0x1p-9 */ 0.001953125, smallest_denorm.to_f64()); + + let qnan = Float8E4M3FN::qnan(None); + assert!(qnan.to_f64().is_nan()); +} + +#[test] +fn single_to_f32() { + let f_pos_zero = Single::from_f32(0.0); + assert!(Single::from_f32(f_pos_zero.to_f32()).is_pos_zero()); + let f_neg_zero = Single::from_f32(-0.0); + assert!(Single::from_f32(f_neg_zero.to_f32()).is_neg_zero()); + + let f_one = Single::from_f32(1.0); + assert_eq!(1.0, f_one.to_f32()); + let f_pos_largest = Single::largest(); + assert_eq!(f32::MAX, f_pos_largest.to_f32()); + let f_neg_largest = -Single::largest(); + assert_eq!(-f32::MAX, f_neg_largest.to_f32()); + let f_pos_smallest = Single::smallest_normalized(); + assert_eq!(f32::MIN_POSITIVE, f_pos_smallest.to_f32()); + let f_neg_smallest = -Single::smallest_normalized(); + assert_eq!(-f32::MIN_POSITIVE, f_neg_smallest.to_f32()); + + let f_smallest_denorm = Single::SMALLEST; + assert_eq!(f32_smallest_subnormal(), f_smallest_denorm.to_f32()); + let f_largest_denorm = "0x1.FFFFFEp-126".parse::<Single>().unwrap(); + assert_eq!(/*0x1.FFFFFEp-126*/ 2.3509885615147286e-38, f_largest_denorm.to_f32()); + + let f_pos_inf = Single::INFINITY; + assert_eq!(f32::INFINITY, f_pos_inf.to_f32()); + let f_neg_inf = -Single::INFINITY; + assert_eq!(-f32::INFINITY, f_neg_inf.to_f32()); + let f_qnan = Single::qnan(None); + assert!(f_qnan.to_f32().is_nan()); +} + +#[test] +fn half_to_f32() { + let h_pos_zero = Half::ZERO; + assert!(Single::from_f32(h_pos_zero.to_f32()).is_pos_zero()); + let h_neg_zero = -Half::ZERO; + assert!(Single::from_f32(h_neg_zero.to_f32()).is_neg_zero()); + + let h_one = "1.0".parse::<Half>().unwrap(); + assert_eq!(1.0, h_one.to_f32()); + let h_pos_largest = Half::largest(); + assert_eq!(/*0x1.FFCp15*/ 65504.0, h_pos_largest.to_f32()); + let h_neg_largest = -Half::largest(); + assert_eq!(/*-0x1.FFCp15*/ -65504.0, h_neg_largest.to_f32()); + let h_pos_smallest = Half::smallest_normalized(); + assert_eq!(/*0x1.p-14*/ 6.103515625e-05, h_pos_smallest.to_f32()); + let h_neg_smallest = -Half::smallest_normalized(); + assert_eq!(/*-0x1.p-14*/ -6.103515625e-05, h_neg_smallest.to_f32()); + + let h_smallest_denorm = Half::SMALLEST; + assert_eq!(/*0x1.p-24*/ 5.960464477539063e-08, h_smallest_denorm.to_f32()); + let h_largest_denorm = "0x1.FFCp-14".parse::<Half>().unwrap(); + assert_eq!(/*0x1.FFCp-14*/ 0.00012201070785522461, h_largest_denorm.to_f32()); + + let h_pos_inf = Half::INFINITY; + assert_eq!(f32::INFINITY, h_pos_inf.to_f32()); + let h_neg_inf = -Half::INFINITY; + assert_eq!(-f32::INFINITY, h_neg_inf.to_f32()); + let h_qnan = Half::qnan(None); + assert!(h_qnan.to_f32().is_nan()); +} + +#[test] +fn bfloat_to_f32() { + let b_pos_zero = BFloat::ZERO; + assert!(Single::from_f32(b_pos_zero.to_f32()).is_pos_zero()); + let b_neg_zero = -BFloat::ZERO; + assert!(Single::from_f32(b_neg_zero.to_f32()).is_neg_zero()); + + let b_one = "1.0".parse::<BFloat>().unwrap(); + assert_eq!(1.0, b_one.to_f32()); + let b_pos_largest = BFloat::largest(); + assert_eq!(/*0x1.FEp127*/ 3.3895313892515355e+38, b_pos_largest.to_f32()); + let b_neg_largest = -BFloat::largest(); + assert_eq!(/*-0x1.FEp127*/ -3.3895313892515355e+38, b_neg_largest.to_f32()); + let b_pos_smallest = BFloat::smallest_normalized(); + assert_eq!(/*0x1.p-126*/ 1.1754943508222875e-38, b_pos_smallest.to_f32()); + let b_neg_smallest = -BFloat::smallest_normalized(); + assert_eq!(/*-0x1.p-126*/ -1.1754943508222875e-38, b_neg_smallest.to_f32()); + + let b_smallest_denorm = BFloat::SMALLEST; + assert_eq!(/*0x1.p-133*/ 9.183549615799121e-41, b_smallest_denorm.to_f32()); + let b_largest_denorm = "0x1.FCp-127".parse::<BFloat>().unwrap(); + assert_eq!(/*0x1.FCp-127*/ 1.1663108012064884e-38, b_largest_denorm.to_f32()); + + let b_pos_inf = BFloat::INFINITY; + assert_eq!(f32::INFINITY, b_pos_inf.to_f32()); + let b_neg_inf = -BFloat::INFINITY; + assert_eq!(-f32::INFINITY, b_neg_inf.to_f32()); + let b_qnan = BFloat::qnan(None); + assert!(b_qnan.to_f32().is_nan()); +} + +#[test] +fn float8e5m2_to_f32() { + let pos_zero = Float8E5M2::ZERO; + assert!(Single::from_f32(pos_zero.to_f32()).is_pos_zero()); + let neg_zero = -Float8E5M2::ZERO; + assert!(Single::from_f32(neg_zero.to_f32()).is_neg_zero()); + + let one = "1.0".parse::<Float8E5M2>().unwrap(); + assert_eq!(1.0, one.to_f32()); + let two = "2.0".parse::<Float8E5M2>().unwrap(); + assert_eq!(2.0, two.to_f32()); + + let pos_largest = Float8E5M2::largest(); + assert_eq!(5.734400e+04, pos_largest.to_f32()); + let neg_largest = -Float8E5M2::largest(); + assert_eq!(-5.734400e+04, neg_largest.to_f32()); + let pos_smallest = Float8E5M2::smallest_normalized(); + assert_eq!(/* 0x1.p-14 */ 6.103515625e-05, pos_smallest.to_f32()); + let neg_smallest = -Float8E5M2::smallest_normalized(); + assert_eq!(/* -0x1.p-14 */ -6.103515625e-05, neg_smallest.to_f32()); + + let smallest_denorm = Float8E5M2::SMALLEST; + assert!(smallest_denorm.is_denormal()); + assert_eq!(/* 0x1.p-16 */ 0.0000152587890625, smallest_denorm.to_f32()); + + let pos_inf = Float8E5M2::INFINITY; + assert_eq!(f32::INFINITY, pos_inf.to_f32()); + let neg_inf = -Float8E5M2::INFINITY; + assert_eq!(-f32::INFINITY, neg_inf.to_f32()); + let qnan = Float8E5M2::qnan(None); + assert!(qnan.to_f32().is_nan()); +} + +#[test] +fn float8e4m3fn_to_f32() { + let pos_zero = Float8E4M3FN::ZERO; + assert!(Single::from_f32(pos_zero.to_f32()).is_pos_zero()); + let neg_zero = -Float8E4M3FN::ZERO; + assert!(Single::from_f32(neg_zero.to_f32()).is_neg_zero()); + + let one = "1.0".parse::<Float8E4M3FN>().unwrap(); + assert_eq!(1.0, one.to_f32()); + let two = "2.0".parse::<Float8E4M3FN>().unwrap(); + assert_eq!(2.0, two.to_f32()); + + let pos_largest = Float8E4M3FN::largest(); + assert_eq!(448., pos_largest.to_f32()); + let neg_largest = -Float8E4M3FN::largest(); + assert_eq!(-448.0, neg_largest.to_f32()); + let pos_smallest = Float8E4M3FN::smallest_normalized(); + assert_eq!(/* 0x1.p-6 */ 0.015625, pos_smallest.to_f32()); + let neg_smallest = -Float8E4M3FN::smallest_normalized(); + assert_eq!(/* -0x1.p-6 */ -0.015625, neg_smallest.to_f32()); + + let smallest_denorm = Float8E4M3FN::SMALLEST; + assert!(smallest_denorm.is_denormal()); + assert_eq!(/* 0x1.p-9 */ 0.001953125, smallest_denorm.to_f32()); + + let qnan = Float8E4M3FN::qnan(None); + assert!(qnan.to_f32().is_nan()); +} diff --git a/vendor/rustc_apfloat/tests/ppc.rs b/vendor/rustc_apfloat/tests/ppc.rs new file mode 100644 index 000000000..7da8f8ac3 --- /dev/null +++ b/vendor/rustc_apfloat/tests/ppc.rs @@ -0,0 +1,470 @@ +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()); +} |