diff --git a/src/ieee.rs b/src/ieee.rs index 6414463..4b8ee52 100644 --- a/src/ieee.rs +++ b/src/ieee.rs @@ -830,7 +830,18 @@ impl Float for IeeeFloat { // extended-precision calculation. if !self.is_finite_non_zero() || !multiplicand.is_finite_non_zero() || !addend.is_finite() { let mut status; - self = unpack!(status=, self.mul_r(multiplicand, round)); + if self.is_finite_non_zero() && multiplicand.is_finite_non_zero() { + // HACK(eddyb) this corresponds to the case where the C++ code + // (`multiplySpecials`) is effectively a noop: no multiplication + // is actually performed, because we're really only here to handle + // the "infinite/NaN `addend`" special-case, which needs to ignore + // the "finite * finite" multiplication entirely, instead of letting + // it e.g. overflow into infinity (and trample over `addend`). + assert!(!addend.is_finite()); + status = Status::OK; + } else { + self = unpack!(status=, self.mul_r(multiplicand, round)); + } // FS can only be Status::OK or Status::INVALID_OP. There is no more work // to do in the latter case. The IEEE-754R standard says it is diff --git a/tests/downstream.rs b/tests/downstream.rs index 100a7cf..19ee16a 100644 --- a/tests/downstream.rs +++ b/tests/downstream.rs @@ -42,3 +42,346 @@ fn fuzz_roundtrip_through_i128() { ); } } + +// `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 */), 0xff9e007f /* 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 */), + 0xff9e009e, /* NaN */ + ), + ((0x20007faa /* 1.0884262e-19 */, 0x9e00611e /* -6.796347e-21 */, 0x7faa0600 /* NaN */), 0x7faa0600 /* NaN */), + ( + (0x20007faa /* 1.0884262e-19 */, 0xaa069e1e /* -1.1956449e-13 */, 0xffffecff /* NaN */), + 0xffffecff, /* NaN */ + ), + ((0x20025eaa /* 1.104275e-19 */, 0x9e01033a /* -6.82987e-21 */, 0xff9e009e /* NaN */), 0xff9e009e /* 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 */), 0x7f8a063a /* 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 */), 0xff820000 /* NaN */), + ((0x6a017faa /* 3.9138577e+25 */, 0x00000070 /* 1.57e-43 */, 0xff80db03 /* NaN */), 0xff80db03 /* NaN */), + ((0x6a017faa /* 3.9138577e+25 */, 0x00000070 /* 1.57e-43 */, 0xff80db23 /* NaN */), 0xff80db23 /* 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 */), + 0xff9b0000, /* 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 */ + ), + 0xfff0001000000000, /* NaN */ + ), + ( + ( + 0x054040000100e213, /* 2.1855837639996873e-283 */ + 0xfbd8000000000000, /* -3.6544927542749997e+288 */ + 0xfff0ff1000000000, /* NaN */ + ), + 0xfff0ff1000000000, /* 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 */ + ), + 0xfff0000000e20000, /* 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()); + } +}