Making NaN detection more robust to -ffast-math.
authorJean-Marc Valin <jmvalin@jmvalin.ca>
Tue, 10 Dec 2013 02:56:21 +0000 (21:56 -0500)
committerJean-Marc Valin <jmvalin@jmvalin.ca>
Tue, 10 Dec 2013 02:56:21 +0000 (21:56 -0500)
celt/arch.h
src/mlp.c
src/mlp_data.c
src/opus_encoder.c

index dcfba76..6693408 100644 (file)
@@ -137,6 +137,22 @@ typedef float celt_sig;
 typedef float celt_norm;
 typedef float celt_ener;
 
+#ifdef FLOAT_APPROX
+/* This code should reliably detect NaN/inf even when -ffast-math is used.
+   Assumes IEEE 754 format. */
+static inline int celt_isnan(float x)
+{
+   union {float f; opus_uint32 i;} in;
+   in.f = x;
+   return ((in.i>>23)&0xFF)==0xFF && (in.i&0x007FFFFF)!=0;
+}
+#else
+#ifdef __FAST_MATH__
+#error Cannot build libopus with -ffast-math unless FLOAT_APPROX is defined. This could result in crashes on extreme (e.g. NaN) input
+#endif
+#define celt_isnan(x) ((x)!=(x))
+#endif
+
 #define Q15ONE 1.0f
 
 #define NORM_SCALING 1.f
index 4638602..524e771 100644 (file)
--- a/src/mlp.c
+++ b/src/mlp.c
@@ -75,6 +75,11 @@ static OPUS_INLINE float tansig_approx(float x)
         return 1;
     if (!(x>-8))
         return -1;
+#ifndef FIXED_POINT
+    /* Another check in case of -ffast-math */
+    if (celt_isnan(x))
+       return 0;
+#endif
        if (x<0)
        {
           x=-x;
index 401c4c0..c2fda4e 100644 (file)
@@ -1,6 +1,10 @@
 /* The contents of this file was automatically generated by mlp_train.c
    It contains multi-layer perceptron (MLP) weights. */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include "mlp.h"
 
 /* RMS error was 0.138320, seed was 1361535663 */
index c819bb1..411e3d2 100644 (file)
@@ -1452,7 +1452,7 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_
        sum = celt_inner_prod(&pcm_buf[total_buffer*st->channels], &pcm_buf[total_buffer*st->channels], frame_size*st->channels);
        /* This should filter out both NaNs and ridiculous signals that could
           cause NaNs further down. */
-       if (!(sum < 1e9))
+       if (!(sum < 1e9) || celt_isnan(sum))
           OPUS_CLEAR(&pcm_buf[total_buffer*st->channels], frame_size*st->channels);
     }
 #endif