in math-emu/fp_arith.c [450:570]
static void fp_roundint(struct fp_ext *dest, int mode)
{
union fp_mant64 oldmant;
unsigned long mask;
if (!fp_normalize_ext(dest))
return;
/* infinities and zeroes */
if (IS_INF(dest) || IS_ZERO(dest))
return;
/* first truncate the lower bits */
oldmant = dest->mant;
switch (dest->exp) {
case 0 ... 0x3ffe:
dest->mant.m64 = 0;
break;
case 0x3fff ... 0x401e:
dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp);
dest->mant.m32[1] = 0;
if (oldmant.m64 == dest->mant.m64)
return;
break;
case 0x401f ... 0x403e:
dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp);
if (oldmant.m32[1] == dest->mant.m32[1])
return;
break;
default:
return;
}
fp_set_sr(FPSR_EXC_INEX2);
/* We might want to normalize upwards here... however, since
we know that this is only called on the output of fp_fdiv,
or with the input to fp_fint or fp_fintrz, and the inputs
to all these functions are either normal or denormalized
(no subnormals allowed!), there's really no need.
In the case of fp_fdiv, observe that 0x80000000 / 0xffff =
0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the
smallest possible normal dividend and the largest possible normal
divisor will still produce a normal quotient, therefore, (normal
<< 64) / normal is normal in all cases) */
switch (mode) {
case FPCR_ROUND_RN:
switch (dest->exp) {
case 0 ... 0x3ffd:
return;
case 0x3ffe:
/* As noted above, the input is always normal, so the
guard bit (bit 63) is always set. therefore, the
only case in which we will NOT round to 1.0 is when
the input is exactly 0.5. */
if (oldmant.m64 == (1ULL << 63))
return;
break;
case 0x3fff ... 0x401d:
mask = 1 << (0x401d - dest->exp);
if (!(oldmant.m32[0] & mask))
return;
if (oldmant.m32[0] & (mask << 1))
break;
if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) &&
!oldmant.m32[1])
return;
break;
case 0x401e:
if (oldmant.m32[1] & 0x80000000)
return;
if (oldmant.m32[0] & 1)
break;
if (!(oldmant.m32[1] << 1))
return;
break;
case 0x401f ... 0x403d:
mask = 1 << (0x403d - dest->exp);
if (!(oldmant.m32[1] & mask))
return;
if (oldmant.m32[1] & (mask << 1))
break;
if (!(oldmant.m32[1] << (dest->exp - 0x401d)))
return;
break;
default:
return;
}
break;
case FPCR_ROUND_RZ:
return;
default:
if (dest->sign ^ (mode - FPCR_ROUND_RM))
break;
return;
}
switch (dest->exp) {
case 0 ... 0x3ffe:
dest->exp = 0x3fff;
dest->mant.m64 = 1ULL << 63;
break;
case 0x3fff ... 0x401e:
mask = 1 << (0x401e - dest->exp);
if (dest->mant.m32[0] += mask)
break;
dest->mant.m32[0] = 0x80000000;
dest->exp++;
break;
case 0x401f ... 0x403e:
mask = 1 << (0x403e - dest->exp);
if (dest->mant.m32[1] += mask)
break;
if (dest->mant.m32[0] += 1)
break;
dest->mant.m32[0] = 0x80000000;
dest->exp++;
break;
}
}