diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index ae6ba71854..a362bf89ca 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -4498,7 +4498,9 @@ floatx80 float32_to_floatx80(float32 a, float_status *status)
     aSign = extractFloat32Sign( a );
     if ( aExp == 0xFF ) {
         if (aSig) {
-            return commonNaNToFloatx80(float32ToCommonNaN(a, status), status);
+            floatx80 res = commonNaNToFloatx80(float32ToCommonNaN(a, status),
+                                               status);
+            return floatx80_silence_nan(res, status);
         }
         return packFloatx80(aSign,
                             floatx80_infinity_high,
@@ -5016,7 +5018,9 @@ floatx80 float64_to_floatx80(float64 a, float_status *status)
     aSign = extractFloat64Sign( a );
     if ( aExp == 0x7FF ) {
         if (aSig) {
-            return commonNaNToFloatx80(float64ToCommonNaN(a, status), status);
+            floatx80 res = commonNaNToFloatx80(float64ToCommonNaN(a, status),
+                                               status);
+            return floatx80_silence_nan(res, status);
         }
         return packFloatx80(aSign,
                             floatx80_infinity_high,
@@ -5618,7 +5622,9 @@ float32 floatx80_to_float32(floatx80 a, float_status *status)
     aSign = extractFloatx80Sign( a );
     if ( aExp == 0x7FFF ) {
         if ( (uint64_t) ( aSig<<1 ) ) {
-            return commonNaNToFloat32(floatx80ToCommonNaN(a, status), status);
+            float32 res = commonNaNToFloat32(floatx80ToCommonNaN(a, status),
+                                             status);
+            return float32_silence_nan(res, status);
         }
         return packFloat32( aSign, 0xFF, 0 );
     }
@@ -5650,7 +5656,9 @@ float64 floatx80_to_float64(floatx80 a, float_status *status)
     aSign = extractFloatx80Sign( a );
     if ( aExp == 0x7FFF ) {
         if ( (uint64_t) ( aSig<<1 ) ) {
-            return commonNaNToFloat64(floatx80ToCommonNaN(a, status), status);
+            float64 res = commonNaNToFloat64(floatx80ToCommonNaN(a, status),
+                                             status);
+            return float64_silence_nan(res, status);
         }
         return packFloat64( aSign, 0x7FF, 0 );
     }
@@ -5681,7 +5689,9 @@ float128 floatx80_to_float128(floatx80 a, float_status *status)
     aExp = extractFloatx80Exp( a );
     aSign = extractFloatx80Sign( a );
     if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) {
-        return commonNaNToFloat128(floatx80ToCommonNaN(a, status), status);
+        float128 res = commonNaNToFloat128(floatx80ToCommonNaN(a, status),
+                                           status);
+        return float128_silence_nan(res, status);
     }
     shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 );
     return packFloat128( aSign, aExp, zSig0, zSig1 );
@@ -5731,7 +5741,7 @@ floatx80 floatx80_round_to_int(floatx80 a, float_status *status)
     }
     if ( aExp < 0x3FFF ) {
         if (    ( aExp == 0 )
-             && ( (uint64_t) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) {
+             && ( (uint64_t) ( extractFloatx80Frac( a ) ) == 0 ) ) {
             return a;
         }
         status->float_exception_flags |= float_flag_inexact;
@@ -5856,6 +5866,12 @@ static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign,
         zSig1 = 0;
         zSig0 = aSig + bSig;
         if ( aExp == 0 ) {
+            if ((aSig | bSig) & UINT64_C(0x8000000000000000) && zSig0 < aSig) {
+                /* At least one of the values is a pseudo-denormal,
+                 * and there is a carry out of the result.  */
+                zExp = 1;
+                goto shiftRight1;
+            }
             if (zSig0 == 0) {
                 return packFloatx80(zSign, 0, 0);
             }
@@ -6959,7 +6975,9 @@ floatx80 float128_to_floatx80(float128 a, float_status *status)
     aSign = extractFloat128Sign( a );
     if ( aExp == 0x7FFF ) {
         if ( aSig0 | aSig1 ) {
-            return commonNaNToFloatx80(float128ToCommonNaN(a, status), status);
+            floatx80 res = commonNaNToFloatx80(float128ToCommonNaN(a, status),
+                                               status);
+            return floatx80_silence_nan(res, status);
         }
         return packFloatx80(aSign, floatx80_infinity_high,
                                    floatx80_infinity_low);
@@ -7948,6 +7966,13 @@ static inline int floatx80_compare_internal(floatx80 a, floatx80 b,
             return 1 - (2 * aSign);
         }
     } else {
+        /* Normalize pseudo-denormals before comparison.  */
+        if ((a.high & 0x7fff) == 0 && a.low & UINT64_C(0x8000000000000000)) {
+            ++a.high;
+        }
+        if ((b.high & 0x7fff) == 0 && b.low & UINT64_C(0x8000000000000000)) {
+            ++b.high;
+        }
         if (a.low == b.low && a.high == b.high) {
             return float_relation_equal;
         } else {
diff --git a/tests/tcg/i386/test-i386-pseudo-denormal.c b/tests/tcg/i386/test-i386-pseudo-denormal.c
new file mode 100644
index 0000000000..00d510cf4a
--- /dev/null
+++ b/tests/tcg/i386/test-i386-pseudo-denormal.c
@@ -0,0 +1,38 @@
+/* Test pseudo-denormal operations.  */
+
+#include <stdint.h>
+#include <stdio.h>
+
+union u {
+    struct { uint64_t sig; uint16_t sign_exp; } s;
+    long double ld;
+};
+
+volatile union u ld_pseudo_m16382 = { .s = { UINT64_C(1) << 63, 0 } };
+
+volatile long double ld_res;
+
+int main(void)
+{
+    short cw;
+    int ret = 0;
+    ld_res = ld_pseudo_m16382.ld + ld_pseudo_m16382.ld;
+    if (ld_res != 0x1p-16381L) {
+        printf("FAIL: pseudo-denormal add\n");
+        ret = 1;
+    }
+    if (ld_pseudo_m16382.ld != 0x1p-16382L) {
+        printf("FAIL: pseudo-denormal compare\n");
+        ret = 1;
+    }
+    /* Set round-upward.  */
+    __asm__ volatile ("fnstcw %0" : "=m" (cw));
+    cw = (cw & ~0xc00) | 0x800;
+    __asm__ volatile ("fldcw %0" : : "m" (cw));
+    __asm__ ("frndint" : "=t" (ld_res) : "0" (ld_pseudo_m16382.ld));
+    if (ld_res != 1.0L) {
+        printf("FAIL: pseudo-denormal round-to-integer\n");
+        ret = 1;
+    }
+    return ret;
+}
diff --git a/tests/tcg/i386/test-i386-snan-convert.c b/tests/tcg/i386/test-i386-snan-convert.c
new file mode 100644
index 0000000000..ed6d535ce2
--- /dev/null
+++ b/tests/tcg/i386/test-i386-snan-convert.c
@@ -0,0 +1,63 @@
+/* Test conversions of signaling NaNs to and from long double.  */
+
+#include <stdint.h>
+#include <stdio.h>
+
+volatile float f_res;
+volatile double d_res;
+volatile long double ld_res;
+
+volatile float f_snan = __builtin_nansf("");
+volatile double d_snan = __builtin_nans("");
+volatile long double ld_snan = __builtin_nansl("");
+
+int issignaling_f(float x)
+{
+    union { float f; uint32_t u; } u = { .f = x };
+    return (u.u & 0x7fffffff) > 0x7f800000 && (u.u & 0x400000) == 0;
+}
+
+int issignaling_d(double x)
+{
+    union { double d; uint64_t u; } u = { .d = x };
+    return (((u.u & UINT64_C(0x7fffffffffffffff)) >
+            UINT64_C(0x7ff0000000000000)) &&
+            (u.u & UINT64_C(0x8000000000000)) == 0);
+}
+
+int issignaling_ld(long double x)
+{
+    union {
+        long double ld;
+        struct { uint64_t sig; uint16_t sign_exp; } s;
+    } u = { .ld = x };
+    return ((u.s.sign_exp & 0x7fff) == 0x7fff &&
+            (u.s.sig >> 63) != 0 &&
+            (u.s.sig & UINT64_C(0x4000000000000000)) == 0);
+}
+
+int main(void)
+{
+    int ret = 0;
+    ld_res = f_snan;
+    if (issignaling_ld(ld_res)) {
+        printf("FAIL: float -> long double\n");
+        ret = 1;
+    }
+    ld_res = d_snan;
+    if (issignaling_ld(ld_res)) {
+        printf("FAIL: double -> long double\n");
+        ret = 1;
+    }
+    f_res = ld_snan;
+    if (issignaling_d(f_res)) {
+        printf("FAIL: long double -> float\n");
+        ret = 1;
+    }
+    d_res = ld_snan;
+    if (issignaling_d(d_res)) {
+        printf("FAIL: long double -> double\n");
+        ret = 1;
+    }
+    return ret;
+}