Improve accuracy of SIMD exp for small args
authorErik Lindahl <erik@kth.se>
Fri, 8 Sep 2017 19:03:58 +0000 (21:03 +0200)
committerMark Abraham <mark.j.abraham@gmail.com>
Tue, 12 Sep 2017 12:26:21 +0000 (14:26 +0200)
Introduce a separate check for small arguments and
set the result to zero below this cutoff. This change
also moves the exponential tests to ldexp(), which
as a side-effect also makes that function safer by
default, with an optional template parameter to avoid
the checks.

Fixes #2243.

Change-Id: I41bccaeec9921c3aead2cd2caf41cbe2206e0687

25 files changed:
src/gromacs/simd/impl_arm_neon/impl_arm_neon_simd_float.h
src/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd_double.h
src/gromacs/simd/impl_ibm_qpx/impl_ibm_qpx_simd_double.h
src/gromacs/simd/impl_ibm_qpx/impl_ibm_qpx_simd_float.h
src/gromacs/simd/impl_ibm_vmx/impl_ibm_vmx_simd_float.h
src/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_double.h
src/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_float.h
src/gromacs/simd/impl_reference/impl_reference_general.h
src/gromacs/simd/impl_reference/impl_reference_simd_double.h
src/gromacs/simd/impl_reference/impl_reference_simd_float.h
src/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd_double.h
src/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd_float.h
src/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_double.h
src/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_float.h
src/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_double.h
src/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_float.h
src/gromacs/simd/impl_x86_mic/impl_x86_mic_simd_double.h
src/gromacs/simd/impl_x86_mic/impl_x86_mic_simd_float.h
src/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_double.h
src/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_float.h
src/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_double.h
src/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_float.h
src/gromacs/simd/simd_math.h
src/gromacs/simd/tests/simd_floatingpoint.cpp
src/gromacs/simd/tests/simd_vector_operations.cpp

index 4920df2c047d2f6990c8c9549fa06b6e005c75d9..eb9367b503f1b337e863aff4c9115fa82be2b04a 100644 (file)
@@ -43,6 +43,8 @@
 
 #include <arm_neon.h>
 
+#include "gromacs/math/utilities.h"
+
 namespace gmx
 {
 
@@ -444,13 +446,20 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
     const int32x4_t exponentBias = vdupq_n_s32(127);
-    int32x4_t       iExponent;
+    int32x4_t       iExponent    = vaddq_s32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vmaxq_s32(iExponent, vdupq_n_s32(0));
+    }
 
-    iExponent = vshlq_n_s32( vaddq_s32(exponent.simdInternal_, exponentBias), 23);
+    iExponent = vshlq_n_s32( iExponent, 23);
 
     return {
                vmulq_f32(value.simdInternal_, vreinterpretq_f32_s32(iExponent))
index 77cf7559a8e99c896d725d0d70fbf171d2679d35..64dda47c14cb65172a3f77d5f61bcf1dba11c650 100644 (file)
@@ -42,6 +42,8 @@
 
 #include <arm_neon.h>
 
+#include "gromacs/math/utilities.h"
+
 #include "impl_arm_neon_asimd_simd_float.h"
 
 namespace gmx
@@ -424,17 +426,25 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
-    const int64x2_t  exponentBias = vdupq_n_s64(1023);
-    int64x2_t        iExponent;
+    const int32x2_t exponentBias = vdup_n_s32(1023);
+    int32x2_t       iExponent    = vadd_s32(exponent.simdInternal_, exponentBias);
+    int64x2_t       iExponent64;
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vmax_s32(iExponent, vdup_n_s32(0));
+    }
 
-    iExponent = vmovl_s32(exponent.simdInternal_);
-    iExponent = vshlq_n_s64(vaddq_s64(iExponent, exponentBias), 52);
+    iExponent64 = vmovl_s32(iExponent);
+    iExponent64 = vshlq_n_s64(iExponent64, 52);
 
     return {
-               vmulq_f64(value.simdInternal_, float64x2_t(iExponent))
+               vmulq_f64(value.simdInternal_, float64x2_t(iExponent64))
     };
 }
 
index 82de8038ca1fc55871327dc95030be869cf2ed43..6cf857002b251a29cf046a2e5e8a861c4a4d17eb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -47,6 +47,7 @@
 #include <qpxmath.h>
 #endif
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/basedefinitions.h"
 
 #include "impl_ibm_qpx_simd_float.h"
@@ -347,6 +348,7 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
     return value;
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
index b5ec8b8705b050480d4cfd31ce1f1146643afef2..3aab94b75c488935da18d6590429b91f6789d2fd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -46,6 +46,7 @@
 #include <qpxmath.h>
 #endif
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/basedefinitions.h"
 
 namespace gmx
