fcvt.d.ld

LBT intrinsic · group: x86 FPU

Synopsis

Instruction: fcvt.d.ld fd, fj, fk
CPU Flags: LBT

Description

Convert x87 80-bit extended precision value (upper 16 bits in fj, lower 64 bits in fk) to double-precision floating point, store in fd.

Operation

uint64_t lo = a; // J[63] | fraction[62:0]
uint64_t hi = b; // sign[15] | exponent[14:0]

unsigned sign = (hi >> 15) & 1;
int64_t exp_x87 = hi & 0x7FFF;
unsigned j_bit = (lo >> 63) & 1;
uint64_t man_x87 = lo & 0x7FFFFFFFFFFFFFFFULL;

// Build the full 64-bit significand: J_bit.fraction
uint64_t full = ((uint64_t)j_bit << 63) | man_x87;

// Re-bias exponent from x87 extended (16383) to double (1023).
// Round 63-bit fraction to 52 bits with round-to-nearest-even.
unsigned exp_d;
uint64_t man_d;
if (exp_x87 == 0x7FFF && j_bit == 1) {
  // True infinity or NaN with J=1.  Hardware always produces a
  // quiet NaN by setting bit 51 of the mantissa.
  man_d = man_x87 >> 11;
  man_d |= (1ULL << 51); // ensure QNaN
  if (man_x87 == 0) {
    // x87 infinity (fraction is zero) → double infinity (mantissa zero)
    man_d = 0;
  }
  exp_d = 0x7FF;
} else if (exp_x87 == 0 && j_bit == 0) {
  // Pseudo-denormal or zero: J=0 with zero exponent.  Hardware
  // treats all of these as zero (pseudo-denormals flush to zero).
  exp_d = 0;
  man_d = 0;
} else if (j_bit == 0) {
  // Unnormal or pseudo-NaN: J=0 with non-zero exponent is an
  // invalid x87 encoding.  Hardware produces the default quiet
  // NaN (sign=0, mantissa=0x8000000000000, exponent=0x7FF).
  sign = 0;
  exp_d = 0x7FF;
  man_d = 0x8000000000000ULL;
} else {
  int64_t rebias = exp_x87 - 16383 + 1023;
  if (rebias >= 0x7FF) {
    // Overflow to infinity.
    exp_d = 0x7FF;
    man_d = 0;
  } else if (rebias <= 0) {
    // Underflow: produce double denormal.
    // Right-shift the full 64-bit significand, then RNE to 52 bits.
    int shift = 1 - (int)rebias;
    exp_d = 0;
    int rshift =
        11 + shift; // total right-shift for 64-bit -> denormal mantissa

    // Handle rshift >= 64 separately: all bits discarded.
    if (rshift >= 64) {
      // Result before rounding is zero.  RNE rounds up to the smallest
      // denormal (man_d=1) only when rshift == 64 and full > 2^63
      // (guard=1, sticky=1 -> round up).  Ties (full == 2^63, guard=1,
      // sticky=0, LSB=0) and all rshift > 64 cases round down to 0.
      man_d = (rshift == 64 && full > (1ULL << 63)) ? 1 : 0;
    } else {
      uint64_t result = full >> rshift;
      uint64_t lost = full & ((1ULL << rshift) - 1);
      uint64_t half = 1ULL << (rshift - 1);

      // Round-to-nearest-even on the discarded bits
      if (lost > half || (lost == half && (result & 1))) {
        result++;
      }

      // If rounding overflowed past the denormal boundary, it becomes
      // the smallest normal number (biased exponent 1, mantissa 0).
      if (result & ((uint64_t)1 << 52)) {
        // overflow to smallest normal
        exp_d = 1;
        man_d = 0;
      } else {
        man_d = result & 0x000FFFFFFFFFFFFFULL;
      }
    }
  } else {
    // Normal case: round 64-bit significand to 53 bits, then strip
    // the implicit leading 1 to get the 52-bit double mantissa.
    exp_d = (unsigned)rebias;
    uint64_t result53 = full >> 11;  // top 53 bits (bit 52 = implicit 1)
    uint64_t lost = full & 0x7FFULL; // bottom 11 bits
    uint64_t half = 0x400ULL;

    // Round-to-nearest-even
    if (lost > half || (lost == half && (result53 & 1))) {
      result53++;
      if (result53 & (1ULL << 53)) {
        // Carry into bit 53 → significand wrapped to 2.0.
        // Renormalize and adjust exponent.
        result53 >>= 1;
        exp_d++;
        if (exp_d >= 0x7FF) {
          // Overflowed to infinity.
          exp_d = 0x7FF;
          result53 = 0;
        }
      }
    }
    man_d = result53 & 0x000FFFFFFFFFFFFFULL;
  }
}

// Recombine: sign[63] | exponent[62:52] | fraction[51:0]
dst = ((uint64_t)sign << 63) | ((uint64_t)exp_d << 52) | man_d;

Tested on real machine.

Latency and Throughput

CPU µarch Latency Throughput (IPC)
3C5000 LA464 4 2
3C6000 LA664 4 4

All intrinsics in alphabetical order