use super::*;
#[test]
fn test_float_to_fix_overflow_trips_when_enabled() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x30, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_W << 8); mmix.set_pc(0x100);
mmix.set_register(2, 1e20_f64.to_bits());
mmix.write_tetra(0x100, 0x05010002);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x30);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_W, 0);
}
#[test]
fn test_floating_overflow_trips_and_the_unenabled_inexact_still_sets_its_event_bit() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x50, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_O << 8); mmix.set_pc(0x100);
mmix.set_register(2, f64::MAX.to_bits());
mmix.set_register(3, f64::MAX.to_bits());
mmix.write_tetra(0x100, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x50);
let ra = mmix.get_special(SpecialReg::RA);
assert_eq!(ra & RA_O, 0, "O tripped: its own event bit stays clear");
assert_eq!(
ra & RA_X,
RA_X,
"X was raised but not enabled: it still sets its event bit"
);
}
#[test]
fn test_floating_underflow_trips_when_enabled() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x60, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_U << 8); mmix.set_pc(0x100);
mmix.set_register(2, f64::MIN_POSITIVE.to_bits());
mmix.set_register(3, f64::MIN_POSITIVE.to_bits());
mmix.write_tetra(0x100, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x60);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_U, 0);
}
#[test]
fn test_floating_divide_by_zero_trips_when_enabled() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x70, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_Z << 8); mmix.set_pc(0x100);
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0x100, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x70);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_Z, 0);
}
#[test]
fn test_floating_inexact_trips_when_enabled() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x80, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_X << 8); mmix.set_pc(0x100);
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 1e-30f64.to_bits());
mmix.write_tetra(0x100, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x80);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_X, 0);
}
#[test]
fn test_fcmp_less_than() {
let mut mmix = MMix::new();
mmix.set_register(2, 2.5f64.to_bits());
mmix.set_register(3, 5.0f64.to_bits());
mmix.write_tetra(0, 0x01010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1); }
#[test]
fn test_fcmp_greater_than() {
let mut mmix = MMix::new();
mmix.set_register(2, 10.0f64.to_bits());
mmix.set_register(3, 3.0f64.to_bits());
mmix.write_tetra(0, 0x01010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1); }
#[test]
fn test_fcmp_equal() {
let mut mmix = MMix::new();
mmix.set_register(2, 7.5f64.to_bits());
mmix.set_register(3, 7.5f64.to_bits());
mmix.write_tetra(0, 0x01010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); }
#[test]
fn test_fcmp_unordered() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::NAN.to_bits());
mmix.set_register(3, 5.0f64.to_bits());
mmix.write_tetra(0, 0x01010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_cmp_cmpu_signed_unsigned_divergence() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x8000000000000000);
mmix.set_register(3, 1);
mmix.write_tetra(0, 0x30010203); mmix.write_tetra(4, 0x32040203); assert!(mmix.execute_instruction());
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1); assert_eq!(mmix.get_register(4) as i64, 1); }
#[test]
fn test_feql() {
let mut mmix = MMix::new();
mmix.set_register(2, 4.0f64.to_bits());
mmix.set_register(3, 4.0f64.to_bits());
mmix.write_tetra(0, 0x03010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1); }
#[test]
fn test_feql_not_equal() {
let mut mmix = MMix::new();
mmix.set_register(2, 4.0f64.to_bits());
mmix.set_register(3, 5.0f64.to_bits());
mmix.write_tetra(0, 0x03010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); }
#[test]
fn test_fun() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::NAN.to_bits());
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x02010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1); }
#[test]
fn test_fun_ordered() {
let mut mmix = MMix::new();
mmix.set_register(2, 2.0f64.to_bits());
mmix.set_register(3, 3.0f64.to_bits());
mmix.write_tetra(0, 0x02010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); }
#[test]
fn test_fcmpe() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.01f64.to_bits());
mmix.set_register(2, 5.0f64.to_bits());
mmix.set_register(3, 5.001f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); }
#[test]
fn test_feqle() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.1f64.to_bits());
mmix.set_register(2, 10.0f64.to_bits());
mmix.set_register(3, 10.05f64.to_bits());
mmix.write_tetra(0, 0x13010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1); }
#[test]
fn test_fune() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.5f64.to_bits());
mmix.set_register(2, 7.0f64.to_bits());
mmix.set_register(3, 7.3f64.to_bits());
mmix.write_tetra(0, 0x12010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
}
#[test]
fn test_fadd() {
let mut mmix = MMix::new();
mmix.set_register(2, 2.5f64.to_bits());
mmix.set_register(3, 3.7f64.to_bits());
mmix.write_tetra(0, 0x04010203); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 6.2).abs() < 1e-10);
}
#[test]
fn test_fsub() {
let mut mmix = MMix::new();
mmix.set_register(2, 10.0f64.to_bits());
mmix.set_register(3, 3.5f64.to_bits());
mmix.write_tetra(0, 0x06010203); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 6.5).abs() < 1e-10);
}
#[test]
fn test_fmul() {
let mut mmix = MMix::new();
mmix.set_register(2, 4.0f64.to_bits());
mmix.set_register(3, 2.5f64.to_bits());
mmix.write_tetra(0, 0x10010203); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 10.0).abs() < 1e-10);
}
#[test]
fn test_fdiv() {
let mut mmix = MMix::new();
mmix.set_register(2, 15.0f64.to_bits());
mmix.set_register(3, 3.0f64.to_bits());
mmix.write_tetra(0, 0x14010203); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 5.0).abs() < 1e-10);
}
#[test]
fn test_frem() {
let mut mmix = MMix::new();
mmix.set_register(2, 7.5f64.to_bits());
mmix.set_register(3, 2.0f64.to_bits());
mmix.write_tetra(0, 0x16010203);
assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result + 0.5).abs() < 1e-10, "got {}", result);
}
#[test]
fn test_frem_zero_divisor_nan() {
let mut mmix = MMix::new();
mmix.set_register(2, 5.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x16010203);
assert!(mmix.execute_instruction());
assert!(f64::from_bits(mmix.get_register(1)).is_nan());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fsqrt() {
let mut mmix = MMix::new();
mmix.set_register(3, 16.0f64.to_bits());
mmix.write_tetra(0, 0x15010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 4.0).abs() < 1e-10);
}
#[test]
fn test_fint() {
let mut mmix = MMix::new();
mmix.set_register(3, 3.7f64.to_bits());
mmix.write_tetra(0, 0x17010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 4.0).abs() < 1e-10);
}
#[test]
fn test_fix() {
let mut mmix = MMix::new();
mmix.set_register(3, 42.9f64.to_bits());
mmix.write_tetra(0, 0x05010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 43);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_fix_trunc_mode() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 1 << RA_ROUND_SHIFT);
mmix.set_register(3, 42.9f64.to_bits());
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 42);
}
#[test]
fn test_fix_negative() {
let mut mmix = MMix::new();
mmix.set_register(3, (-17.8f64).to_bits());
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -18);
}
#[test]
fn test_fix_nan_raises_i_alone() {
let mut mmix = MMix::new();
mmix.set_register(3, f64::NAN.to_bits());
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
let ra = mmix.get_special(SpecialReg::RA);
assert_eq!(ra & RA_W, 0);
assert_ne!(ra & RA_I, 0);
assert_eq!(mmix.get_register(1), f64::NAN.to_bits());
}
#[test]
fn test_fixu() {
let mut mmix = MMix::new();
mmix.set_register(3, 99.5f64.to_bits());
mmix.write_tetra(0, 0x07010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 100);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_flot() {
let mut mmix = MMix::new();
mmix.set_register(3, 42);
mmix.write_tetra(0, 0x08010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 42.0).abs() < 1e-10);
}
#[test]
fn test_flot_negative() {
let mut mmix = MMix::new();
mmix.set_register(3, (-100i64) as u64);
mmix.write_tetra(0, 0x08010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - (-100.0)).abs() < 1e-10);
}
#[test]
fn test_floti() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x09010064); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 100.0).abs() < 1e-10);
}
#[test]
fn test_floti_immediate_is_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x090100FF);
assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 255.0).abs() < 1e-10);
}
#[test]
fn test_flotu() {
let mut mmix = MMix::new();
mmix.set_register(3, 1000);
mmix.write_tetra(0, 0x0A010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 1000.0).abs() < 1e-10);
}
#[test]
fn test_flotui() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x0B0100F4); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 244.0).abs() < 1e-10);
}
#[test]
fn test_sflot() {
let mut mmix = MMix::new();
mmix.set_register(3, 123);
mmix.write_tetra(0, 0x0C010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 123.0).abs() < 1e-5);
}
#[test]
fn test_sfloti() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x0D010040); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 64.0).abs() < 1e-5);
}
#[test]
fn test_sflotu() {
let mut mmix = MMix::new();
mmix.set_register(3, 777);
mmix.write_tetra(0, 0x0E010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 777.0).abs() < 1e-5);
}
#[test]
fn test_sflotui() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x0F0100FF); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 255.0).abs() < 1e-5);
}
#[test]
fn test_fint_round_near() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 0 << RA_ROUND_SHIFT); mmix.set_register(3, 3.7f64.to_bits());
mmix.write_tetra(0, 0x17010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 4.0).abs() < 1e-10);
}
#[test]
fn test_fint_round_off() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 1 << RA_ROUND_SHIFT);
mmix.set_register(3, 3.7f64.to_bits());
mmix.write_tetra(0, 0x17010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 3.0).abs() < 1e-10);
}
#[test]
fn test_fint_round_up() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT);
mmix.set_register(3, 3.2f64.to_bits());
mmix.write_tetra(0, 0x17010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 4.0).abs() < 1e-10);
}
#[test]
fn test_fint_round_down() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT);
mmix.set_register(3, 3.9f64.to_bits());
mmix.write_tetra(0, 0x17010003); assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result - 3.0).abs() < 1e-10);
}
#[test]
fn test_fint_round_down_negative() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT);
mmix.set_register(3, (-3.2f64).to_bits());
mmix.write_tetra(0, 0x17010003);
assert!(mmix.execute_instruction());
let result = f64::from_bits(mmix.get_register(1));
assert!((result + 4.0).abs() < 1e-10);
}
#[test]
fn test_fint_inexact_flag() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 0 << RA_ROUND_SHIFT);
mmix.set_register(3, 3.5f64.to_bits());
mmix.write_tetra(0, 0x17010003);
assert!(mmix.execute_instruction());
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_stsf() {
let mut mmix = MMix::new();
let f64_value = std::f64::consts::PI;
mmix.set_register(1, f64_value.to_bits());
mmix.set_register(2, 1000);
mmix.set_register(3, 8);
mmix.write_tetra(0, 0xB0010203); assert!(mmix.execute_instruction());
let stored_tetra = mmix.read_tetra(1008);
let f32_value = f32::from_bits(stored_tetra);
assert!((f32_value - std::f32::consts::PI).abs() < 1e-5);
}
#[test]
fn test_stsfi() {
let mut mmix = MMix::new();
let f64_value = std::f64::consts::E;
mmix.set_register(1, f64_value.to_bits());
mmix.set_register(2, 2000);
mmix.write_tetra(0, 0xB1010210); assert!(mmix.execute_instruction());
let stored_tetra = mmix.read_tetra(2016);
let f32_value = f32::from_bits(stored_tetra);
assert!((f32_value - std::f32::consts::E).abs() < 1e-5);
}
#[test]
fn test_fadd_overflow_sets_o() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::MAX.to_bits());
mmix.set_register(3, f64::MAX.to_bits());
mmix.write_tetra(0, 0x04010203); assert!(mmix.execute_instruction());
assert!(f64::from_bits(mmix.get_register(1)).is_infinite());
assert!((mmix.get_special(SpecialReg::RA) & RA_O) != 0);
}
#[test]
fn test_fsub_inf_minus_inf_sets_i() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::INFINITY.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x06010203); assert!(mmix.execute_instruction());
assert!(f64::from_bits(mmix.get_register(1)).is_nan());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fmul_zero_times_inf_sets_i() {
let mut mmix = MMix::new();
mmix.set_register(2, 0.0f64.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x10010203); assert!(mmix.execute_instruction());
assert!(f64::from_bits(mmix.get_register(1)).is_nan());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fdiv_by_zero_sets_z() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x14010203); assert!(mmix.execute_instruction());
assert!(f64::from_bits(mmix.get_register(1)).is_infinite());
let ra = mmix.get_special(SpecialReg::RA);
assert!((ra & RA_Z) != 0, "rA={:#x}", ra);
}
#[test]
fn test_fdiv_zero_by_zero_sets_i() {
let mut mmix = MMix::new();
mmix.set_register(2, 0.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert!(f64::from_bits(mmix.get_register(1)).is_nan());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fdiv_underflow_sets_u() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::MIN_POSITIVE.to_bits());
mmix.set_register(3, 1e16f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
let r = f64::from_bits(mmix.get_register(1));
assert_eq!(r, 0.0, "expected complete underflow to zero, got {:?}", r);
assert!((mmix.get_special(SpecialReg::RA) & RA_U) != 0);
}
#[test]
fn test_fsqrt_negative_sets_i() {
let mut mmix = MMix::new();
mmix.set_register(3, (-4.0f64).to_bits());
mmix.write_tetra(0, 0x15010003); assert!(mmix.execute_instruction());
assert!(f64::from_bits(mmix.get_register(1)).is_nan());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fsqrt_normal_no_flag() {
let mut mmix = MMix::new();
mmix.set_register(3, 9.0f64.to_bits());
mmix.write_tetra(0, 0x15010003);
assert!(mmix.execute_instruction());
assert!((f64::from_bits(mmix.get_register(1)) - 3.0).abs() < 1e-10);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_I, 0);
}
#[test]
fn test_fcmp_nan_sets_i() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::NAN.to_bits());
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x01010203); assert!(mmix.execute_instruction());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_feql_nan_no_flag() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::NAN.to_bits());
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x03010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_I, 0);
}
#[test]
fn test_fune_includes_nan() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.001f64.to_bits());
mmix.set_register(2, f64::NAN.to_bits());
mmix.set_register(3, 5.0f64.to_bits());
mmix.write_tetra(0, 0x12010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1);
}
#[test]
fn test_fcmpe_outside_epsilon() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.001f64.to_bits());
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 2.0f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1);
}
#[test]
fn test_fcmpe_binade_scaled_radius() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.25f64.to_bits());
mmix.set_register(2, 1024.0f64.to_bits());
mmix.set_register(3, 1040.0f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
}
#[test]
fn test_fcmpe_radius_uses_e_minus_1022_not_1023() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.5f64.to_bits());
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 1.75f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
}
#[test]
fn test_fcmpe_denormal_neighborhood_uses_fixed_radius() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 1e-10f64.to_bits());
mmix.set_register(2, 1e-300f64.to_bits());
mmix.set_register(3, f64::from_bits(1).to_bits()); mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, 1);
}
#[test]
fn test_fcmpe_infinite_neighborhood_epsilon_at_least_two() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 3.0f64.to_bits());
mmix.set_register(2, 5.0f64.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
}
#[test]
fn test_fcmpe_nan_operand_forces_zero_and_raises_invalid() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.1f64.to_bits());
mmix.set_register(2, f64::NAN.to_bits());
mmix.set_register(3, 5.0f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fcmpe_negative_epsilon_forces_zero_and_raises_invalid() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, (-1.0f64).to_bits());
mmix.set_register(2, 5.0f64.to_bits());
mmix.set_register(3, 6.0f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fune_negative_epsilon_is_exceptional() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, (-1.0f64).to_bits());
mmix.set_register(2, 5.0f64.to_bits());
mmix.set_register(3, 5.0f64.to_bits());
mmix.write_tetra(0, 0x12010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_I, 0); }
#[test]
fn test_fix_y_override_forces_mode_regardless_of_ra() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT);
mmix.set_register(2, 2.5f64.to_bits());
mmix.write_tetra(0, 0x05010102); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, 2);
}
#[test]
fn test_fix_two_operand_form_still_honors_ra_mode() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT); mmix.set_register(2, 2.5f64.to_bits());
mmix.write_tetra(0, 0x05010002); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, 3); }
#[test]
fn test_y_greater_than_four_halts_with_diagnostic() {
let (host, handle) = CaptureHost::new();
let mut mmix = MMix::with_host(host);
mmix.write_tetra(0, 0x0901050A); let should_continue = mmix.execute_instruction();
assert!(!should_continue);
assert_eq!(handle.diagnostics().len(), 1);
assert!(handle.diagnostics()[0].contains("FLOTI"));
assert!(handle.diagnostics()[0].contains("Y=5"));
}
#[test]
fn test_fsqrt_y_greater_than_four_halts_with_diagnostic() {
let (host, handle) = CaptureHost::new();
let mut mmix = MMix::with_host(host);
mmix.write_tetra(0, 0x15010502); let should_continue = mmix.execute_instruction();
assert!(!should_continue);
assert_eq!(handle.diagnostics().len(), 1);
assert!(handle.diagnostics()[0].contains("FSQRT"));
assert!(handle.diagnostics()[0].contains("Y=5"));
}
#[test]
fn test_fix_y_two_selects_round_up_not_round_off() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 1 << RA_ROUND_SHIFT); mmix.set_register(2, 2.5f64.to_bits());
mmix.write_tetra(0, 0x05010202); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, 3);
}
#[test]
fn test_fcmpe_denormal_radius_pins_exact_constant() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.1f64.to_bits());
mmix.set_register(2, 1.5e-308f64.to_bits());
mmix.set_register(3, 1.83e-308f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
}
#[test]
fn test_fcmpe_denormal_radius_pins_exact_constant_upper_side() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.1f64.to_bits());
mmix.set_register(2, 1.5e-308f64.to_bits());
mmix.set_register(3, 2.16752215755216e-308f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1);
}
#[test]
fn test_fcmpe_infinite_neighborhood_epsilon_below_one() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.5f64.to_bits());
mmix.set_register(2, 5.0f64.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1);
}
#[test]
fn test_fcmpe_infinite_neighborhood_epsilon_below_one_opposite_infinities() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.5f64.to_bits());
mmix.set_register(2, f64::NEG_INFINITY.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1);
}
#[test]
fn test_fcmpe_infinite_neighborhood_epsilon_one_to_two() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 1.5f64.to_bits());
mmix.set_register(2, 5.0f64.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
let mut opposite = MMix::new();
opposite.set_special(SpecialReg::RE, 1.5f64.to_bits());
opposite.set_register(2, f64::NEG_INFINITY.to_bits());
opposite.set_register(3, f64::INFINITY.to_bits());
opposite.write_tetra(0, 0x11010203); assert!(opposite.execute_instruction());
assert_eq!(opposite.get_register(1) as i64, -1);
}
#[test]
fn test_feqle_stronger_than_fcmpe_asymmetric_binade() {
let mut fcmpe = MMix::new();
fcmpe.set_special(SpecialReg::RE, 0.002f64.to_bits());
fcmpe.set_register(2, 3.99f64.to_bits());
fcmpe.set_register(3, 4.0f64.to_bits());
fcmpe.write_tetra(0, 0x11010203); assert!(fcmpe.execute_instruction());
assert_eq!(fcmpe.get_register(1), 0);
let mut feqle = MMix::new();
feqle.set_special(SpecialReg::RE, 0.002f64.to_bits());
feqle.set_register(2, 3.99f64.to_bits());
feqle.set_register(3, 4.0f64.to_bits());
feqle.write_tetra(0, 0x13010203); assert!(feqle.execute_instruction());
assert_eq!(feqle.get_register(1), 0);
}
#[test]
fn test_feqle_nan_operand_forces_zero_and_raises_invalid() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.1f64.to_bits());
mmix.set_register(2, f64::NAN.to_bits());
mmix.set_register(3, 5.0f64.to_bits());
mmix.write_tetra(0, 0x13010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fcmpe_reflexive_at_top_binade_zero_epsilon() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.0f64.to_bits());
mmix.set_register(2, f64::MAX.to_bits());
mmix.set_register(3, f64::MAX.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, 0);
}
#[test]
fn test_feqle_reflexive_at_top_binade_zero_epsilon() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.0f64.to_bits());
mmix.set_register(2, f64::MAX.to_bits());
mmix.set_register(3, f64::MAX.to_bits());
mmix.write_tetra(0, 0x13010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1);
}
#[test]
fn test_fcmpe_zero_neighborhood_reflexive() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.0f64.to_bits());
mmix.set_register(2, 0.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
}
#[test]
fn test_fcmpe_zero_neighborhood_excludes_nonzero() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 0.1f64.to_bits());
mmix.set_register(2, 0.0f64.to_bits());
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1);
}
#[test]
fn test_fcmpe_top_binade_radius_stays_finite_not_infinite() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RE, 1e-300f64.to_bits());
mmix.set_register(2, (f64::MAX / 2.0).to_bits());
mmix.set_register(3, f64::MAX.to_bits());
mmix.write_tetra(0, 0x11010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1);
}
#[test]
fn test_stsf_rounding_default_near() {
let mut mmix = MMix::new();
let v: f64 = 1.0f64 + f32::EPSILON as f64 / 2.0; mmix.set_register(1, v.to_bits()); mmix.set_register(2, 0x100); mmix.set_register(3, 0); mmix.write_tetra(0, 0xB0010203);
assert!(mmix.execute_instruction());
let stored_bits = mmix.read_tetra(0x100);
let round_trip = f32::from_bits(stored_bits) as f64;
assert_eq!(round_trip, 1.0);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_stsf_rounding_up_mode() {
let mut mmix = MMix::new();
let v: f64 = 1.0f64 + (f32::EPSILON as f64) * 0.25;
mmix.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT); mmix.set_register(1, v.to_bits());
mmix.set_register(2, 0x100);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0xB0010203);
assert!(mmix.execute_instruction());
let stored = f32::from_bits(mmix.read_tetra(0x100));
assert!((stored as f64) >= v, "{} >= {}", stored, v);
assert!(stored > 1.0);
}
#[test]
fn test_stsf_rounding_down_mode() {
let mut mmix = MMix::new();
let v: f64 = 1.0f64 + (f32::EPSILON as f64) * 0.25;
mmix.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT); mmix.set_register(1, v.to_bits());
mmix.set_register(2, 0x100);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0xB0010203);
assert!(mmix.execute_instruction());
let stored = f32::from_bits(mmix.read_tetra(0x100));
assert!((stored as f64) <= v);
assert_eq!(stored, 1.0);
}
#[test]
fn test_stsf_overflow_to_inf_sets_o() {
let mut mmix = MMix::new();
mmix.set_register(1, 1e40f64.to_bits());
mmix.set_register(2, 0x100);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0xB0010203);
assert!(mmix.execute_instruction());
let stored = f32::from_bits(mmix.read_tetra(0x100));
assert!(stored.is_infinite() && stored.is_sign_positive());
assert!((mmix.get_special(SpecialReg::RA) & RA_O) != 0);
}
#[test]
fn test_ieee_remainder_helper() {
assert_eq!(MMix::ieee_remainder(7.5, 2.0), -0.5);
assert_eq!(MMix::ieee_remainder(10.0, 3.0), 1.0);
assert!(MMix::ieee_remainder(1.0, 0.0).is_nan());
assert!(MMix::ieee_remainder(f64::INFINITY, 1.0).is_nan());
assert_eq!(MMix::ieee_remainder(3.0, f64::INFINITY), 3.0);
}
#[test]
fn test_assembler_emits_fcmpe() {
use crate::encode::encode_instruction_bytes;
use crate::mmixal::MMixInstruction;
let bytes = encode_instruction_bytes(&MMixInstruction::FCMPE(1, 2, 3));
assert_eq!(bytes, vec![0x11, 1, 2, 3]);
}
#[test]
fn test_assembler_emits_fune() {
use crate::encode::encode_instruction_bytes;
use crate::mmixal::MMixInstruction;
let bytes = encode_instruction_bytes(&MMixInstruction::FUNE(1, 2, 3));
assert_eq!(bytes, vec![0x12, 1, 2, 3]);
}
#[test]
fn test_assembler_emits_feqle() {
use crate::encode::encode_instruction_bytes;
use crate::mmixal::MMixInstruction;
let bytes = encode_instruction_bytes(&MMixInstruction::FEQLE(1, 2, 3));
assert_eq!(bytes, vec![0x13, 1, 2, 3]);
}
const SNAN_BITS: u64 = 0x7FF0_0000_0000_0001;
const QNAN_BITS: u64 = 0x7FF8_0000_0000_0001;
#[test]
fn test_is_signaling_nan_classifies_correctly() {
assert!(MMix::is_signaling_nan(f64::from_bits(SNAN_BITS)));
assert!(!MMix::is_signaling_nan(f64::from_bits(QNAN_BITS)));
assert!(!MMix::is_signaling_nan(f64::NAN));
assert!(!MMix::is_signaling_nan(1.0));
assert!(!MMix::is_signaling_nan(f64::INFINITY));
}
#[test]
fn test_quiet_nan_sets_high_mantissa_bit() {
let q = MMix::quiet_nan(f64::from_bits(SNAN_BITS));
assert!(q.is_nan());
assert_eq!(q.to_bits() & (1 << 51), 1 << 51);
assert_eq!(MMix::quiet_nan(1.5).to_bits(), 1.5f64.to_bits());
}
#[test]
fn test_fadd_snan_raises_i_and_quiets() {
let mut mmix = MMix::new();
mmix.set_register(2, SNAN_BITS);
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x04010203); assert!(mmix.execute_instruction());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
let result = mmix.get_register(1);
assert!(f64::from_bits(result).is_nan());
assert_eq!(result & (1 << 51), 1 << 51);
}
#[test]
fn test_fadd_qnan_does_not_raise_i() {
let mut mmix = MMix::new();
mmix.set_register(2, QNAN_BITS);
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_I, 0);
assert!(f64::from_bits(mmix.get_register(1)).is_nan());
}
#[test]
fn test_fmul_snan_raises_i() {
let mut mmix = MMix::new();
mmix.set_register(2, 2.0f64.to_bits());
mmix.set_register(3, SNAN_BITS);
mmix.write_tetra(0, 0x10010203); assert!(mmix.execute_instruction());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
#[test]
fn test_fsqrt_snan_raises_i() {
let mut mmix = MMix::new();
mmix.set_register(3, SNAN_BITS);
mmix.write_tetra(0, 0x15010003); assert!(mmix.execute_instruction());
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
fn one_plus_half_ulp() -> (f64, f64) {
let a = 1.0f64;
let half_ulp = f64::from_bits(0x3CA0_0000_0000_0000); (a, half_ulp)
}
#[test]
fn test_fadd_round_near_default() {
let mut mmix = MMix::new();
let (a, b) = one_plus_half_ulp();
mmix.set_register(2, a.to_bits());
mmix.set_register(3, b.to_bits());
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 1.0);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_fadd_round_up_bumps_to_next_f64() {
let mut mmix = MMix::new();
let (a, b) = one_plus_half_ulp();
mmix.set_register(2, a.to_bits());
mmix.set_register(3, b.to_bits());
mmix.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
let r = f64::from_bits(mmix.get_register(1));
assert!(r > 1.0, "expected r > 1.0, got {}", r);
assert_eq!(r, 1.0f64.next_up());
}
#[test]
fn test_fadd_round_down_keeps_below() {
let mut mmix = MMix::new();
let (a, b) = one_plus_half_ulp();
mmix.set_register(2, a.to_bits());
mmix.set_register(3, b.to_bits());
mmix.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 1.0);
}
#[test]
fn test_fadd_round_off_toward_zero_negative() {
let mut mmix = MMix::new();
let (a, b) = one_plus_half_ulp();
mmix.set_register(2, (-a).to_bits());
mmix.set_register(3, (-b).to_bits());
mmix.set_special(SpecialReg::RA, 1 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), -1.0);
}
#[test]
fn test_fmul_directed_rounding() {
let mut mmix = MMix::new();
let third = 1.0f64 / 3.0;
mmix.set_register(2, third.to_bits());
mmix.set_register(3, 3.0f64.to_bits());
mmix.write_tetra(0, 0x10010203); assert!(mmix.execute_instruction());
let near_result = f64::from_bits(mmix.get_register(1));
let mut mmix = MMix::new();
mmix.set_register(2, third.to_bits());
mmix.set_register(3, 3.0f64.to_bits());
mmix.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT);
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
let down_result = f64::from_bits(mmix.get_register(1));
assert!(down_result < 1.0, "ROUND_DOWN gave {}", down_result);
assert!(down_result <= near_result);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_fdiv_directed_rounding() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 3.0f64.to_bits());
mmix.write_tetra(0, 0x14010203); assert!(mmix.execute_instruction());
let near = f64::from_bits(mmix.get_register(1));
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 3.0f64.to_bits());
mmix.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
let up = f64::from_bits(mmix.get_register(1));
assert!(up > near, "ROUND_UP={} should exceed nearest={}", up, near);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_fdiv_directed_rounding_negative_divisor() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, (-3.0f64).to_bits());
mmix.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
let r = f64::from_bits(mmix.get_register(1));
assert!(r < -0.333, "ROUND_DOWN of 1/-3 was {}", r);
}
#[test]
fn test_fsqrt_directed_rounding() {
let mut mmix = MMix::new();
mmix.set_register(3, 2.0f64.to_bits());
mmix.write_tetra(0, 0x15010003); assert!(mmix.execute_instruction());
let near = f64::from_bits(mmix.get_register(1));
let mut mmix = MMix::new();
mmix.set_register(3, 2.0f64.to_bits());
mmix.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x15010003);
assert!(mmix.execute_instruction());
let up = f64::from_bits(mmix.get_register(1));
let mut mmix = MMix::new();
mmix.set_register(3, 2.0f64.to_bits());
mmix.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x15010003);
assert!(mmix.execute_instruction());
let down = f64::from_bits(mmix.get_register(1));
assert!(down <= near && near <= up);
assert!(down < up);
}
#[test]
fn test_fadd_exact_no_x_flag() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 2.0f64.to_bits());
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_X, 0);
}
#[test]
fn test_fmul_exact_no_x_flag() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.5f64.to_bits());
mmix.set_register(3, 4.0f64.to_bits());
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_X, 0);
}
#[test]
fn test_fdiv_exact_no_x_flag() {
let mut mmix = MMix::new();
mmix.set_register(2, 12.0f64.to_bits());
mmix.set_register(3, 4.0f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_X, 0);
}
#[test]
fn test_exact_cancellation_fadd_raises_no_underflow() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, (-1.0f64).to_bits());
mmix.write_tetra(0, 0x04010203); assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 0.0);
assert_eq!(
mmix.get_special(SpecialReg::RA) & RA_U,
0,
"exact cancellation is not an underflow"
);
}
#[test]
fn test_exact_cancellation_fsub_raises_no_underflow() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x06010203); assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 0.0);
assert_eq!(
mmix.get_special(SpecialReg::RA) & RA_U,
0,
"exact cancellation is not an underflow"
);
}
#[test]
fn test_frem_exact_zero_raises_no_underflow() {
let mut mmix = MMix::new();
mmix.set_register(2, (-3.0f64).to_bits());
mmix.set_register(3, 1.5f64.to_bits());
mmix.write_tetra(0, 0x16010203); assert!(mmix.execute_instruction());
assert_eq!(
mmix.get_special(SpecialReg::RA) & RA_U,
0,
"an exact remainder is not an underflow"
);
}
#[test]
fn test_fdiv_to_zero_raises_underflow() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::MIN_POSITIVE.to_bits());
mmix.set_register(3, 1e16f64.to_bits());
mmix.write_tetra(0, 0x14010203); assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 0.0);
assert!((mmix.get_special(SpecialReg::RA) & RA_U) != 0);
}
#[test]
fn test_fmul_to_zero_raises_underflow() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::MIN_POSITIVE.to_bits());
mmix.set_register(3, f64::MIN_POSITIVE.to_bits());
mmix.write_tetra(0, 0x10010203); assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 0.0);
assert!((mmix.get_special(SpecialReg::RA) & RA_U) != 0);
}
#[test]
fn test_fdiv_by_infinity_is_exact_zero() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x14010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0.0f64.to_bits());
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_frem_zero_takes_dividend_sign() {
let mut mmix = MMix::new();
mmix.set_register(2, (-3.0f64).to_bits());
mmix.set_register(3, 1.5f64.to_bits());
mmix.write_tetra(0, 0x16010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), (-0.0f64).to_bits());
let mut mmix = MMix::new();
mmix.set_register(2, 3.0f64.to_bits());
mmix.set_register(3, (-1.5f64).to_bits());
mmix.write_tetra(0, 0x16010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0.0f64.to_bits());
}
#[test]
fn test_fixu_wraps_mod_two_to_the_64() {
let cases: [(f64, u64); 10] = [
(-1.0, 0xFFFF_FFFF_FFFF_FFFF),
(-0.5, 0), (3.7, 4),
(9223372036854775808.0, 0x8000_0000_0000_0000), (-9223372036854775808.0, 0x8000_0000_0000_0000), (18446744073709551616.0, 0), (1e300, 0),
(18446744073709549568.0, 0xFFFF_FFFF_FFFF_F800),
(
f64::from_bits(((115 + 1023) << 52) | 1),
0x8000_0000_0000_0000,
),
(f64::from_bits(((116 + 1023) << 52) | 1), 0),
];
for (operand, expected) in cases {
let mut mmix = MMix::new();
mmix.set_register(3, operand.to_bits());
mmix.write_tetra(0, 0x07010003); assert!(mmix.execute_instruction());
assert_eq!(
mmix.get_register(1),
expected,
"FIXU {operand} should be {expected:#X}"
);
}
}
#[test]
fn test_fix_negative_is_still_signed() {
let mut mmix = MMix::new();
mmix.set_register(3, (-1.0f64).to_bits());
mmix.write_tetra(0, 0x05010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, -1);
}
#[test]
fn test_fcmp_nan_answers_zero_in_either_position() {
for (y, z) in [(f64::NAN, 1.0f64), (1.0f64, f64::NAN), (f64::NAN, f64::NAN)] {
let mut mmix = MMix::new();
mmix.set_register(2, y.to_bits());
mmix.set_register(3, z.to_bits());
mmix.write_tetra(0, 0x01010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0, "FCMP {y},{z}");
assert!((mmix.get_special(SpecialReg::RA) & RA_I) != 0);
}
}
#[test]
fn test_fcmp_ordered_still_three_way() {
for (y, z, expected) in [(1.0f64, 2.0f64, -1i64), (2.0, 2.0, 0), (3.0, 2.0, 1)] {
let mut mmix = MMix::new();
mmix.set_register(2, y.to_bits());
mmix.set_register(3, z.to_bits());
mmix.write_tetra(0, 0x01010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1) as i64, expected, "FCMP {y},{z}");
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_I, 0);
}
}
#[test]
fn test_flot_inexact_conversion_raises_x() {
let mut mmix = MMix::new();
mmix.set_register(3, (1u64 << 53) + 1);
mmix.write_tetra(0, 0x08010003); assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 9007199254740992.0);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_flot_exact_conversion_raises_nothing() {
let mut mmix = MMix::new();
mmix.set_register(3, 42);
mmix.write_tetra(0, 0x08010003); assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 42.0);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_flot_honors_directed_rounding_mode() {
let mut up = MMix::new();
up.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT); up.set_register(3, (1u64 << 53) + 1);
up.write_tetra(0, 0x08010003); assert!(up.execute_instruction());
let up_result = f64::from_bits(up.get_register(1));
let mut down = MMix::new();
down.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT); down.set_register(3, (1u64 << 53) + 1);
down.write_tetra(0, 0x08010003);
assert!(down.execute_instruction());
let down_result = f64::from_bits(down.get_register(1));
assert_eq!(down_result, 9007199254740992.0);
assert_eq!(up_result, 9007199254740994.0);
assert!(up_result > down_result);
let mut neg_up = MMix::new();
neg_up.set_special(SpecialReg::RA, 2 << RA_ROUND_SHIFT); neg_up.set_register(3, (-((1i64 << 53) + 1)) as u64);
neg_up.write_tetra(0, 0x08010003);
assert!(neg_up.execute_instruction());
assert_eq!(f64::from_bits(neg_up.get_register(1)), -9007199254740992.0);
let mut neg_down = MMix::new();
neg_down.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT); neg_down.set_register(3, (-((1i64 << 53) + 1)) as u64);
neg_down.write_tetra(0, 0x08010003);
assert!(neg_down.execute_instruction());
assert_eq!(
f64::from_bits(neg_down.get_register(1)),
-9007199254740994.0
);
}
#[test]
fn test_flotu_max_raises_x() {
let mut mmix = MMix::new();
mmix.set_register(3, u64::MAX);
mmix.write_tetra(0, 0x0A010003); assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 18446744073709551616.0);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_sflotu_wide_integer_rounds_once_to_short_precision() {
let mut mmix = MMix::new();
mmix.set_register(3, (1u64 << 53) + 1);
mmix.write_tetra(0, 0x0E010003); assert!(mmix.execute_instruction());
assert_eq!(f64::from_bits(mmix.get_register(1)), 9007199254740992.0);
assert!((mmix.get_special(SpecialReg::RA) & RA_X) != 0);
}
#[test]
fn test_overflow_round_off_clamps_to_max() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::MAX.to_bits());
mmix.set_register(3, f64::MAX.to_bits());
mmix.set_special(SpecialReg::RA, 1 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x04010203); assert!(mmix.execute_instruction());
let r = f64::from_bits(mmix.get_register(1));
assert_eq!(r, f64::MAX);
assert!((mmix.get_special(SpecialReg::RA) & RA_O) != 0);
}
#[test]
fn test_overflow_round_down_negative_keeps_neg_inf() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::MIN.to_bits()); mmix.set_register(3, f64::MIN.to_bits());
mmix.set_special(SpecialReg::RA, 3 << RA_ROUND_SHIFT); mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
let r = f64::from_bits(mmix.get_register(1));
assert!(r.is_infinite() && r.is_sign_negative());
}
#[test]
fn test_floti_immediate_200() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x090100C8); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x4069000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_sfloti_immediate_200() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x0D0100C8); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x4069000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_floti_round_up_255() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x090102FF); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x406F_E000_0000_0000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fix_1e20_wraps_and_raises_w() {
let mut mmix = MMix::new();
mmix.set_register(3, 1e20_f64.to_bits());
mmix.write_tetra(0, 0x05010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x6BC7_5E2D_6310_0000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_W);
}
#[test]
fn test_fix_two_to_the_64_wraps_to_zero() {
let mut mmix = MMix::new();
mmix.set_register(3, 2f64.powi(64).to_bits());
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_W);
}
#[test]
fn test_fix_1e300_wraps_to_zero() {
let mut mmix = MMix::new();
mmix.set_register(3, 1e300_f64.to_bits());
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_W);
}
#[test]
fn test_fix_two_to_the_63_is_out_of_range() {
let mut mmix = MMix::new();
mmix.set_register(3, 2f64.powi(63).to_bits());
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x8000000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_W);
}
#[test]
fn test_fix_negative_two_to_the_63_is_in_range() {
let mut mmix = MMix::new();
mmix.set_register(3, (-(2f64.powi(63))).to_bits());
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x8000000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fix_infinity_copies_through_with_i_alone() {
let mut mmix = MMix::new();
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), f64::INFINITY.to_bits());
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fix_quiet_nan_copies_through_with_i_alone() {
let mut mmix = MMix::new();
mmix.set_register(3, 0x7FF8000000000005);
mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000005);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fixu_quiet_nan_copies_through_with_i_alone() {
let mut mmix = MMix::new();
mmix.set_register(3, 0x7FF8000000000005);
mmix.write_tetra(0, 0x07010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000005);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fix_signaling_nan_copies_through_unquieted() {
let mut mmix = MMix::new();
mmix.set_register(3, 0x7FF0000000000001); mmix.write_tetra(0, 0x05010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF0000000000001);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fadd_two_quiet_nans_picks_z() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x7FF8000000000005); mmix.set_register(3, 0x7FF8000000000007); mmix.write_tetra(0, 0x04010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000007);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fadd_signaling_y_picks_z_and_raises_i() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x7FF0000000000001); mmix.set_register(3, 0x7FF8000000000007); mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000007);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fadd_signaling_z_quiets_and_raises_i() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x7FF8000000000005);
mmix.set_register(3, 0x7FF0000000000002); mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000002);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fadd_both_signaling_picks_z_and_raises_i() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x7FF0000000000001);
mmix.set_register(3, 0x7FF0000000000002);
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000002);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fadd_signaling_y_nonnan_z_picks_y() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x7FF0000000000001);
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000001);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fmul_two_quiet_nans_picks_z() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x7FF8000000000005);
mmix.set_register(3, 0x7FF8000000000007);
mmix.write_tetra(0, 0x10010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000007);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fdiv_signaling_y_picks_z_and_raises_i() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x7FF0000000000001);
mmix.set_register(3, 0x7FF8000000000007);
mmix.write_tetra(0, 0x14010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000007);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fsub_negative_nan_z_stays_unnegated() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 0xFFF8000000000005);
mmix.write_tetra(0, 0x06010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xFFF8000000000005);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_frem_quiet_nan_y_picks_y() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x7FF8000000000005);
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x16010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000005);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_frem_signaling_z_quiets_and_raises_i() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 0x7FF0000000000002);
mmix.write_tetra(0, 0x16010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000002);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fadd_opposite_infinities_is_invalid_signed_by_z() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::INFINITY.to_bits());
mmix.set_register(3, f64::NEG_INFINITY.to_bits());
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xFFF8000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fadd_opposite_infinities_reversed_is_invalid_signed_by_z() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::NEG_INFINITY.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fsub_same_sign_infinities_is_invalid() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::INFINITY.to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x06010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xFFF8000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fmul_zero_times_infinity_is_invalid() {
let mut mmix = MMix::new();
mmix.set_register(2, (-0.0f64).to_bits());
mmix.set_register(3, f64::INFINITY.to_bits());
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xFFF8000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fdiv_zero_over_zero_is_invalid() {
let mut mmix = MMix::new();
mmix.set_register(2, (-0.0f64).to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xFFF8000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_frem_infinite_y_is_invalid() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::NEG_INFINITY.to_bits());
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x16010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xFFF8000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_frem_zero_z_is_invalid() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x16010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fsqrt_of_negative_is_invalid() {
let mut mmix = MMix::new();
mmix.set_register(3, (-1.0f64).to_bits());
mmix.write_tetra(0, 0x15010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xFFF8000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_fint_quiet_nan_passes_through() {
let mut mmix = MMix::new();
mmix.set_register(3, 0x7FF8000000000005);
mmix.write_tetra(0, 0x17010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000005);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fint_signaling_nan_quiets_and_raises_i() {
let mut mmix = MMix::new();
mmix.set_register(3, 0x7FF0000000000001);
mmix.write_tetra(0, 0x17010003);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF8000000000001);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_ldsf_signaling_nan_widens_bit_for_bit() {
let mut mmix = MMix::new();
mmix.set_register(2, 100);
mmix.set_register(3, 0);
mmix.write_tetra(100, 0x7F800001);
mmix.write_tetra(0, 0x90010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF0000020000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_ldsf_quiet_nan_widens_bit_for_bit() {
let mut mmix = MMix::new();
mmix.set_register(2, 100);
mmix.set_register(3, 0);
mmix.write_tetra(100, 0x7FC00005);
mmix.write_tetra(0, 0x90010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x7FF80000A0000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_stsf_signaling_nan_quiets_on_store() {
let mut mmix = MMix::new();
mmix.set_register(1, 0x7FF0000000000001); mmix.set_register(2, 100);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0xB0010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(100), 0x7FC00000);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_stsf_signaling_nan_truncates_and_quiets() {
let mut mmix = MMix::new();
mmix.set_register(1, 0x7FF0000020000000);
mmix.set_register(2, 100);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0xB0010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(100), 0x7FC00001);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_I);
}
#[test]
fn test_stsf_exact_short_subnormal_raises_nothing() {
let mut mmix = MMix::new();
mmix.set_register(1, 0x37D0000000000000); mmix.set_register(2, 100);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0xB0010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(100), 0x00080000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fadd_smallest_subnormals_sum_exactly() {
let mut mmix = MMix::new();
mmix.set_register(2, 1);
mmix.set_register(3, 1);
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 2);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fsub_at_the_subnormal_boundary_is_exact() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x0010000000000000);
mmix.set_register(3, 1);
mmix.write_tetra(0, 0x06010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x000FFFFFFFFFFFFF);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fmul_exact_subnormal_product_raises_nothing() {
let mut mmix = MMix::new();
mmix.set_register(2, 2f64.powi(-1000).to_bits());
mmix.set_register(3, 2f64.powi(-30).to_bits());
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x0000100000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fmul_boundary_normal_times_half_is_exact_subnormal() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x0010000000000000);
mmix.set_register(3, 0.5f64.to_bits());
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x0008000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fmul_exact_subnormal_still_trips_when_u_enabled() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x60, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_U << 8); mmix.set_pc(0x100);
mmix.set_register(2, 0x0010000000000000);
mmix.set_register(3, 0.5f64.to_bits());
mmix.write_tetra(0x100, 0x10010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x60);
}
#[test]
fn test_fmul_inexact_underflow_raises_u_and_x() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x0008000000000000);
mmix.set_register(3, 0.1f64.to_bits());
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x0000CCCCCCCCCCCD);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_U | RA_X);
}
#[test]
fn test_fmul_underflow_to_zero_raises_u_and_x() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x0010000000000000);
mmix.set_register(3, 0x0010000000000000);
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_U | RA_X);
}
#[test]
fn test_fmul_smallest_subnormal_times_half_underflows_to_zero() {
let mut mmix = MMix::new();
mmix.set_register(2, 1);
mmix.set_register(3, 0.5f64.to_bits());
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_U | RA_X);
}
#[test]
fn test_fmul_subnormal_times_half_rounds_and_underflows() {
let mut mmix = MMix::new();
mmix.set_register(2, 3);
mmix.set_register(3, 0.5f64.to_bits());
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 2);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_U | RA_X);
}
#[test]
fn test_fdiv_boundary_normal_over_one_point_five_underflows() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x0010000000000000);
mmix.set_register(3, 1.5f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x000AAAAAAAAAAAAB);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_U | RA_X);
}
#[test]
fn test_fmul_round_up_pushes_a_tiny_product_up_from_zero() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 0x20000); mmix.set_register(2, 1);
mmix.set_register(3, 0.5f64.to_bits());
mmix.write_tetra(0, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1);
assert_eq!(mmix.get_special(SpecialReg::RA), 0x20005);
}
#[test]
fn test_fsqrt_of_a_subnormal_rounds_to_a_normal_result() {
let mut mmix = MMix::new();
mmix.set_register(3, 2);
mmix.write_tetra(0, 0x15010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x1E66A09E667F3BCD);
assert_eq!(mmix.get_special(SpecialReg::RA), RA_X);
}
#[test]
fn test_fdiv_infinity_over_zero_raises_nothing() {
let mut mmix = MMix::new();
mmix.set_register(2, f64::INFINITY.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), f64::INFINITY.to_bits());
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fdiv_by_zero_raises_z_alone() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), f64::INFINITY.to_bits());
assert_eq!(mmix.get_special(SpecialReg::RA), RA_Z);
}
#[test]
fn test_fdiv_negative_by_zero_raises_z_alone() {
let mut mmix = MMix::new();
mmix.set_register(2, (-1.0f64).to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), f64::NEG_INFINITY.to_bits());
assert_eq!(mmix.get_special(SpecialReg::RA), RA_Z);
}
#[test]
fn test_fdiv_subnormal_by_zero_raises_z_alone() {
let mut mmix = MMix::new();
mmix.set_register(2, 1);
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), f64::INFINITY.to_bits());
assert_eq!(mmix.get_special(SpecialReg::RA), RA_Z);
}
#[test]
fn test_fdiv_by_zero_is_exact_in_round_off_too() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 0x10000); mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x14010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), f64::INFINITY.to_bits());
assert_eq!(mmix.get_special(SpecialReg::RA), 0x10002);
}
#[test]
fn test_fadd_round_down_mixed_zero_is_negative() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 0x30000); mmix.set_register(2, 0.0f64.to_bits());
mmix.set_register(3, (-0.0f64).to_bits());
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x8000000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0x30000);
}
#[test]
fn test_fsub_round_down_cancellation_is_negative() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 0x30000);
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x06010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x8000000000000000);
assert_eq!(mmix.get_special(SpecialReg::RA), 0x30000);
}
#[test]
fn test_fadd_round_down_both_positive_zero_stays_positive() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, 0x30000);
mmix.set_register(2, 0.0f64.to_bits());
mmix.set_register(3, 0.0f64.to_bits());
mmix.write_tetra(0, 0x04010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_special(SpecialReg::RA), 0x30000);
}
#[test]
fn test_fsub_cancellation_is_positive_by_default() {
let mut mmix = MMix::new();
mmix.set_register(2, 1.0f64.to_bits());
mmix.set_register(3, 1.0f64.to_bits());
mmix.write_tetra(0, 0x06010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_fix_with_illegal_round_mode_exits_1() {
let (host, handle) = CaptureHost::new();
let mut mmix = MMix::with_host(host);
mmix.set_register(3, 42.0f64.to_bits());
mmix.write_tetra(0, 0x05020503); assert!(!mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0, "the PC stays on the rejected instruction");
assert_eq!(mmix.get_register(2), 0, "the write did not land");
assert_eq!(mmix.get_exit_code(), 1);
assert_eq!(handle.diagnostics().len(), 1);
assert!(handle.diagnostics()[0].contains("FIX"));
}
#[derive(Default)]
struct RowChecks(Vec<String>);
impl RowChecks {
fn check(&mut self, row: &str, field: &str, actual: u64, expected: u64) {
if actual != expected {
self.0.push(format!(
"row {row} {field}: expected {expected:#018x}, got {actual:#018x}"
));
}
}
fn check_tetra(&mut self, row: &str, field: &str, actual: u32, expected: u32) {
if actual != expected {
self.0.push(format!(
"row {row} {field}: expected {expected:#010x}, got {actual:#010x}"
));
}
}
fn finish(self) {
assert!(self.0.is_empty(), "\n{}", self.0.join("\n"));
}
}
fn run_sflot_reg(opcode: u8, y: u8, z_bits: u64, ra_in: u64) -> (u64, u64) {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, ra_in);
mmix.set_register(3, z_bits);
let tetra = ((opcode as u32) << 24) | (1u32 << 16) | ((y as u32) << 8) | 3;
mmix.write_tetra(0, tetra);
assert!(mmix.execute_instruction());
(mmix.get_register(1), mmix.get_special(SpecialReg::RA))
}
fn run_sflot_imm(opcode: u8, y: u8, z: u8, ra_in: u64) -> (u64, u64) {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, ra_in);
let tetra = ((opcode as u32) << 24) | (1u32 << 16) | ((y as u32) << 8) | (z as u32);
mmix.write_tetra(0, tetra);
assert!(mmix.execute_instruction());
(mmix.get_register(1), mmix.get_special(SpecialReg::RA))
}
fn run_frem(y_bits: u64, z_bits: u64, ra_in: u64) -> (u64, u64) {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, ra_in);
mmix.set_register(2, y_bits);
mmix.set_register(3, z_bits);
mmix.write_tetra(0, 0x16010203);
assert!(mmix.execute_instruction());
(mmix.get_register(1), mmix.get_special(SpecialReg::RA))
}
fn run_stsf(x_bits: u64, ra_in: u64) -> (u32, u64) {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, ra_in);
mmix.set_register(1, x_bits);
mmix.set_register(2, 0x100);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0xB0010203);
assert!(mmix.execute_instruction());
(mmix.read_tetra(0x100), mmix.get_special(SpecialReg::RA))
}
fn run_stsfi(x_bits: u64, ra_in: u64) -> (u32, u64) {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, ra_in);
mmix.set_register(1, x_bits);
mmix.set_register(2, 0x100);
mmix.write_tetra(0, 0xB1010200);
assert!(mmix.execute_instruction());
(mmix.read_tetra(0x100), mmix.get_special(SpecialReg::RA))
}
fn run_binop(opcode: u8, y_bits: u64, z_bits: u64, ra_in: u64) -> (u64, u64) {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RA, ra_in);
mmix.set_register(2, y_bits);
mmix.set_register(3, z_bits);
let tetra = ((opcode as u32) << 24) | (1u32 << 16) | (2u32 << 8) | 3;
mmix.write_tetra(0, tetra);
assert!(mmix.execute_instruction());
(mmix.get_register(1), mmix.get_special(SpecialReg::RA))
}
#[test]
fn test_sflot_rounds_the_integer_once() {
let mut c = RowChecks::default();
let (r, ra) = run_sflot_reg(0x0C, 0, 0x1000001000000001, 0x0);
c.check("S1", "result", r, 0x43B0000020000000);
c.check("S1", "rA", ra, 0x1);
let (r, ra) = run_sflot_reg(0x0C, 0, 0x1000002FFFFFFFFF, 0x0);
c.check("S2", "result", r, 0x43B0000020000000);
c.check("S2", "rA", ra, 0x1);
let (r, ra) = run_sflot_reg(0x0C, 2, 0x1000000000000001, 0x0);
c.check("S3", "result", r, 0x43B0000020000000);
c.check("S3", "rA", ra, 0x1);
let (r, ra) = run_sflot_reg(0x0C, 0, 0x1000000000000001, 0x20000);
c.check("S4", "result", r, 0x43B0000020000000);
c.check("S4", "rA", ra, 0x20001);
let (r, ra) = run_sflot_reg(0x0C, 1, 0x1000001FFFFFFFFF, 0x0);
c.check("S5", "result", r, 0x43B0000000000000);
c.check("S5", "rA", ra, 0x1);
let (r, ra) = run_sflot_reg(0x0C, 3, 0xEFFFFFFFFFFFFFFF, 0x0);
c.check("S6", "result", r, 0xC3B0000020000000);
c.check("S6", "rA", ra, 0x1);
c.finish();
}
#[test]
fn test_sflotu_rounds_the_integer_once() {
let mut c = RowChecks::default();
let (r, ra) = run_sflot_reg(0x0E, 0, 0x8000008000000001, 0x0);
c.check("S7", "result", r, 0x43E0000020000000);
c.check("S7", "rA", ra, 0x1);
let (r, ra) = run_sflot_reg(0x0E, 3, 0xFFFFFFFFFFFFFFFF, 0x0);
c.check("S8", "result", r, 0x43EFFFFFE0000000);
c.check("S8", "rA", ra, 0x1);
c.finish();
}
#[test]
fn test_sflot_stays_correct_where_double_rounding_would_not_differ() {
let mut c = RowChecks::default();
let (r, ra) = run_sflot_reg(0x0C, 2, 0xEFFFFFFFFFFFFFFF, 0x0);
c.check("S9", "result", r, 0xC3B0000000000000);
c.check("S9", "rA", ra, 0x1);
let (r, ra) = run_sflot_reg(0x0C, 0, 0x8000000000000000, 0x0);
c.check("S11", "result", r, 0xC3E0000000000000);
c.check("S11", "rA", ra, 0x0);
let (r, ra) = run_sflot_reg(0x0C, 0, 0x1000002000000000, 0x0);
c.check("S12", "result", r, 0x43B0000020000000);
c.check("S12", "rA", ra, 0x0);
let (r, ra) = run_sflot_reg(0x0C, 0, 0x0000000001000001, 0x0);
c.check("S13", "result", r, 0x4170000000000000);
c.check("S13", "rA", ra, 0x1);
c.finish();
}
#[test]
fn test_sflotu_max_rounds_up_to_two_pow_64() {
let (r, ra) = run_sflot_reg(0x0E, 0, 0xFFFFFFFFFFFFFFFF, 0x0);
assert_eq!(r, 0x43F0000000000000, "row S10 result");
assert_eq!(ra, 0x1, "row S10 rA");
}
#[test]
fn test_sflotui_immediate_is_always_exact() {
let (r, ra) = run_sflot_imm(0x0F, 0, 255, 0x20000);
assert_eq!(r, 0x406FE00000000000, "row S14 result");
assert_eq!(ra, 0x20000, "row S14 rA");
}
#[test]
fn test_sfloti_immediate_is_always_exact() {
let (r, ra) = run_sflot_imm(0x0D, 0, 200, 0x20000);
assert_eq!(r, 0x4069000000000000, "row S15 result");
assert_eq!(ra, 0x20000, "row S15 rA");
}
#[test]
fn test_frem_is_exact() {
let mut c = RowChecks::default();
let (r, ra) = run_frem(0x43B0000000000000, 0x4008000000000000, 0x0);
c.check("F1", "result", r, 0x3FF0000000000000);
c.check("F1", "rA", ra, 0x0);
let (r, ra) = run_frem(0xC3B0000000000000, 0x4008000000000000, 0x0);
c.check("F2", "result", r, 0xBFF0000000000000);
c.check("F2", "rA", ra, 0x0);
let (r, ra) = run_frem(0x43B0000000000000, 0xC008000000000000, 0x0);
c.check("F3", "result", r, 0x3FF0000000000000);
c.check("F3", "rA", ra, 0x0);
let (r, ra) = run_frem(0x7FEFFFFFFFFFFFFF, 0x4008000000000000, 0x0);
c.check("F4", "result", r, 0xBFF0000000000000);
c.check("F4", "rA", ra, 0x0);
let (r, ra) = run_frem(0x7FEFFFFFFFFFFFFF, 0x0000000000000003, 0x0);
c.check("F5", "result", r, 0x8000000000000001);
c.check("F5", "rA", ra, 0x0);
let (r, ra) = run_frem(0x7FEFFFFFFFFFFFFF, 0x0000000000000001, 0x0);
c.check("F6", "result", r, 0x0000000000000000);
c.check("F6", "rA", ra, 0x0);
let (r, ra) = run_frem(0xFFEFFFFFFFFFFFFF, 0x0000000000000001, 0x0);
c.check("F7", "result", r, 0x8000000000000000);
c.check("F7", "rA", ra, 0x0);
let (r, ra) = run_frem(0x401E4B06CFABE967, 0x3FF18072E8F9C859, 0x0);
c.check("F8", "result", r, 0xBFB57092024D4D30);
c.check("F8", "rA", ra, 0x0);
let (r, ra) = run_frem(0x41FC2CE6F4E623B1, 0x3FF414C3423C5FD7, 0x0);
c.check("F9", "result", r, 0x3FDC758743355304);
c.check("F9", "rA", ra, 0x0);
c.finish();
}
#[test]
fn test_frem_stays_exact_regardless_of_mode() {
let mut c = RowChecks::default();
let (r, ra) = run_frem(0x401E000000000000, 0x4014000000000000, 0x0);
c.check("F10", "result", r, 0xC004000000000000);
c.check("F10", "rA", ra, 0x0);
let (r, ra) = run_frem(0x0000000000000005, 0x0000000000000002, 0x0);
c.check("F11", "result", r, 0x0000000000000001);
c.check("F11", "rA", ra, 0x0);
let (r, ra) = run_frem(0x4008000000000000, 0x3FF8000000000000, 0x30000);
c.check("F12", "result", r, 0x0000000000000000);
c.check("F12", "rA", ra, 0x30000);
c.finish();
}
#[test]
fn test_stsf_overflow_raises_o() {
let mut c = RowChecks::default();
let (bits, ra) = run_stsf(0x7E37000000000000, 0x10000);
c.check_tetra("T1", "stores", bits, 0x7F7FFFFF);
c.check("T1", "rA", ra, 0x10009);
let (bits, ra) = run_stsf(0x7E37000000000000, 0x30000);
c.check_tetra("T2", "stores", bits, 0x7F7FFFFF);
c.check("T2", "rA", ra, 0x30009);
let (bits, ra) = run_stsf(0xFE37000000000000, 0x10000);
c.check_tetra("T3", "stores", bits, 0xFF7FFFFF);
c.check("T3", "rA", ra, 0x10009);
let (bits, ra) = run_stsf(0xFE37000000000000, 0x20000);
c.check_tetra("T4", "stores", bits, 0xFF7FFFFF);
c.check("T4", "rA", ra, 0x20009);
let (bits, ra) = run_stsf(0x47F0000000000000, 0x10000);
c.check_tetra("T5", "stores", bits, 0x7F7FFFFF);
c.check("T5", "rA", ra, 0x10009);
c.finish();
}
#[test]
fn test_stsfi_overflow_raises_o() {
let (bits, ra) = run_stsfi(0x7E37000000000000, 0x10000);
assert_eq!(bits, 0x7F7FFFFF, "row T6 stores");
assert_eq!(ra, 0x10009, "row T6 rA");
}
#[test]
fn test_stsf_exact_overflow_to_infinity_raises_o() {
let mut c = RowChecks::default();
let (bits, ra) = run_stsf(0x7E37000000000000, 0x0);
c.check_tetra("T7", "stores", bits, 0x7F800000);
c.check("T7", "rA", ra, 0x9);
let (bits, ra) = run_stsf(0x7E37000000000000, 0x20000);
c.check_tetra("T8", "stores", bits, 0x7F800000);
c.check("T8", "rA", ra, 0x20009);
let (bits, ra) = run_stsf(0xFE37000000000000, 0x30000);
c.check_tetra("T9", "stores", bits, 0xFF800000);
c.check("T9", "rA", ra, 0x30009);
c.finish();
}
#[test]
fn test_stsf_near_max_boundary_follows_exponent_unbounded_rounding() {
let mut c = RowChecks::default();
let (bits, ra) = run_stsf(0x47EFFFFFF0000000, 0x10000);
c.check_tetra("T10", "stores", bits, 0x7F7FFFFF);
c.check("T10", "rA", ra, 0x10001);
let (bits, ra) = run_stsf(0x47EFFFFFF0000000, 0x30000);
c.check_tetra("T11", "stores", bits, 0x7F7FFFFF);
c.check("T11", "rA", ra, 0x30001);
let (bits, ra) = run_stsf(0x47EFFFFFF0000000, 0x0);
c.check_tetra("T12", "stores", bits, 0x7F800000);
c.check("T12", "rA", ra, 0x9);
let (bits, ra) = run_stsf(0x47EFFFFFE0000000, 0x20000);
c.check_tetra("T13", "stores", bits, 0x7F7FFFFF);
c.check("T13", "rA", ra, 0x20000);
let (bits, ra) = run_stsf(0x47EFFFFFE0000001, 0x20000);
c.check_tetra("T14", "stores", bits, 0x7F800000);
c.check("T14", "rA", ra, 0x20009);
let (bits, ra) = run_stsf(0x7FF0000000000000, 0x10000);
c.check_tetra("T15", "stores", bits, 0x7F800000);
c.check("T15", "rA", ra, 0x10000);
c.finish();
}
#[test]
fn test_fadd_overflow_follows_rounded_result() {
let mut c = RowChecks::default();
let (r, ra) = run_binop(0x04, 0x7FEFFFFFFFFFFFFF, 0x7C90000000000000, 0x10000);
c.check("B1", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("B1", "rA", ra, 0x10001);
let (r, ra) = run_binop(0x04, 0x7FEFFFFFFFFFFFFF, 0x7C90000000000000, 0x30000);
c.check("B2", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("B2", "rA", ra, 0x30001);
let (r, ra) = run_binop(0x04, 0x7FEFFFFFFFFFFFFF, 0x7C80000000000000, 0x20000);
c.check("B4", "result", r, 0x7FF0000000000000);
c.check("B4", "rA", ra, 0x20009);
let (r, ra) = run_binop(0x04, 0xFFEFFFFFFFFFFFFF, 0xFC80000000000000, 0x30000);
c.check("B6", "result", r, 0xFFF0000000000000);
c.check("B6", "rA", ra, 0x30009);
c.finish();
}
#[test]
fn test_fsub_overflow_follows_rounded_result() {
let (r, ra) = run_binop(0x06, 0x7FEFFFFFFFFFFFFF, 0xFC80000000000000, 0x20000);
assert_eq!(r, 0x7FF0000000000000, "row B5 result");
assert_eq!(ra, 0x20009, "row B5 rA");
}
#[test]
fn test_fmul_overflow_follows_rounded_result() {
let mut c = RowChecks::default();
let (r, ra) = run_binop(0x10, 0x7FEFFFFFFFFFFFFE, 0x3FF0000000000001, 0x10000);
c.check("B3", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("B3", "rA", ra, 0x10001);
let (r, ra) = run_binop(0x10, 0x7FE8000000000001, 0x3FF5555555555554, 0x20000);
c.check("B12", "result", r, 0x7FF0000000000000);
c.check("B12", "rA", ra, 0x20009);
c.finish();
}
#[test]
fn test_fadd_round_off_exact_next_binade_boundary_raises_o() {
let (r, ra) = run_binop(0x04, 0x7FEFFFFFFFFFFFFF, 0x7CA0000000000000, 0x10000);
assert_eq!(r, 0x7FEFFFFFFFFFFFFF, "result");
assert_eq!(ra, 0x10009, "rA");
}
#[test]
fn test_fmul_round_off_exact_next_binade_boundary_raises_o() {
let (r, ra) = run_binop(0x10, 0x7FE0000000000000, 0x4000000000000000, 0x10000);
assert_eq!(r, 0x7FEFFFFFFFFFFFFF, "result");
assert_eq!(ra, 0x10009, "rA");
}
#[test]
fn test_exact_max_boundary_under_round_up_does_not_raise_o() {
let mut c = RowChecks::default();
let (r, ra) = run_binop(0x04, 0x7FEFFFFFFFFFFFFF, 0x0000000000000000, 0x20000);
c.check("MAX+0", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("MAX+0", "rA", ra, 0x20000);
let (r, ra) = run_binop(0x10, 0x7FEFFFFFFFFFFFFF, 0x3FF0000000000000, 0x20000);
c.check("MAX*1", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("MAX*1", "rA", ra, 0x20000);
let (r, ra) = run_binop(0x14, 0x7FEFFFFFFFFFFFFF, 0x3FF0000000000000, 0x20000);
c.check("MAX/1", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("MAX/1", "rA", ra, 0x20000);
let (r, ra) = run_binop(0x04, 0x7FE0000000000000, 0x7FDFFFFFFFFFFFFE, 0x20000);
c.check("2^1023+rest", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("2^1023+rest", "rA", ra, 0x20000);
c.finish();
}
#[test]
fn test_fadd_ties_and_directed_overflow_raise_o() {
let mut c = RowChecks::default();
let (r, ra) = run_binop(0x04, 0x7FEFFFFFFFFFFFFF, 0x7C90000000000000, 0x0);
c.check("B7", "result", r, 0x7FF0000000000000);
c.check("B7", "rA", ra, 0x9);
let (r, ra) = run_binop(0x04, 0x7FEFFFFFFFFFFFFF, 0x7FEFFFFFFFFFFFFF, 0x10000);
c.check("B10", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("B10", "rA", ra, 0x10009);
c.finish();
}
#[test]
fn test_fadd_stays_finite_or_already_infinite_without_raising_o() {
let mut c = RowChecks::default();
let (r, ra) = run_binop(0x04, 0x7FEFFFFFFFFFFFFF, 0x7C80000000000000, 0x0);
c.check("B8", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("B8", "rA", ra, 0x1);
let (r, ra) = run_binop(0x04, 0x7FF0000000000000, 0x3FF0000000000000, 0x10000);
c.check("B13", "result", r, 0x7FF0000000000000);
c.check("B13", "rA", ra, 0x10000);
c.finish();
}
#[test]
fn test_fmul_near_overflow_raises_o() {
let (r, ra) = run_binop(0x10, 0x7FEFFFFFFFFFFFFE, 0x3FF0000000000001, 0x0);
assert_eq!(r, 0x7FF0000000000000, "row B9 result");
assert_eq!(ra, 0x9, "row B9 rA");
}
#[test]
fn test_fdiv_overflow_raises_o_and_zero_divisor_raises_z_alone() {
let mut c = RowChecks::default();
let (r, ra) = run_binop(0x14, 0x7FEFFFFFFFFFFFFF, 0x3FEFFFFFFFFFFFFF, 0x10000);
c.check("B11", "result", r, 0x7FEFFFFFFFFFFFFF);
c.check("B11", "rA", ra, 0x10009);
let (r, ra) = run_binop(0x14, 0x3FF0000000000000, 0x0000000000000000, 0x20000);
c.check("Z1", "result", r, 0x7FF0000000000000);
c.check("Z1", "rA", ra, 0x20002);
let (r, ra) = run_binop(0x14, 0xBFF0000000000000, 0x0000000000000000, 0x30000);
c.check("Z2", "result", r, 0xFFF0000000000000);
c.check("Z2", "rA", ra, 0x30002);
c.finish();
}