@@ -344,6 +345,7 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
     return value;
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
index 45520a2010cc38f65892a370cbfdcd573024797e..2a027701d294457bcd1844e554476180b8c01622 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,6 +40,7 @@
 
 #include <cstdint>
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/basedefinitions.h"
 
 #include "impl_ibm_vmx_definitions.h"
@@ -384,6 +385,7 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
@@ -391,6 +393,13 @@ ldexp(SimdFloat value, SimdFInt32 exponent)
     __vector signed int       iExponent;
 
     iExponent  = vec_add(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vec_max(iExponent, vec_splat_s32(0));
+    }
+
     iExponent  = vec_sl(iExponent, vec_add(vec_splat_u32(15), vec_splat_u32(8)));
 
     return {
index 19a6b08a69f7197aefa8190fd588e34a98cb4841..ebe1aadef220393e820608135d2b54f8e09a35d1 100644 (file)
@@ -38,6 +38,7 @@
 
 #include "config.h"
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/basedefinitions.h"
 
 #include "impl_ibm_vsx_definitions.h"
@@ -422,6 +423,7 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
@@ -434,6 +436,13 @@ ldexp(SimdDouble value, SimdDInt32 exponent)
 #endif
 
     iExponent = vec_add(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vec_max(iExponent, vec_splat_s32(0));
+    }
+
     // exponent is now present in pairs of integers; 0011.
     // Elements 0/2 already correspond to the upper half of each double,
     // so we only need to shift by another 52-32=20 bits.
index 12353fc350746b7612f76ed157dec4b74fca9bb5..7776cae85820f621de1618627a02e279eb2cd435 100644 (file)
@@ -38,6 +38,7 @@
 
 #include "config.h"
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/basedefinitions.h"
 
 #include "impl_ibm_vsx_definitions.h"
@@ -401,13 +402,22 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
     const __vector signed int exponentBias   = vec_splats(127);
     __vector signed int       iExponent;
 
-    iExponent = vec_sl( vec_add(exponent.simdInternal_, exponentBias), vec_splats(23U));
+    iExponent  = vec_add(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vec_max(iExponent, vec_splat_s32(0));
+    }
+
+    iExponent = vec_sl( iExponent, vec_splats(23U));
 
     return {
                vec_mul(value.simdInternal_, reinterpret_cast<__vector float>(iExponent))
index 6cd0af1cf826e116836a80f4b0090d018772f4f1..448415126c976e96f84b2ffb87ce434460e8bb3c 100644 (file)
@@ -67,7 +67,7 @@ namespace gmx
  *        but if the pointer is not aligned the prefetch might start at the
  *        lower cache line boundary (meaning fewer bytes are prefetched).
  */
-static inline void
+static inline void gmx_unused
 simdPrefetch(void gmx_unused * m)
 {
     // Do nothing for reference implementation
index 07a9cec67259a6331c36b3b11ec42fcc27bf1de4..8b953402b3e7ca1dea00b86abc5d1b006eada05a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,6 +55,7 @@
 #include <algorithm>
 #include <array>
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/fatalerror.h"
 
 #include "impl_reference_definitions.h"
@@ -866,11 +867,20 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
 }
 
 /*! \brief Multiply a SIMD double value by the number 2 raised to an exp power.
+ *
+ * \tparam opt By default, this routine will return zero for input arguments
+ *             that are so small they cannot be reproduced in the current
+ *             precision. If the unsafe math optimization template parameter
+ *             setting is used, these tests are skipped, and the result will
+ *             be undefined (possible even NaN). This might happen below -127
+ *             in single precision or -1023 in double, although some
+ *             might use denormal support to extend the range.
  *
  * \param value Floating-point number to multiply with new exponent
  * \param exponent Integer that will not overflow as 2^exponent.
  * \return value*2^exponent
  */
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble gmx_simdcall
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
@@ -878,6 +888,8 @@ ldexp(SimdDouble value, SimdDInt32 exponent)
 
     for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
     {
+        // std::ldexp already takes care of clamping arguments, so we do not
+        // need to do anything in the reference implementation
         res.simdInternal_[i] = std::ldexp(value.simdInternal_[i], exponent.simdInternal_[i]);
     }
     return res;
index ba436c8ad6cc0f676ca4877002079b20d3a87ed6..01da697399ef27f775793a98904898c5ca3ea7ab 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -55,6 +55,8 @@
 #include <algorithm>
 #include <array>
 
+#include "gromacs/math/utilities.h"
+
 #include "impl_reference_definitions.h"
 
 namespace gmx
@@ -861,11 +863,20 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
 }
 
 /*! \brief Multiply a SIMD float value by the number 2 raised to an exp power.
+ *
+ * \tparam opt By default, this routine will return zero for input arguments
+ *             that are so small they cannot be reproduced in the current
+ *             precision. If the unsafe math optimization template parameter
+ *             setting is used, these tests are skipped, and the result will
+ *             be undefined (possible even NaN). This might happen below -127
+ *             in single precision or -1023 in double, although some
+ *             might use denormal support to extend the range.
  *
  * \param value Floating-point number to multiply with new exponent
  * \param exponent Integer that will not overflow as 2^exponent.
  * \return value*2^exponent
  */
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
@@ -873,6 +884,8 @@ ldexp(SimdFloat value, SimdFInt32 exponent)
 
     for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
     {
+        // std::ldexp already takes care of clamping arguments, so we do not
+        // need to do anything in the reference implementation
         res.simdInternal_[i] = std::ldexp(value.simdInternal_[i], exponent.simdInternal_[i]);
     }
     return res;
index 21c4cf9f1b5f64e139b34d2a9231ec988a34c1ed..099bc40ddcad3ada9703e0f633ce17904f78da79 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,6 +40,7 @@
 
 #include <immintrin.h>
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_double.h"
 
 namespace gmx
@@ -110,15 +111,22 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
-    const __m256i  exponentBias = _mm256_set1_epi64x(1023LL);
-    __m256i        iExponent    = _mm256_cvtepi32_epi64(exponent.simdInternal_);
+    const __m128i  exponentBias = _mm_set1_epi32(1023);
+    __m128i        iExponent    = _mm_add_epi32(exponent.simdInternal_, exponentBias);
 
-    iExponent = _mm256_slli_epi64(_mm256_add_epi64(iExponent, exponentBias), 52);
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_max_epi32(iExponent, _mm_setzero_si128());
+    }
+
+    __m256i iExponent256 = _mm256_slli_epi64(_mm256_cvtepi32_epi64(iExponent), 52);
     return {
-               _mm256_mul_pd(value.simdInternal_, _mm256_castsi256_pd(iExponent))
+               _mm256_mul_pd(value.simdInternal_, _mm256_castsi256_pd(iExponent256))
     };
 }
 
