Implement proper floating point negation

Using "-0.0 - x" still isn't enough if x is a NaN, so bite the bullet
and implement sign-bit flipping via memory.  (It's usually not too bad,
the -0.0-x method also uses memory for the floating point constant).

This way is at least shorter than implementing a new top-level operation
for all backends (a negate) that would be unary.
This commit is contained in:
Michael Matz 2021-01-04 03:58:22 +01:00
parent 4c9516941c
commit 29d8871d61
2 changed files with 48 additions and 13 deletions

View file

@ -5880,19 +5880,47 @@ ST_FUNC void unary(void)
unary();
t = vtop->type.t & VT_BTYPE;
if (is_float(t)) {
/* In IEEE negate(x) isn't subtract(0,x), but rather
subtract(-0, x). */
vpush(&vtop->type);
if (t == VT_FLOAT)
vtop->c.f = -1.0 * 0.0;
else if (t == VT_DOUBLE)
vtop->c.d = -1.0 * 0.0;
else
vtop->c.ld = -1.0 * 0.0;
} else
vpushi(0);
vswap();
gen_op('-');
if ((vtop->r & VT_VALMASK) == VT_CONST) {
/* This is what gen_opif would do if we had a NEG operation. */
if (t == VT_FLOAT)
vtop->c.f = -vtop->c.f;
else if (t == VT_DOUBLE)
vtop->c.d = -vtop->c.d;
else
vtop->c.ld = -vtop->c.ld;
} else {
/* In IEEE negate(x) isn't subtract(0,x). Without NaNs it's
subtract(-0, x), but with them it's really a sign flip
operation. We implement this with bit manipulation and have
to do some type reinterpretation for this, which TCC can do
only via memory. */
int align, size = type_size(&vtop->type, &align);
save_reg(gv(RC_TYPE(t)));
vdup();
gaddrof();
vtop->type = char_pointer_type;
/* Byte of sign bit. For big endian, this would have to
add zero always. */
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_I386)
/* sizeof long double is 12 or 16 here, but it's
really the 80bit extended float format. */
if (t == VT_LDOUBLE)
size = 10;
#endif
vpushi(size - 1);
gen_op('+');
indir();
vdup();
vpushi(0x80); /* flip sign */
gen_op('^');
vstore();
vpop();
}
} else {
vpushi(0);
vswap();
gen_op('-');
}
break;
case TOK_LAND:
if (!gnu_ext)

View file

@ -2227,6 +2227,12 @@ void prefix ## signed_zeros(void) \
else\
printf ("x != -y; this is wrong!\n");\
}\
void prefix ## nan(void)\
{\
type nan = 0.0/0.0;\
type nnan = -nan; \
printf("nantest: " fmt " " fmt "\n", nan, nnan);\
}\
void prefix ## test(void)\
{\
printf("testing '%s'\n", #typename);\
@ -2237,6 +2243,7 @@ void prefix ## test(void)\
prefix ## fcast(-2334.6);\
prefix ## call();\
prefix ## signed_zeros();\
prefix ## nan();\
}
FTEST(f, float, float, "%f")