|
2 | 2 | extern crate rustc_apfloat;
|
3 | 3 |
|
4 | 4 | use core::cmp::Ordering;
|
5 |
| -use rustc_apfloat::ieee::{Double, Half, Quad, Single, X87DoubleExtended}; |
| 5 | +use rustc_apfloat::ieee::{BFloat, Double, Half, Quad, Single, X87DoubleExtended}; |
6 | 6 | use rustc_apfloat::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO};
|
7 | 7 | use rustc_apfloat::{Float, FloatConvert, Round, Status};
|
8 | 8 |
|
@@ -36,6 +36,35 @@ impl DoubleExt for Double {
|
36 | 36 | }
|
37 | 37 | }
|
38 | 38 |
|
| 39 | +// NOTE(eddyb) these match the C++ `convertToFloat`/`convertToDouble` methods, |
| 40 | +// after their generalization to allow an optional lossless conversion to their |
| 41 | +// expected semantics (from e.g. `IEEEhalf`/`BFloat`, for `convertToSingle`). |
| 42 | +// FIXME(eddyb) should the methods have e.g. `_lossless_via_convert` in their names? |
| 43 | +fn assert_lossless_conversion<S: FloatConvert<T>, T: Float>(src: S) -> T { |
| 44 | + let mut loses_info = false; |
| 45 | + let status; |
| 46 | + let r = unpack!(status=, src.convert(&mut loses_info)); |
| 47 | + assert!(!status.intersects(Status::INEXACT) && !loses_info, "Unexpected imprecision"); |
| 48 | + r |
| 49 | +} |
| 50 | + |
| 51 | +trait ToF32LosslessViaConvertToSingle: FloatConvert<Single> { |
| 52 | + fn to_f32(self) -> f32 { |
| 53 | + assert_lossless_conversion(self).to_f32() |
| 54 | + } |
| 55 | +} |
| 56 | +impl ToF32LosslessViaConvertToSingle for Half {} |
| 57 | +impl ToF32LosslessViaConvertToSingle for BFloat {} |
| 58 | + |
| 59 | +trait ToF64LosslessViaConvertToDouble: FloatConvert<Double> { |
| 60 | + fn to_f64(self) -> f64 { |
| 61 | + assert_lossless_conversion(self).to_f64() |
| 62 | + } |
| 63 | +} |
| 64 | +impl ToF64LosslessViaConvertToDouble for Single {} |
| 65 | +// HACK(eddyb) take advantage of the transitivity of "are conversions lossless". |
| 66 | +impl<T: ToF32LosslessViaConvertToSingle + FloatConvert<Double>> ToF64LosslessViaConvertToDouble for T {} |
| 67 | + |
39 | 68 | #[test]
|
40 | 69 | fn is_signaling() {
|
41 | 70 | // We test qNaN, -qNaN, +sNaN, -sNaN with and without payloads.
|
@@ -4009,3 +4038,216 @@ fn x87_largest() {
|
4009 | 4038 | fn x87_next() {
|
4010 | 4039 | assert_eq!("-1.0".parse::<X87DoubleExtended>().unwrap().next_up().value.ilogb(), -1);
|
4011 | 4040 | }
|
| 4041 | + |
| 4042 | +// HACK(eddyb) C`{FLT,DBL}_TRUE_MIN` / C++ `std::numeric_limits<T>::denorm_min` |
| 4043 | +// equivalents, for the two tests below, as Rust seems to lack anything like them, |
| 4044 | +// but their bit-patterns are thankfuly trivial, with the main caveat that they |
| 4045 | +// can't be `const` (subnormals and NaNs are banned from CTFE `{to,from}_bits`). |
| 4046 | +fn f64_smallest_subnormal() -> f64 { |
| 4047 | + f64::from_bits(1) |
| 4048 | +} |
| 4049 | +fn f32_smallest_subnormal() -> f32 { |
| 4050 | + f32::from_bits(1) |
| 4051 | +} |
| 4052 | + |
| 4053 | +#[test] |
| 4054 | +fn to_f64() { |
| 4055 | + let d_pos_zero = Double::from_f64(0.0); |
| 4056 | + assert!(Double::from_f64(d_pos_zero.to_f64()).is_pos_zero()); |
| 4057 | + let d_neg_zero = Double::from_f64(-0.0); |
| 4058 | + assert!(Double::from_f64(d_neg_zero.to_f64()).is_neg_zero()); |
| 4059 | + |
| 4060 | + let d_one = Double::from_f64(1.0); |
| 4061 | + assert_eq!(1.0, d_one.to_f64()); |
| 4062 | + let d_pos_largest = Double::largest(); |
| 4063 | + assert_eq!(f64::MAX, d_pos_largest.to_f64()); |
| 4064 | + let d_neg_largest = -Double::largest(); |
| 4065 | + assert_eq!(-f64::MAX, d_neg_largest.to_f64()); |
| 4066 | + let d_pos_smallest = Double::smallest_normalized(); |
| 4067 | + assert_eq!(f64::MIN_POSITIVE, d_pos_smallest.to_f64()); |
| 4068 | + let d_neg_smallest = -Double::smallest_normalized(); |
| 4069 | + assert_eq!(-f64::MIN_POSITIVE, d_neg_smallest.to_f64()); |
| 4070 | + |
| 4071 | + let d_smallest_denorm = Double::SMALLEST; |
| 4072 | + assert_eq!(f64_smallest_subnormal(), d_smallest_denorm.to_f64()); |
| 4073 | + let d_largest_denorm = "0x0.FFFFFFFFFFFFFp-1022".parse::<Double>().unwrap(); |
| 4074 | + assert_eq!(/*0x0.FFFFFFFFFFFFFp-1022*/ 2.225073858507201e-308, d_largest_denorm.to_f64()); |
| 4075 | + |
| 4076 | + let d_pos_inf = Double::INFINITY; |
| 4077 | + assert_eq!(f64::INFINITY, d_pos_inf.to_f64()); |
| 4078 | + let d_neg_inf = -Double::INFINITY; |
| 4079 | + assert_eq!(-f64::INFINITY, d_neg_inf.to_f64()); |
| 4080 | + let d_qnan = Double::qnan(None); |
| 4081 | + assert!(d_qnan.to_f64().is_nan()); |
| 4082 | + |
| 4083 | + let f_pos_zero = Single::from_f32(0.0); |
| 4084 | + assert!(Double::from_f64(f_pos_zero.to_f64()).is_pos_zero()); |
| 4085 | + let f_neg_zero = Single::from_f32(-0.0); |
| 4086 | + assert!(Double::from_f64(f_neg_zero.to_f64()).is_neg_zero()); |
| 4087 | + |
| 4088 | + let f_one = Single::from_f32(1.0); |
| 4089 | + assert_eq!(1.0, f_one.to_f64()); |
| 4090 | + let f_pos_largest = Single::largest(); |
| 4091 | + assert_eq!(f32::MAX as f64, f_pos_largest.to_f64()); |
| 4092 | + let f_neg_largest = -Single::largest(); |
| 4093 | + assert_eq!(-f32::MAX as f64, f_neg_largest.to_f64()); |
| 4094 | + let f_pos_smallest = Single::smallest_normalized(); |
| 4095 | + assert_eq!(f32::MIN_POSITIVE as f64, f_pos_smallest.to_f64()); |
| 4096 | + let f_neg_smallest = -Single::smallest_normalized(); |
| 4097 | + assert_eq!(-f32::MIN_POSITIVE as f64, f_neg_smallest.to_f64()); |
| 4098 | + |
| 4099 | + let f_smallest_denorm = Single::SMALLEST; |
| 4100 | + assert_eq!(f32_smallest_subnormal() as f64, f_smallest_denorm.to_f64()); |
| 4101 | + let f_largest_denorm = "0x0.FFFFFEp-126".parse::<Double>().unwrap(); |
| 4102 | + assert_eq!(/*0x0.FFFFFEp-126*/ 1.1754942106924411e-38, f_largest_denorm.to_f64()); |
| 4103 | + |
| 4104 | + let f_pos_inf = Single::INFINITY; |
| 4105 | + assert_eq!(f64::INFINITY, f_pos_inf.to_f64()); |
| 4106 | + let f_neg_inf = -Single::INFINITY; |
| 4107 | + assert_eq!(-f64::INFINITY, f_neg_inf.to_f64()); |
| 4108 | + let f_qnan = Single::qnan(None); |
| 4109 | + assert!(f_qnan.to_f64().is_nan()); |
| 4110 | + |
| 4111 | + let h_pos_zero = Half::ZERO; |
| 4112 | + assert!(Double::from_f64(h_pos_zero.to_f64()).is_pos_zero()); |
| 4113 | + let h_neg_zero = -Half::ZERO; |
| 4114 | + assert!(Double::from_f64(h_neg_zero.to_f64()).is_neg_zero()); |
| 4115 | + |
| 4116 | + let h_one = "1.0".parse::<Half>().unwrap(); |
| 4117 | + assert_eq!(1.0, h_one.to_f64()); |
| 4118 | + let h_pos_largest = Half::largest(); |
| 4119 | + assert_eq!(65504.0, h_pos_largest.to_f64()); |
| 4120 | + let h_neg_largest = -Half::largest(); |
| 4121 | + assert_eq!(-65504.0, h_neg_largest.to_f64()); |
| 4122 | + let h_pos_smallest = Half::smallest_normalized(); |
| 4123 | + assert_eq!(/*0x1.p-14*/ 6.103515625e-05, h_pos_smallest.to_f64()); |
| 4124 | + let h_neg_smallest = -Half::smallest_normalized(); |
| 4125 | + assert_eq!(/*-0x1.p-14*/ -6.103515625e-05, h_neg_smallest.to_f64()); |
| 4126 | + |
| 4127 | + let h_smallest_denorm = Half::SMALLEST; |
| 4128 | + assert_eq!(/*0x1.p-24*/ 5.960464477539063e-08, h_smallest_denorm.to_f64()); |
| 4129 | + let h_largest_denorm = "0x1.FFCp-14".parse::<Half>().unwrap(); |
| 4130 | + assert_eq!(/*0x1.FFCp-14*/ 0.00012201070785522461, h_largest_denorm.to_f64()); |
| 4131 | + |
| 4132 | + let h_pos_inf = Half::INFINITY; |
| 4133 | + assert_eq!(f64::INFINITY, h_pos_inf.to_f64()); |
| 4134 | + let h_neg_inf = -Half::INFINITY; |
| 4135 | + assert_eq!(-f64::INFINITY, h_neg_inf.to_f64()); |
| 4136 | + let h_qnan = Half::qnan(None); |
| 4137 | + assert!(h_qnan.to_f64().is_nan()); |
| 4138 | + |
| 4139 | + let b_pos_zero = Half::ZERO; |
| 4140 | + assert!(Double::from_f64(b_pos_zero.to_f64()).is_pos_zero()); |
| 4141 | + let b_neg_zero = -Half::ZERO; |
| 4142 | + assert!(Double::from_f64(b_neg_zero.to_f64()).is_neg_zero()); |
| 4143 | + |
| 4144 | + let b_one = "1.0".parse::<BFloat>().unwrap(); |
| 4145 | + assert_eq!(1.0, b_one.to_f64()); |
| 4146 | + let b_pos_largest = BFloat::largest(); |
| 4147 | + assert_eq!(/*0x1.FEp127*/ 3.3895313892515355e+38, b_pos_largest.to_f64()); |
| 4148 | + let b_neg_largest = -BFloat::largest(); |
| 4149 | + assert_eq!(/*-0x1.FEp127*/ -3.3895313892515355e+38, b_neg_largest.to_f64()); |
| 4150 | + let b_pos_smallest = BFloat::smallest_normalized(); |
| 4151 | + assert_eq!(/*0x1.p-126*/ 1.1754943508222875e-38, b_pos_smallest.to_f64()); |
| 4152 | + let b_neg_smallest = -BFloat::smallest_normalized(); |
| 4153 | + assert_eq!(/*-0x1.p-126*/ -1.1754943508222875e-38, b_neg_smallest.to_f64()); |
| 4154 | + |
| 4155 | + let b_smallest_denorm = BFloat::SMALLEST; |
| 4156 | + assert_eq!(/*0x1.p-133*/ 9.183549615799121e-41, b_smallest_denorm.to_f64()); |
| 4157 | + let b_largest_denorm = "0x1.FCp-127".parse::<BFloat>().unwrap(); |
| 4158 | + assert_eq!(/*0x1.FCp-127*/ 1.1663108012064884e-38, b_largest_denorm.to_f64()); |
| 4159 | + |
| 4160 | + let b_pos_inf = BFloat::INFINITY; |
| 4161 | + assert_eq!(f64::INFINITY, b_pos_inf.to_f64()); |
| 4162 | + let b_neg_inf = -BFloat::INFINITY; |
| 4163 | + assert_eq!(-f64::INFINITY, b_neg_inf.to_f64()); |
| 4164 | + let b_qnan = BFloat::qnan(None); |
| 4165 | + assert!(b_qnan.to_f64().is_nan()); |
| 4166 | +} |
| 4167 | + |
| 4168 | +#[test] |
| 4169 | +fn to_f32() { |
| 4170 | + let f_pos_zero = Single::from_f32(0.0); |
| 4171 | + assert!(Single::from_f32(f_pos_zero.to_f32()).is_pos_zero()); |
| 4172 | + let f_neg_zero = Single::from_f32(-0.0); |
| 4173 | + assert!(Single::from_f32(f_neg_zero.to_f32()).is_neg_zero()); |
| 4174 | + |
| 4175 | + let f_one = Single::from_f32(1.0); |
| 4176 | + assert_eq!(1.0, f_one.to_f32()); |
| 4177 | + let f_pos_largest = Single::largest(); |
| 4178 | + assert_eq!(f32::MAX, f_pos_largest.to_f32()); |
| 4179 | + let f_neg_largest = -Single::largest(); |
| 4180 | + assert_eq!(-f32::MAX, f_neg_largest.to_f32()); |
| 4181 | + let f_pos_smallest = Single::smallest_normalized(); |
| 4182 | + assert_eq!(f32::MIN_POSITIVE, f_pos_smallest.to_f32()); |
| 4183 | + let f_neg_smallest = -Single::smallest_normalized(); |
| 4184 | + assert_eq!(-f32::MIN_POSITIVE, f_neg_smallest.to_f32()); |
| 4185 | + |
| 4186 | + let f_smallest_denorm = Single::SMALLEST; |
| 4187 | + assert_eq!(f32_smallest_subnormal(), f_smallest_denorm.to_f32()); |
| 4188 | + let f_largest_denorm = "0x1.FFFFFEp-126".parse::<Single>().unwrap(); |
| 4189 | + assert_eq!(/*0x1.FFFFFEp-126*/ 2.3509885615147286e-38, f_largest_denorm.to_f32()); |
| 4190 | + |
| 4191 | + let f_pos_inf = Single::INFINITY; |
| 4192 | + assert_eq!(f32::INFINITY, f_pos_inf.to_f32()); |
| 4193 | + let f_neg_inf = -Single::INFINITY; |
| 4194 | + assert_eq!(-f32::INFINITY, f_neg_inf.to_f32()); |
| 4195 | + let f_qnan = Single::qnan(None); |
| 4196 | + assert!(f_qnan.to_f32().is_nan()); |
| 4197 | + |
| 4198 | + let h_pos_zero = Half::ZERO; |
| 4199 | + assert!(Single::from_f32(h_pos_zero.to_f32()).is_pos_zero()); |
| 4200 | + let h_neg_zero = -Half::ZERO; |
| 4201 | + assert!(Single::from_f32(h_neg_zero.to_f32()).is_neg_zero()); |
| 4202 | + |
| 4203 | + let h_one = "1.0".parse::<Half>().unwrap(); |
| 4204 | + assert_eq!(1.0, h_one.to_f32()); |
| 4205 | + let h_pos_largest = Half::largest(); |
| 4206 | + assert_eq!(/*0x1.FFCp15*/ 65504.0, h_pos_largest.to_f32()); |
| 4207 | + let h_neg_largest = -Half::largest(); |
| 4208 | + assert_eq!(/*-0x1.FFCp15*/ -65504.0, h_neg_largest.to_f32()); |
| 4209 | + let h_pos_smallest = Half::smallest_normalized(); |
| 4210 | + assert_eq!(/*0x1.p-14*/ 6.103515625e-05, h_pos_smallest.to_f32()); |
| 4211 | + let h_neg_smallest = -Half::smallest_normalized(); |
| 4212 | + assert_eq!(/*-0x1.p-14*/ -6.103515625e-05, h_neg_smallest.to_f32()); |
| 4213 | + |
| 4214 | + let h_smallest_denorm = Half::SMALLEST; |
| 4215 | + assert_eq!(/*0x1.p-24*/ 5.960464477539063e-08, h_smallest_denorm.to_f32()); |
| 4216 | + let h_largest_denorm = "0x1.FFCp-14".parse::<Half>().unwrap(); |
| 4217 | + assert_eq!(/*0x1.FFCp-14*/ 0.00012201070785522461, h_largest_denorm.to_f32()); |
| 4218 | + |
| 4219 | + let h_pos_inf = Half::INFINITY; |
| 4220 | + assert_eq!(f32::INFINITY, h_pos_inf.to_f32()); |
| 4221 | + let h_neg_inf = -Half::INFINITY; |
| 4222 | + assert_eq!(-f32::INFINITY, h_neg_inf.to_f32()); |
| 4223 | + let h_qnan = Half::qnan(None); |
| 4224 | + assert!(h_qnan.to_f32().is_nan()); |
| 4225 | + |
| 4226 | + let b_pos_zero = BFloat::ZERO; |
| 4227 | + assert!(Single::from_f32(b_pos_zero.to_f32()).is_pos_zero()); |
| 4228 | + let b_neg_zero = -BFloat::ZERO; |
| 4229 | + assert!(Single::from_f32(b_neg_zero.to_f32()).is_neg_zero()); |
| 4230 | + |
| 4231 | + let b_one = "1.0".parse::<BFloat>().unwrap(); |
| 4232 | + assert_eq!(1.0, b_one.to_f32()); |
| 4233 | + let b_pos_largest = BFloat::largest(); |
| 4234 | + assert_eq!(/*0x1.FEp127*/ 3.3895313892515355e+38, b_pos_largest.to_f32()); |
| 4235 | + let b_neg_largest = -BFloat::largest(); |
| 4236 | + assert_eq!(/*-0x1.FEp127*/ -3.3895313892515355e+38, b_neg_largest.to_f32()); |
| 4237 | + let b_pos_smallest = BFloat::smallest_normalized(); |
| 4238 | + assert_eq!(/*0x1.p-126*/ 1.1754943508222875e-38, b_pos_smallest.to_f32()); |
| 4239 | + let b_neg_smallest = -BFloat::smallest_normalized(); |
| 4240 | + assert_eq!(/*-0x1.p-126*/ -1.1754943508222875e-38, b_neg_smallest.to_f32()); |
| 4241 | + |
| 4242 | + let b_smallest_denorm = BFloat::SMALLEST; |
| 4243 | + assert_eq!(/*0x1.p-133*/ 9.183549615799121e-41, b_smallest_denorm.to_f32()); |
| 4244 | + let b_largest_denorm = "0x1.FCp-127".parse::<BFloat>().unwrap(); |
| 4245 | + assert_eq!(/*0x1.FCp-127*/ 1.1663108012064884e-38, b_largest_denorm.to_f32()); |
| 4246 | + |
| 4247 | + let b_pos_inf = BFloat::INFINITY; |
| 4248 | + assert_eq!(f32::INFINITY, b_pos_inf.to_f32()); |
| 4249 | + let b_neg_inf = -BFloat::INFINITY; |
| 4250 | + assert_eq!(-f32::INFINITY, b_neg_inf.to_f32()); |
| 4251 | + let b_qnan = BFloat::qnan(None); |
| 4252 | + assert!(b_qnan.to_f32().is_nan()); |
| 4253 | +} |
0 commit comments