index 6695e80bb9eb937bb08ef660a1ca298ba30127de..e549657a0c1aa4242a4a6d692cf131d8ebc85caa 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -40,6 +40,7 @@
 
 #include <immintrin.h>
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_float.h"
 
 namespace gmx
@@ -118,13 +119,20 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
     const __m256i  exponentBias = _mm256_set1_epi32(127);
-    __m256i        iExponent;
+    __m256i        iExponent    = _mm256_add_epi32(exponent.simdInternal_, exponentBias);
 
-    iExponent = _mm256_slli_epi32(_mm256_add_epi32(exponent.simdInternal_, exponentBias), 23);
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm256_max_epi32(iExponent, _mm256_setzero_si256());
+    }
+
+    iExponent = _mm256_slli_epi32(iExponent, 23);
     return {
                _mm256_mul_ps(value.simdInternal_, _mm256_castsi256_ps(iExponent))
     };
index 34919ac1b07e3e83e6c34fd007a15a6b17ff063e..5910184acb9772803ccfde0c0bc916f30b8f1f6b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,6 +44,8 @@
 
 #include <immintrin.h>
 
+#include "gromacs/math/utilities.h"
+
 #include "impl_x86_avx_256_simd_float.h"
 
 
@@ -416,6 +418,7 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
@@ -424,6 +427,13 @@ ldexp(SimdDouble value, SimdDInt32 exponent)
     __m256d       fExponent;
 
     iExponentLow  = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponentLow  = _mm_max_epi32(iExponentLow, _mm_setzero_si128());
+    }
+
     iExponentHigh = _mm_shuffle_epi32(iExponentLow, _MM_SHUFFLE(3, 3, 2, 2));
     iExponentLow  = _mm_shuffle_epi32(iExponentLow, _MM_SHUFFLE(1, 1, 0, 0));
     iExponentHigh = _mm_slli_epi64(iExponentHigh, 52);
index 05a32506ea641781776ae6d43265a207c984e451..a6fefb362a452ac92a665c860d59dda7d06c3fa5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,6 +44,8 @@
 
 #include <immintrin.h>
 
