return float64_pack_raw(p);
}
-static float64 float64r32_round_pack_canonical(FloatParts64 *p,
- float_status *s)
+static float64 float64r32_pack_raw(FloatParts64 *p)
{
- parts_uncanon(p, s, &float32_params);
-
/*
* In parts_uncanon, we placed the fraction for float32 at the lsb.
* We need to adjust the fraction higher so that the least N bits are
return float64_pack_raw(p);
}
+static float64 float64r32_round_pack_canonical(FloatParts64 *p,
+ float_status *s)
+{
+ parts_uncanon(p, s, &float32_params);
+ return float64r32_pack_raw(p);
+}
+
static void float128_unpack_canonical(FloatParts128 *p, float128 f,
float_status *s)
{
float16_unpack_canonical(&pc, c, status);
pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
- return float16_round_pack_canonical(pr, status);
+ /* Round before applying negate result. */
+ parts_uncanon(pr, status, &float16_params);
+ if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
+ pr->sign ^= 1;
+ }
+ return float16_pack_raw(pr);
}
float16 float16_muladd(float16 a, float16 b, float16 c,
float32_unpack_canonical(&pc, c, status);
pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
- return float32_round_pack_canonical(pr, status);
+ /* Round before applying negate result. */
+ parts_uncanon(pr, status, &float32_params);
+ if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
+ pr->sign ^= 1;
+ }
+ return float32_pack_raw(pr);
}
float64 QEMU_SOFTFLOAT_ATTR
float64_unpack_canonical(&pc, c, status);
pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
- return float64_round_pack_canonical(pr, status);
+ /* Round before applying negate result. */
+ parts_uncanon(pr, status, &float64_params);
+ if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
+ pr->sign ^= 1;
+ }
+ return float64_pack_raw(pr);
}
static bool force_soft_fma;
float64_unpack_canonical(&pc, c, status);
pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
- return float64r32_round_pack_canonical(pr, status);
+ /* Round before applying negate result. */
+ parts_uncanon(pr, status, &float32_params);
+ if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
+ pr->sign ^= 1;
+ }
+ return float64r32_pack_raw(pr);
}
bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c,
bfloat16_unpack_canonical(&pc, c, status);
pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
- return bfloat16_round_pack_canonical(pr, status);
+ /* Round before applying negate result. */
+ parts_uncanon(pr, status, &bfloat16_params);
+ if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
+ pr->sign ^= 1;
+ }
+ return bfloat16_pack_raw(pr);
}
float128 QEMU_FLATTEN float128_muladd(float128 a, float128 b, float128 c,
float128_unpack_canonical(&pc, c, status);
pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
- return float128_round_pack_canonical(pr, status);
+ /* Round before applying negate result. */
+ parts_uncanon(pr, status, &float128_params);
+ if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
+ pr->sign ^= 1;
+ }
+ return float128_pack_raw(pr);
}
/*
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <stdio.h>
+#include <math.h>
+#include <fenv.h>
+
+union U {
+ double d;
+ unsigned long long l;
+};
+
+union U x = { .l = 0x4ff0000000000000ULL };
+union U y = { .l = 0x2ff0000000000000ULL };
+union U r;
+
+int main()
+{
+#ifdef FE_DOWNWARD
+ fesetround(FE_DOWNWARD);
+
+#if defined(__loongarch__)
+ asm("fnmsub.d %0, %1, %1, %2" : "=f"(r.d) : "f"(x.d), "f"(y.d));
+#elif defined(__powerpc64__)
+ asm("fnmsub %0,%1,%1,%2" : "=f"(r.d) : "f"(x.d), "f"(y.d));
+#elif defined(__s390x__) && 0 /* need -march=z14 */
+ asm("vfnms %0,%1,%1,%2,0,3" : "=f"(r.d) : "f"(x.d), "f"(y.d));
+#else
+ r.d = -fma(x.d, x.d, -y.d);
+#endif
+
+ if (r.l != 0xdfefffffffffffffULL) {
+ printf("r = %.18a (%016llx)\n", r.d, r.l);
+ return 1;
+ }
+#endif
+ return 0;
+}