///|
pub fn compat_is_f32_nan(x : Float) -> Bool {
  let bits = x.reinterpret_as_uint()
  let exp = bits & 0x7F800000U
  let frac = bits & 0x007FFFFFU
  exp == 0x7F800000U && frac != 0U
}

///|
pub fn compat_is_f64_nan(x : Double) -> Bool {
  let bits = x.reinterpret_as_uint64()
  let exp = bits & 0x7FF0000000000000UL
  let frac = bits & 0x000FFFFFFFFFFFFFUL
  exp == 0x7FF0000000000000UL && frac != 0UL
}

///|
pub fn compat_is_f64_non_finite(x : Double) -> Bool {
  (x.reinterpret_as_uint64() & 0x7FF0000000000000UL) == 0x7FF0000000000000UL
}

///|
fn compat_trunc_f64_to_u64_nonneg(x : Double) -> UInt64 {
  let two_pow_32 = 4294967296.0
  let hi = (x / two_pow_32).trunc().to_uint64()
  let lo = (x - hi.to_double() * two_pow_32).to_uint64()
  (hi << 32) | lo
}

///|
pub fn compat_trunc_f64_to_i64_s(x : Double) -> Int64? {
  if compat_is_f64_non_finite(x) {
    return None
  }
  let t = x.trunc()
  if t < -9223372036854775808.0 || t >= 9223372036854775808.0 {
    None
  } else if t < 0.0 {
    if t == -9223372036854775808.0 {
      Some(-9223372036854775808L)
    } else {
      let abs_u = compat_trunc_f64_to_u64_nonneg(-t)
      Some(-abs_u.reinterpret_as_int64())
    }
  } else {
    Some(compat_trunc_f64_to_u64_nonneg(t).reinterpret_as_int64())
  }
}

///|
pub fn compat_trunc_f64_to_i64_u(x : Double) -> Int64? {
  if compat_is_f64_non_finite(x) {
    return None
  }
  let t = x.trunc()
  if t < 0.0 || t >= 18446744073709551616.0 {
    None
  } else {
    Some(compat_trunc_f64_to_u64_nonneg(t).reinterpret_as_int64())
  }
}