+#include "gromacs/math/utilities.h"
+
 namespace gmx
 {
 
@@ -398,6 +400,7 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
 
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
@@ -407,8 +410,19 @@ ldexp(SimdFloat value, SimdFInt32 exponent)
 
     iExponentHigh = _mm256_extractf128_si256(exponent.simdInternal_, 0x1);
     iExponentLow  = _mm256_castsi256_si128(exponent.simdInternal_);
-    iExponentLow  = _mm_slli_epi32(_mm_add_epi32(iExponentLow, exponentBias), 23);
-    iExponentHigh = _mm_slli_epi32(_mm_add_epi32(iExponentHigh, exponentBias), 23);
+
+    iExponentLow  = _mm_add_epi32(iExponentLow, exponentBias);
+    iExponentHigh = _mm_add_epi32(iExponentHigh, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponentLow  = _mm_max_epi32(iExponentLow, _mm_setzero_si128());
+        iExponentHigh = _mm_max_epi32(iExponentHigh, _mm_setzero_si128());
+    }
+
+    iExponentLow  = _mm_slli_epi32(iExponentLow, 23);
+    iExponentHigh = _mm_slli_epi32(iExponentHigh, 23);
     iExponent     = _mm256_castsi128_si256(iExponentLow);
     iExponent     = _mm256_insertf128_si256(iExponent, iExponentHigh, 0x1);
     return {
index 499290a930e504376ea30716384e0ce0ac575544..1366675c18d53c085ec027a126e24a06d1c450d3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +43,7 @@
 
 #include <immintrin.h>
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/basedefinitions.h"
 
 #include "impl_x86_avx_512_general.h"
@@ -392,15 +393,23 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
-    const __m512i exponentBias = _mm512_set1_epi32(1023);
-    __m512i       iExponent;
+    const __m256i exponentBias = _mm256_set1_epi32(1023);
+    __m256i       iExponent    = _mm256_add_epi32(exponent.simdInternal_, exponentBias);
+    __m512i       iExponent512;
 
-    iExponent = _mm512_permutexvar_epi32(_mm512_set_epi32(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0), _mm512_castsi256_si512(exponent.simdInternal_));
-    iExponent = _mm512_mask_slli_epi32(_mm512_setzero_epi32(), avx512Int2Mask(0xAAAA), _mm512_add_epi32(iExponent, exponentBias), 20);
-    return _mm512_mul_pd(_mm512_castsi512_pd(iExponent), value.simdInternal_);
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm256_max_epi32(iExponent, _mm256_setzero_si256());
+    }
+
+    iExponent512 = _mm512_permutexvar_epi32(_mm512_set_epi32(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0), _mm512_castsi256_si512(iExponent));
+    iExponent512 = _mm512_mask_slli_epi32(_mm512_setzero_epi32(), avx512Int2Mask(0xAAAA), iExponent512, 20);
+    return _mm512_mul_pd(_mm512_castsi512_pd(iExponent512), value.simdInternal_);
 }
 
 static inline double gmx_simdcall
index d7106a1ce09bde041cc4a20e9ea8eed2249530db..4202fe46d4c59ff40990db55505187eb740760bc 100644 (file)
@@ -43,6 +43,7 @@
 
 #include <immintrin.h>
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/real.h"
 
 #include "impl_x86_avx_512_general.h"
@@ -392,13 +393,20 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
     const __m512i exponentBias = _mm512_set1_epi32(127);
-    __m512i       iExponent;
+    __m512i       iExponent    =  _mm512_add_epi32(exponent.simdInternal_, exponentBias);
 
-    iExponent = _mm512_slli_epi32( _mm512_add_epi32(exponent.simdInternal_, exponentBias), 23);
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm512_max_epi32(iExponent, _mm512_setzero_epi32());
+    }
+
+    iExponent = _mm512_slli_epi32(iExponent, 23);
 
     return {
                _mm512_mul_ps(value.simdInternal_, _mm512_castsi512_ps(iExponent))
index ab964b470951d45dbe8e7ed99a691a582c92d631..1c2a85d8c04c0e657f2f89d1c7206402a2d3d34f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +43,7 @@
 
 #include <immintrin.h>
 
+#include "gromacs/math/utilities.h"
 #include "gromacs/utility/basedefinitions.h"
 
 #include "impl_x86_mic_simd_float.h"
@@ -392,14 +393,21 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
     const __m512i exponentBias = _mm512_set1_epi32(1023);
-    __m512i       iExponent;
+    __m512i       iExponent    = _mm512_add_epi32(exponent.simdInternal_, exponentBias);
 
-    iExponent = _mm512_permutevar_epi32(_mm512_set_epi32(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0), exponent.simdInternal_);
-    iExponent = _mm512_mask_slli_epi32(_mm512_setzero_epi32(), _mm512_int2mask(0xAAAA), _mm512_add_epi32(iExponent, exponentBias), 20);
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm512_max_epi32(iExponent, _mm512_setzero_epi32());
+    }
+
+    iExponent = _mm512_permutevar_epi32(_mm512_set_epi32(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0), iExponent);
+    iExponent = _mm512_mask_slli_epi32(_mm512_setzero_epi32(), _mm512_int2mask(0xAAAA), iExponent, 20);
     return _mm512_mul_pd(_mm512_castsi512_pd(iExponent), value.simdInternal_);
 }
 
