@@ -83,6 +83,7 @@ namespace {
8383 }
8484
8585 constexpr T Inf = std::numeric_limits<T>::infinity();
86+ constexpr T NaN = std::numeric_limits<T>::quiet_NaN();
8687 constexpr int N = 10000 ;
8788} // namespace
8889
@@ -196,6 +197,66 @@ TEST_CASE("Backend transcendental ULP accuracy", "[backend]")
196197 }
197198}
198199
200+ // Regression net for a previously-unknown bug: eve::clamp/min/max on this
201+ // platform return the non-NaN operand instead of propagating NaN (x86
202+ // minps/maxps semantics, not IEEE-754 NaN rules), so any Fast* approximation
203+ // that range-limits its input via those primitives can silently turn a NaN
204+ // (e.g. from sqrt(negative) elsewhere in a tree) into a finite,
205+ // plausible-looking wrong value instead of correctly propagating
206+ // "undefined" outward. Found in FastExp and FastTanh (both fixed with an
207+ // explicit is_nan guard, matching FastLog's pre-existing one).
208+ //
209+ // Each lane in every batch here is either NaN or the inert padding value
210+ // 0.5 -- deliberately NOT mixed with other "interesting" edge-case values.
211+ // A NaN placed alongside an unrelated real value in the same batch was
212+ // found, separately, to trigger a genuine third-party bug in eve::sinh
213+ // (a NaN in one SIMD lane corrupting an unrelated lane's result) -- see
214+ // project_eve_sinh_crosslane_nan_bug in memory. That's a different bug in
215+ // eve itself, not something this test is trying to catch, and mixing
216+ // values here would make this test's pass/fail depend on unrelated
217+ // batch-layout coincidences.
218+ TEST_CASE (" Backend NaN propagation" , " [backend]" )
219+ {
220+ auto allNan = [](auto fn) -> bool {
221+ Buf src{};
222+ src.v .fill (T{0.5 });
223+ src.v [0 ] = NaN;
224+ Buf dst{};
225+ fn (dst.v .data (), T{1 }, src.v .data ());
226+ return std::isnan (dst.v [0 ]);
227+ };
228+
229+ CHECK (allNan (Backend::Exp<T,S>));
230+ CHECK (allNan (Backend::Log<T,S>));
231+ CHECK (allNan (Backend::Log1p<T,S>));
232+ CHECK (allNan (Backend::Logabs<T,S>));
233+ CHECK (allNan (Backend::Sin<T,S>));
234+ CHECK (allNan (Backend::Cos<T,S>));
235+ CHECK (allNan (Backend::Tan<T,S>));
236+ CHECK (allNan (Backend::Asin<T,S>));
237+ CHECK (allNan (Backend::Acos<T,S>));
238+ CHECK (allNan (Backend::Atan<T,S>));
239+ CHECK (allNan (Backend::Sinh<T,S>));
240+ CHECK (allNan (Backend::Cosh<T,S>));
241+ CHECK (allNan (Backend::Tanh<T,S>));
242+ CHECK (allNan (Backend::Sqrt<T,S>));
243+ CHECK (allNan (Backend::Sqrtabs<T,S>));
244+ CHECK (allNan (Backend::Cbrt<T,S>));
245+
246+ auto allNan2 = [](auto fn, T x, T y) -> bool {
247+ Buf sa{}; sa.v .fill (T{0.5 }); sa.v [0 ] = x;
248+ Buf sb{}; sb.v .fill (T{0.5 }); sb.v [0 ] = y;
249+ Buf dst{};
250+ fn (dst.v .data (), T{1 }, sa.v .data (), sb.v .data ());
251+ return std::isnan (dst.v [0 ]);
252+ };
253+
254+ CHECK (allNan2 (Backend::Pow<T,S>, NaN, 2 .f ));
255+ CHECK (allNan2 (Backend::Pow<T,S>, 2 .f , NaN));
256+ CHECK (allNan2 (Backend::Powabs<T,S>, NaN, 2 .f ));
257+ CHECK (allNan2 (Backend::Powabs<T,S>, 2 .f , NaN));
258+ }
259+
199260TEST_CASE (" Backend Pow/Powabs ULP accuracy" , " [backend]" )
200261{
201262 auto MaxUlpError2 = [](auto fn, auto ref, std::vector<T> const & xs, std::vector<T> const & ys) {
0 commit comments