|
| 1 | +use float::Float; |
| 2 | +use int::Int; |
| 3 | + |
| 4 | +macro_rules! fp_overflow { |
| 5 | + (infinity, $fty:ty, $sign: expr) => { |
| 6 | + return { |
| 7 | + <$fty as Float>::from_parts( |
| 8 | + $sign, |
| 9 | + <$fty as Float>::exponent_max() as <$fty as Float>::Int, |
| 10 | + 0 as <$fty as Float>::Int) |
| 11 | + } |
| 12 | + } |
| 13 | +} |
| 14 | + |
| 15 | +macro_rules! fp_convert { |
| 16 | + ($intrinsic:ident: $ity:ty, $fty:ty) => { |
| 17 | + |
| 18 | + pub extern "C" fn $intrinsic(i: $ity) -> $fty { |
| 19 | + if i == 0 { |
| 20 | + return 0.0 |
| 21 | + } |
| 22 | + |
| 23 | + let mant_dig = <$fty>::significand_bits() + 1; |
| 24 | + let exponent_bias = <$fty>::exponent_bias(); |
| 25 | + |
| 26 | + let n = <$ity>::bits(); |
| 27 | + let (s, a) = i.extract_sign(); |
| 28 | + let mut a = a; |
| 29 | + |
| 30 | + // number of significant digits |
| 31 | + let sd = n - a.leading_zeros(); |
| 32 | + |
| 33 | + // exponent |
| 34 | + let mut e = sd - 1; |
| 35 | + |
| 36 | + if <$ity>::bits() < mant_dig { |
| 37 | + return <$fty>::from_parts(s, |
| 38 | + (e + exponent_bias) as <$fty as Float>::Int, |
| 39 | + (a as <$fty as Float>::Int) << (mant_dig - e - 1)) |
| 40 | + } |
| 41 | + |
| 42 | + a = if sd > mant_dig { |
| 43 | + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx |
| 44 | + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR |
| 45 | + * 12345678901234567890123456 |
| 46 | + * 1 = msb 1 bit |
| 47 | + * P = bit MANT_DIG-1 bits to the right of 1 |
| 48 | + * Q = bit MANT_DIG bits to the right of 1 |
| 49 | + * R = "or" of all bits to the right of Q |
| 50 | + */ |
| 51 | + let mant_dig_plus_one = mant_dig + 1; |
| 52 | + let mant_dig_plus_two = mant_dig + 2; |
| 53 | + a = if sd == mant_dig_plus_one { |
| 54 | + a << 1 |
| 55 | + } else if sd == mant_dig_plus_two { |
| 56 | + a |
| 57 | + } else { |
| 58 | + (a >> (sd - mant_dig_plus_two)) as <$ity as Int>::UnsignedInt | |
| 59 | + ((a & <$ity as Int>::UnsignedInt::max_value()).wrapping_shl((n + mant_dig_plus_two) - sd) != 0) as <$ity as Int>::UnsignedInt |
| 60 | + }; |
| 61 | + |
| 62 | + /* finish: */ |
| 63 | + a |= ((a & 4) != 0) as <$ity as Int>::UnsignedInt; /* Or P into R */ |
| 64 | + a += 1; /* round - this step may add a significant bit */ |
| 65 | + a >>= 2; /* dump Q and R */ |
| 66 | + |
| 67 | + /* a is now rounded to mant_dig or mant_dig+1 bits */ |
| 68 | + if (a & (1 << mant_dig)) != 0 { |
| 69 | + a >>= 1; e += 1; |
| 70 | + } |
| 71 | + a |
| 72 | + /* a is now rounded to mant_dig bits */ |
| 73 | + } else { |
| 74 | + a.wrapping_shl(mant_dig - sd) |
| 75 | + /* a is now rounded to mant_dig bits */ |
| 76 | + }; |
| 77 | + |
| 78 | + <$fty>::from_parts(s, |
| 79 | + (e + exponent_bias) as <$fty as Float>::Int, |
| 80 | + a as <$fty as Float>::Int) |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +fp_convert!(__floatsisf: i32, f32); |
| 86 | +fp_convert!(__floatsidf: i32, f64); |
| 87 | +fp_convert!(__floatdidf: i64, f64); |
| 88 | +fp_convert!(__floatunsisf: u32, f32); |
| 89 | +fp_convert!(__floatunsidf: u32, f64); |
| 90 | +fp_convert!(__floatundidf: u64, f64); |
| 91 | + |
| 92 | +#[derive(PartialEq, Debug)] |
| 93 | +enum Sign { |
| 94 | + Positive, |
| 95 | + Negative |
| 96 | +} |
| 97 | +macro_rules! fp_fix { |
| 98 | + ($intrinsic:ident: $fty:ty, $ity:ty) => { |
| 99 | + pub extern "C" fn $intrinsic(f: $fty) -> $ity { |
| 100 | + let fixint_min = <$ity>::min_value(); |
| 101 | + let fixint_max = <$ity>::max_value(); |
| 102 | + let fixint_bits = <$ity>::bits() as usize; |
| 103 | + let fixint_unsigned = fixint_min == 0; |
| 104 | + |
| 105 | + let sign_bit = <$fty>::sign_mask(); |
| 106 | + let significand_bits = <$fty>::significand_bits() as usize; |
| 107 | + let exponent_bias = <$fty>::exponent_bias() as usize; |
| 108 | + //let exponent_max = <$fty>::exponent_max() as usize; |
| 109 | + |
| 110 | + // Break a into sign, exponent, significand |
| 111 | + let a_rep = <$fty>::repr(f); |
| 112 | + let a_abs = a_rep & !sign_bit; |
| 113 | + |
| 114 | + // this is used to work around -1 not being available for unsigned |
| 115 | + let sign = if (a_rep & sign_bit) == 0 { Sign::Positive } else { Sign::Negative }; |
| 116 | + let mut exponent = (a_abs >> significand_bits) as usize; |
| 117 | + let significand = (a_abs & <$fty>::significand_mask()) | <$fty>::implicit_bit(); |
| 118 | + |
| 119 | + // if < 1 or unsigned & negative |
| 120 | + if exponent < exponent_bias || |
| 121 | + fixint_unsigned && sign == Sign::Negative { |
| 122 | + return 0 |
| 123 | + } |
| 124 | + exponent -= exponent_bias; |
| 125 | + |
| 126 | + // If the value is infinity, saturate. |
| 127 | + // If the value is too large for the integer type, 0. |
| 128 | + if exponent >= (if fixint_unsigned {fixint_bits} else {fixint_bits -1}) { |
| 129 | + return if sign == Sign::Positive {fixint_max} else {fixint_min} |
| 130 | + } |
| 131 | + // If 0 <= exponent < significand_bits, right shift to get the result. |
| 132 | + // Otherwise, shift left. |
| 133 | + // (sign - 1) will never overflow as negative signs are already returned as 0 for unsigned |
| 134 | + let r = if exponent < significand_bits { |
| 135 | + (significand >> (significand_bits - exponent)) as $ity |
| 136 | + } else { |
| 137 | + (significand as $ity) << (exponent - significand_bits) |
| 138 | + }; |
| 139 | + |
| 140 | + if sign == Sign::Negative { |
| 141 | + (!r).wrapping_add(1) |
| 142 | + } else { |
| 143 | + r |
| 144 | + } |
| 145 | + } |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +fp_fix!(__fixsfsi: f32, i32); |
| 150 | +fp_fix!(__fixsfdi: f32, i64); |
| 151 | +fp_fix!(__fixdfsi: f64, i32); |
| 152 | +fp_fix!(__fixdfdi: f64, i64); |
| 153 | + |
| 154 | +fp_fix!(__fixunssfsi: f32, u32); |
| 155 | +fp_fix!(__fixunssfdi: f32, u64); |
| 156 | +fp_fix!(__fixunsdfsi: f64, u32); |
| 157 | +fp_fix!(__fixunsdfdi: f64, u64); |
| 158 | + |
| 159 | +// NOTE(cfg) for some reason, on arm*-unknown-linux-gnueabihf, our implementation doesn't |
| 160 | +// match the output of its gcc_s or compiler-rt counterpart. Until we investigate further, we'll |
| 161 | +// just avoid testing against them on those targets. Do note that our implementation gives the |
| 162 | +// correct answer; gcc_s and compiler-rt are incorrect in this case. |
| 163 | +// |
| 164 | +#[cfg(all(test, not(arm_linux)))] |
| 165 | +mod tests { |
| 166 | + use qc::{I32, U32, I64, U64, F32, F64}; |
| 167 | + |
| 168 | + check! { |
| 169 | + fn __floatsisf(f: extern "C" fn(i32) -> f32, |
| 170 | + a: I32) |
| 171 | + -> Option<F32> { |
| 172 | + Some(F32(f(a.0))) |
| 173 | + } |
| 174 | + fn __floatsidf(f: extern "C" fn(i32) -> f64, |
| 175 | + a: I32) |
| 176 | + -> Option<F64> { |
| 177 | + Some(F64(f(a.0))) |
| 178 | + } |
| 179 | + fn __floatdidf(f: extern "C" fn(i64) -> f64, |
| 180 | + a: I64) |
| 181 | + -> Option<F64> { |
| 182 | + Some(F64(f(a.0))) |
| 183 | + } |
| 184 | + fn __floatunsisf(f: extern "C" fn(u32) -> f32, |
| 185 | + a: U32) |
| 186 | + -> Option<F32> { |
| 187 | + Some(F32(f(a.0))) |
| 188 | + } |
| 189 | + fn __floatunsidf(f: extern "C" fn(u32) -> f64, |
| 190 | + a: U32) |
| 191 | + -> Option<F64> { |
| 192 | + Some(F64(f(a.0))) |
| 193 | + } |
| 194 | + fn __floatundidf(f: extern "C" fn(u64) -> f64, |
| 195 | + a: U64) |
| 196 | + -> Option<F64> { |
| 197 | + Some(F64(f(a.0))) |
| 198 | + } |
| 199 | + |
| 200 | + fn __fixsfsi(f: extern "C" fn(f32) -> i32, |
| 201 | + a: F32) |
| 202 | + -> Option<I32> { |
| 203 | + if a.0 > (i32::max_value() as f32) || |
| 204 | + a.0 < (i32::min_value() as f32) || a.0.is_nan() { |
| 205 | + None |
| 206 | + } else { Some(I32(f(a.0))) } |
| 207 | + } |
| 208 | + fn __fixsfdi(f: extern "C" fn(f32) -> i64, |
| 209 | + a: F32) |
| 210 | + -> Option<I64> { |
| 211 | + if a.0 > (i64::max_value() as f32) || |
| 212 | + a.0 < (i64::min_value() as f32) || a.0.is_nan() { |
| 213 | + None |
| 214 | + } else { Some(I64(f(a.0))) } |
| 215 | + } |
| 216 | + fn __fixdfsi(f: extern "C" fn(f64) -> i32, |
| 217 | + a: F64) |
| 218 | + -> Option<I32> { |
| 219 | + if a.0 > (i32::max_value() as f64) || |
| 220 | + a.0 < (i32::min_value() as f64) || a.0.is_nan() { |
| 221 | + None |
| 222 | + } else { Some(I32(f(a.0))) } |
| 223 | + } |
| 224 | + fn __fixdfdi(f: extern "C" fn(f64) -> i64, |
| 225 | + a: F64) |
| 226 | + -> Option<I64> { |
| 227 | + if a.0 > (i64::max_value() as f64) || |
| 228 | + a.0 < (i64::min_value() as f64) || a.0.is_nan() { |
| 229 | + None |
| 230 | + } else { Some(I64(f(a.0))) } |
| 231 | + } |
| 232 | + |
| 233 | + fn __fixunssfsi(f: extern "C" fn(f32) -> u32, |
| 234 | + a: F32) |
| 235 | + -> Option<U32> { |
| 236 | + if a.0 > (u32::max_value() as f32) || |
| 237 | + a.0 < (u32::min_value() as f32) || a.0.is_nan() { |
| 238 | + None |
| 239 | + } else { Some(U32(f(a.0))) } |
| 240 | + } |
| 241 | + fn __fixunssfdi(f: extern "C" fn(f32) -> u64, |
| 242 | + a: F32) |
| 243 | + -> Option<U64> { |
| 244 | + if a.0 > (u64::max_value() as f32) || |
| 245 | + a.0 < (u64::min_value() as f32) || a.0.is_nan() { |
| 246 | + None |
| 247 | + } else { Some(U64(f(a.0))) } |
| 248 | + } |
| 249 | + fn __fixunsdfsi(f: extern "C" fn(f64) -> u32, |
| 250 | + a: F64) |
| 251 | + -> Option<U32> { |
| 252 | + if a.0 > (u32::max_value() as f64) || |
| 253 | + a.0 < (u32::min_value() as f64) || a.0.is_nan() { |
| 254 | + None |
| 255 | + } else { Some(U32(f(a.0))) } |
| 256 | + } |
| 257 | + fn __fixunsdfdi(f: extern "C" fn(f64) -> u64, |
| 258 | + a: F64) |
| 259 | + -> Option<U64> { |
| 260 | + if a.0 <= (u64::max_value() as f64) || |
| 261 | + a.0 >= (u64::min_value() as f64) || a.0.is_nan() { |
| 262 | + None |
| 263 | + } else { Some(U64(f(a.0))) } |
| 264 | + } |
| 265 | + } |
| 266 | +} |
0 commit comments