index a58b73d7b352e81a50840f4c2c072cfdaf1e4c7c..9a054eea231bc3db504e1a24275aeb167bc5423a 100644 (file)
@@ -393,13 +393,20 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
     const __m512i exponentBias = _mm512_set1_epi32(127);
-    __m512i       iExponent;
+    __m512i       iExponent    = _mm512_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm512_max_epi32(iExponent, _mm512_setzero_epi32());
+    }
 
-    iExponent = _mm512_slli_epi32( _mm512_add_epi32(exponent.simdInternal_, exponentBias), 23);
+    iExponent = _mm512_slli_epi32( iExponent, 23);
 
     return {
                _mm512_mul_ps(value.simdInternal_, _mm512_castsi512_ps(iExponent))
index 7332d43c7ab4619ca75c3d74ffb8377cd4212225..a8e46d783aa0c63f68226d26effea9b9e6e63c68 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +43,8 @@
 
 #include <emmintrin.h>
 
+#include "gromacs/math/utilities.h"
+
 #include "impl_x86_sse2_simd_float.h"
 
 namespace gmx
@@ -415,21 +417,31 @@ frexp(SimdDouble value, SimdDInt32 * exponent)
     };
 }
 
+// Override for SSE4.1
+#if GMX_SIMD_X86_SSE2
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble
 ldexp(SimdDouble value, SimdDInt32 exponent)
 {
     const __m128i  exponentBias = _mm_set1_epi32(1023);
-    __m128i        iExponent;
+    __m128i        iExponent    = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_and_si128(iExponent, _mm_cmpgt_epi32(iExponent, _mm_setzero_si128()));
+    }
 
     // After conversion integers will be in slot 0,1. Move them to 0,2 so
     // we can do a 64-bit shift and get them to the dp exponents.
-    iExponent = _mm_shuffle_epi32(exponent.simdInternal_, _MM_SHUFFLE(3, 1, 2, 0));
-    iExponent = _mm_slli_epi64(_mm_add_epi32(iExponent, exponentBias), 52);
+    iExponent = _mm_shuffle_epi32(iExponent, _MM_SHUFFLE(3, 1, 2, 0));
+    iExponent = _mm_slli_epi64(iExponent, 52);
 
     return {
                _mm_mul_pd(value.simdInternal_, _mm_castsi128_pd(iExponent))
     };
 }
+#endif
 
 // Override for AVX-128-FMA and higher
 #if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
index 0c2787184e1e06a15707cc122c9698a0cfecd459..c2f3ab8da655d251bd406a44268df6c104f7d086 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +43,8 @@
 
 #include <emmintrin.h>
 
+#include "gromacs/math/utilities.h"
+
 namespace gmx
 {
 
@@ -408,18 +410,30 @@ frexp(SimdFloat value, SimdFInt32 * exponent)
     };
 }
 
+// Override for SSE4.1
+#if GMX_SIMD_X86_SSE2
+template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 ldexp(SimdFloat value, SimdFInt32 exponent)
 {
     const __m128i exponentBias = _mm_set1_epi32(127);
     __m128i       iExponent;
 
-    iExponent = _mm_slli_epi32( _mm_add_epi32(exponent.simdInternal_, exponentBias), 23);
+    iExponent = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_and_si128(iExponent, _mm_cmpgt_epi32(iExponent, _mm_setzero_si128()));
+    }
+
+    iExponent = _mm_slli_epi32( iExponent, 23);
 
     return {
                _mm_mul_ps(value.simdInternal_, _mm_castsi128_ps(iExponent))
     };
 }
+#endif
 
 // Override for AVX-128-FMA and higher
 #if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
index cc8995cbc7c0cbaa15b3c3945035aadfa9b23cfa..0e4b5ea1711e7712dbb4e0885da22545d2925773 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -125,6 +125,29 @@ blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble
+ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    const __m128i  exponentBias = _mm_set1_epi32(1023);
+    __m128i        iExponent    = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_max_epi32(iExponent, _mm_setzero_si128());
+    }
+
+    // After conversion integers will be in slot 0,1. Move them to 0,2 so
+    // we can do a 64-bit shift and get them to the dp exponents.
+    iExponent = _mm_shuffle_epi32(iExponent, _MM_SHUFFLE(3, 1, 2, 0));
+    iExponent = _mm_slli_epi64(iExponent, 52);
+
+    return {
+               _mm_mul_pd(value.simdInternal_, _mm_castsi128_pd(iExponent))
+    };
+}
+
 }      // namespace gmx
 
 #endif // GMX_SIMD_IMPL_X86_SSE4_1_SIMD_DOUBLE_H
index 8963cef91b846999aa0e80350fe4ecda701403fc..a0df1808bf51b9cb6a85c4ece4493d00f6ffc4b8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -114,6 +114,27 @@ blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
     };
 }
 
+template <MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall
+ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    const __m128i exponentBias = _mm_set1_epi32(127);
+    __m128i       iExponent;
+
+    iExponent = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_max_epi32(iExponent, _mm_setzero_si128());
+    }
+
+    iExponent = _mm_slli_epi32( iExponent, 23);
+
+    return {
+               _mm_mul_ps(value.simdInternal_, _mm_castsi128_ps(iExponent))
+    };
+}
 
 }      // namespace gmx
 
index e3f1db68258b361d1753d6ceedca8686b32a53b7..22a7232dfc3e6588c14fc43926b8124ea86c791c 100644 (file)
@@ -59,6 +59,8 @@
 
 #include <cmath>
 
+#include <limits>
+
 #include "gromacs/math/utilities.h"
 #include "gromacs/simd/simd.h"
 #include "gromacs/utility/basedefinitions.h"
@@ -415,11 +417,6 @@ template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdFloat gmx_simdcall
 exp2(SimdFloat x)
 {
-    // Lower bound: Clamp args that would lead to an IEEE fp exponent below -127.
-    // Note that we use a slightly wider range inside this *implementation* compared
-    // to that guaranteed by the API documentation in the comment above (which has
-    // to cover all architectures).
-    const SimdFloat  smallArgLimit(-127.0f);
     const SimdFloat  CC6(0.0001534581200287996416911311f);
     const SimdFloat  CC5(0.001339993121934088894618990f);
     const SimdFloat  CC4(0.009618488957115180159497841f);
@@ -444,16 +441,17 @@ exp2(SimdFloat x)
     //    subtraction to get the fractional part loses accuracy, and then we
     //    can end up with overflows in the polynomial.
     //
-    // For now, we handle this by clamping smaller arguments to -127. At this
-    // point we will already return zero, so we don't need to do anything
-    // extra for the exponent.
-
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer.
     if (opt == MathOptimization::Safe)
     {
-        x         = max(x, smallArgLimit);
+        x         = max(x, SimdFloat(std::numeric_limits<std::int32_t>::lowest()));
     }
 
-    fexppart  = ldexp(one, cvtR2I(x));
+    fexppart  = ldexp<opt>(one, cvtR2I(x));
     intpart   = round(x);
     x         = x - intpart;
 
@@ -507,11 +505,6 @@ static inline SimdFloat gmx_simdcall
 exp(SimdFloat x)
 {
     const SimdFloat  argscale(1.44269504088896341f);
-    // Lower bound: Clamp args that would lead to an IEEE fp exponent below -127.
-    // Note that we use a slightly wider range inside this *implementation* compared
-    // to that guaranteed by the API documentation in the comment above (which has
-    // to cover all architectures).
-    const SimdFloat  smallArgLimit(-88.0296919311f);
     const SimdFloat  invargscale0(-0.693145751953125f);
     const SimdFloat  invargscale1(-1.428606765330187045e-06f);
     const SimdFloat  CC4(0.00136324646882712841033936f);
@@ -526,7 +519,7 @@ exp(SimdFloat x)
 
     // Large negative values are valid arguments to exp2(), so there are two
     // things we need to account for:
-    // 1. When the IEEE exponents reaches -127, the (biased) exponent field will be
+    // 1. When the exponents reaches -127, the (biased) exponent field will be
     //    zero and we can no longer multiply with it. There are special IEEE
     //    formats to handle this range, but for now we have to accept that
     //    we cannot handle those arguments. If input value becomes even more
@@ -536,17 +529,22 @@ exp(SimdFloat x)
     //    subtraction to get the fractional part loses accuracy, and then we
     //    can end up with overflows in the polynomial.
     //
-    // For now, we handle this by clamping smaller arguments to -127*ln(2). At this
-    // point we will already return zero, so we don't need to do anything
-    // extra for the exponent.
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer
+    // after being multiplied by argscale.
 
     if (opt == MathOptimization::Safe)
     {
-        x         = max(x, smallArgLimit);
+        x         = max(x, SimdFloat(std::numeric_limits<std::int32_t>::lowest())/argscale);
     }
 
     y         = x * argscale;
-    fexppart  = ldexp(one, cvtR2I(y));
+
+
+    fexppart  = ldexp<opt>(one, cvtR2I(y));
     intpart   = round(y);
 
     // Extended precision arithmetics
@@ -1797,8 +1795,6 @@ template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble gmx_simdcall
 exp2(SimdDouble x)
 {
-    // Lower bound: Clamp args that would lead to an IEEE fp exponent below -1023.
-    const SimdDouble  smallArgLimit(-1023.0);
     const SimdDouble  CE11(4.435280790452730022081181e-10);
     const SimdDouble  CE10(7.074105630863314448024247e-09);
     const SimdDouble  CE9(1.017819803432096698472621e-07);
@@ -1828,16 +1824,17 @@ exp2(SimdDouble x)
     //    subtraction to get the fractional part loses accuracy, and then we
     //    can end up with overflows in the polynomial.
     //
-    // For now, we handle this by clamping smaller arguments to -1023. At this
-    // point we will already return zero, so we don't need to do anything
-    // extra for the exponent.
-
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer.
     if (opt == MathOptimization::Safe)
     {
-        x         = max(x, smallArgLimit);
+        x         = max(x, SimdDouble(std::numeric_limits<std::int32_t>::lowest()));
     }
 
-    fexppart  = ldexp(one, cvtR2I(x));
+    fexppart  = ldexp<opt>(one, cvtR2I(x));
     intpart   = round(x);
     x         = x - intpart;
 
@@ -1867,8 +1864,6 @@ static inline SimdDouble gmx_simdcall
 exp(SimdDouble x)
 {
     const SimdDouble  argscale(1.44269504088896340735992468100);
-    // Lower bound: Clamp args that would lead to an IEEE fp exponent below -1023.
-    const SimdDouble  smallArgLimit(-709.0895657128);
     const SimdDouble  invargscale0(-0.69314718055966295651160180568695068359375);
     const SimdDouble  invargscale1(-2.8235290563031577122588448175013436025525412068e-13);
     const SimdDouble  CE12(2.078375306791423699350304e-09);
@@ -1899,17 +1894,21 @@ exp(SimdDouble x)
     //    subtraction to get the fractional part loses accuracy, and then we
     //    can end up with overflows in the polynomial.
     //
-    // For now, we handle this by clamping smaller arguments to -1023*ln(2). At this
-    // point we will already return zero, so we don't need to do anything
-    // extra for the exponent.
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer
+    // after being multiplied by argscale.
 
     if (opt == MathOptimization::Safe)
     {
-        x         = max(x, smallArgLimit);
+        x         = max(x, SimdDouble(std::numeric_limits<std::int32_t>::lowest())/argscale);
     }
 
     y         = x * argscale;
-    fexppart  = ldexp(one, cvtR2I(y));
+
+    fexppart  = ldexp<opt>(one, cvtR2I(y));
     intpart   = round(y);
 
     // Extended precision arithmetics
@@ -3155,8 +3154,6 @@ template <MathOptimization opt = MathOptimization::Safe>
 static inline SimdDouble gmx_simdcall
 exp2SingleAccuracy(SimdDouble x)
 {
-    // Lower bound: Clamp args that would lead to an IEEE fp exponent below -1023.
-    const SimdDouble  smallArgLimit(-1023.0);
     const SimdDouble  CC6(0.0001534581200287996416911311);
     const SimdDouble  CC5(0.001339993121934088894618990);
     const SimdDouble  CC4(0.009618488957115180159497841);
@@ -3181,13 +3178,14 @@ exp2SingleAccuracy(SimdDouble x)
     //    subtraction to get the fractional part loses accuracy, and then we
     //    can end up with overflows in the polynomial.
     //
-    // For now, we handle this by clamping smaller arguments to -1023. At this
-    // point we will already return zero, so we don't need to do anything
-    // extra for the exponent.
-
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer.
     if (opt == MathOptimization::Safe)
     {
-        x         = max(x, smallArgLimit);
+        x         = max(x, SimdDouble(std::numeric_limits<std::int32_t>::lowest()));
     }
 
     ix        = cvtR2I(x);
@@ -3200,7 +3198,7 @@ exp2SingleAccuracy(SimdDouble x)
     p         = fma(p, x, CC2);
     p         = fma(p, x, CC1);
     p         = fma(p, x, one);
-    x         = ldexp(p, ix);
+    x         = ldexp<opt>(p, ix);
 
     return x;
 }
@@ -3231,7 +3229,7 @@ expSingleAccuracy(SimdDouble x)
 
     // Large negative values are valid arguments to exp2(), so there are two
     // things we need to account for:
-    // 1. When the exponents reaches -127, the (biased) exponent field will be
+    // 1. When the exponents reaches -1023, the (biased) exponent field will be
     //    zero and we can no longer multiply with it. There are special IEEE
     //    formats to handle this range, but for now we have to accept that
     //    we cannot handle those arguments. If input value becomes even more
@@ -3241,16 +3239,20 @@ expSingleAccuracy(SimdDouble x)
     //    subtraction to get the fractional part loses accuracy, and then we
     //    can end up with overflows in the polynomial.
     //
-    // For now, we handle this by clamping smaller arguments to -127. At this
-    // point we will already return zero, so we don't need to do anything
-    // extra for the exponent.
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer
+    // after being multiplied by argscale.
 
     if (opt == MathOptimization::Safe)
     {
-        x         = max(x, smallArgLimit);
+        x         = max(x, SimdDouble(std::numeric_limits<std::int32_t>::lowest())/argscale);
     }
 
     y         = x * argscale;
+
     iy        = cvtR2I(y);
     intpart   = round(y);        // use same rounding algorithm here
 
@@ -3264,7 +3266,7 @@ expSingleAccuracy(SimdDouble x)
     p         = fma(p, x, CC0);
     p         = fma(x*x, p, x);
     p         = p + one;
-    x         = ldexp(p, iy);
+    x         = ldexp<opt>(p, iy);
     return x;
 }
 
index 0e291cabf9c943c6f8a78442bc6f7ce60e6ed3a9..fe728444ee11d0d4cd625e1fa4169e57f80cb53f 100644 (file)
@@ -259,14 +259,17 @@ TEST_F(SimdFloatingpointTest, ldexp)
     SimdReal one = setSimdRealFrom1R(1.0);
 
     GMX_EXPECT_SIMD_REAL_EQ(setSimdRealFrom3R(pow(2.0, 60.0), pow(2.0, -41.0), pow(2.0, 54.0)),
-                            ldexp(one, setSimdIntFrom3I(60, -41, 54)));
+                            ldexp<MathOptimization::Unsafe>(one, setSimdIntFrom3I(60, -41, 54)));
 #if GMX_SIMD_HAVE_DOUBLE && GMX_DOUBLE
     GMX_EXPECT_SIMD_REAL_EQ(setSimdRealFrom3R(pow(2.0, 587.0), pow(2.0, -462.0), pow(2.0, 672.0)),
-                            ldexp(one, setSimdIntFrom3I(587, -462, 672)));
+                            ldexp<MathOptimization::Unsafe>(one, setSimdIntFrom3I(587, -462, 672)));
 #endif
     /* Rounding mode in conversions must be consistent with simdRound() for SetExponent() to work */
-    GMX_EXPECT_SIMD_REAL_EQ(ldexp(one, cvtR2I(round(x0))), ldexp(one, cvtR2I(x0)));
-    GMX_EXPECT_SIMD_REAL_EQ(ldexp(one, cvtR2I(round(x1))), ldexp(one, cvtR2I(x1)));
+    GMX_EXPECT_SIMD_REAL_EQ(ldexp<MathOptimization::Unsafe>(one, cvtR2I(round(x0))), ldexp<MathOptimization::Unsafe>(one, cvtR2I(x0)));
+    GMX_EXPECT_SIMD_REAL_EQ(ldexp<MathOptimization::Unsafe>(one, cvtR2I(round(x1))), ldexp<MathOptimization::Unsafe>(one, cvtR2I(x1)));
+
+    // The default safe version must be able to handle very negative arguments too
+    GMX_EXPECT_SIMD_REAL_EQ(setZero(), ldexp(one, setSimdIntFrom3I(-2000, -1000000, -1000000000)));
 }
 
 /*
index 3fbc94c14f3407777b2dbba087cd466b24402062..147493db016fffc0e422d02a7d3a09311ab385ec 100644 (file)
@@ -112,7 +112,7 @@ TEST_F(SimdVectorOperationsTest, cprod)
     //The test values cannot be computed without FMA for the case that SIMD has no FMA. Even if no explicit FMA were
     //used, the compiler could choose to use FMA. This causes up to 6upl error because of the product is up to 6 times
     //larger than the final result after the difference.
-    setUlpTol(GMX_SIMD_HAVE_FMA ? 0 : 6);
+    setUlpTol(GMX_SIMD_HAVE_FMA ? ulpTol_ : 6);
 
     GMX_EXPECT_SIMD_REAL_NEAR(refcX, cX);
     GMX_EXPECT_SIMD_REAL_NEAR(refcY, cY);