Skip to content

qualify isnan() with std namespace - #565

Merged
brenocq merged 3 commits into
epezent:masterfrom
guijan:master
Oct 25, 2026
Merged

brenocq merged 3 commits into
epezent:masterfrom
guijan:master

Conversation

@guijan

@guijan guijan commented Apr 20, 2024

Copy link
Copy Markdown
Contributor

See the commit message for details. I've tested this change on NetBSD only.

Found while porting dolphin-emu/dolphin to NetBSD:

[159/402] Building CXX object Externals/implot/CMakeFiles/implot.dir/implot/implot_items.cpp.o
samu: job failed: /usr/bin/c++ -DAUTOUPDATE=1 -DDATA_DIR=\"/usr/local/share/dolphin-emu/\" -DHAVE_EGL=1 -DHAVE_X11=1 -DHAVE_XRANDR=1 -DUSE_ANALYTICS=1 -DUSE_MEMORYWATCHER=1 -DUSE_PIPES=1 -D_A
RCH_32=1 -D_DEBUG -D_DEFAULT_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_M_GENERIC=1 -I/home/jan/Work/dolphin/Source/Core -I/home/jan/Work/dolphin/Externals/implot/implot -I/home/jan
/Work/dolphin/Externals/implot/imgui -I/home/jan/Work/dolphin/Externals/imgui -I/home/jan/Work/dolphin/Externals/fmt/fmt/include -g -std=c++2a -fdiagnostics-color -fno-strict-aliasing -fno-ex
ceptions -fvisibility-inlines-hidden -fvisibility=hidden -ggdb -w -MD -MT Externals/implot/CMakeFiles/implot.dir/implot/implot_items.cpp.o -MF Externals/implot/CMakeFiles/implot.dir/implot/im
plot_items.cpp.o.d -o Externals/implot/CMakeFiles/implot.dir/implot/implot_items.cpp.o -c /home/jan/Work/dolphin/Externals/implot/implot/implot_items.cpp
In file included from /home/jan/Work/dolphin/Externals/implot/implot/implot_items.cpp:27:
/home/jan/Work/dolphin/Externals/implot/implot/implot_internal.h: In function 'bool ImNan(double)':
/home/jan/Work/dolphin/Externals/implot/implot/implot_internal.h:123:47: error: 'isnan' was not declared in this scope; did you mean 'std::isnan'?
  123 | static inline bool ImNan(double val) { return isnan(val); }
      |                                               ^~~~~
      |                                               std::isnan
In file included from /home/jan/Work/dolphin/Externals/fmt/fmt/include/fmt/format.h:36,
                 from /home/jan/Work/dolphin/Source/Core/Common/Logging/Log.h:7,
                 from /home/jan/Work/dolphin/Source/Core/Common/Assert.h:8,
                 from /home/jan/Work/dolphin/Externals/imgui/imconfig.h:17,
                 from /home/jan/Work/dolphin/Externals/imgui/imgui.h:58,
                 from /home/jan/Work/dolphin/Externals/implot/implot/implot.h:48,
                 from /home/jan/Work/dolphin/Externals/implot/implot/implot_items.cpp:26:
/usr/include/g++/cmath:632:5: note: 'std::isnan' declared here
  632 |     isnan(_Tp __x)
      |     ^~~~~
samu: subcommand failed

When a program that depends on implot includes <implot.h>, we have this
sequence of includes:
<implot.h> -> <implot_internal.h> -> <imgui_internal.h> -> <math.h>

Inside <implot_internal.h>'s source code, there's an unqualified call to
isnan(). By doing so, it relies on <imgui_internal.h> having isnan() in
the global namespace, which <math.h> is guaranteed to do. However, it's
possible that implot's user included <cmath> first, which is not
guaranteed to put isnan() in the global namespace, and may prevent a
later <math.h> from putting isnan() in the global namespace.

On NetBSD 10.0 i386, <cmath> doesn't put isnan() in the global
namespace and prevents <math.h> from doing so, causing a compile error.

Qualify the call to fix it.
guijan added a commit to guijan/dolphin that referenced this pull request Apr 22, 2024
The NetBSD port will only work after
epezent/implot#565 is merged and Dolphin updates
to a version with the fix. In the meantime, you can apply the fix
yourself to a Dolphin checkout.
guijan added a commit to guijan/dolphin that referenced this pull request Apr 22, 2024
The NetBSD port will only work after
epezent/implot#565 is merged and Dolphin updates
to a version with the fix. In the meantime, you can apply the fix
yourself to a Dolphin checkout.
@brenocq

brenocq commented Nov 7, 2025

Copy link
Copy Markdown
Collaborator

@guijan is this still an issue with the latest ImGui? I thought isnan would be defined in <math.h> (which is included by imgui_internal.h).

@brenocq
brenocq self-requested a review November 7, 2025 05:34
@brenocq brenocq added type:fix Something isn't working prio:low Low priority status:review The task is under review labels Nov 7, 2025
@JoshuaVandaele

JoshuaVandaele commented Dec 3, 2025 •

Copy link
Copy Markdown

I can confirm that this still happens with master. I just came across this issue while trying to cross-compile from x64 to ARM64 on Linux while using glibc in particular.

@brenocq brenocq added prio:high High priority and removed building prio:low Low priority labels Dec 3, 2025
@brenocq

brenocq commented Dec 3, 2025 •

Copy link
Copy Markdown
Collaborator

Oh... I've moved the issue to high priority :)

Are you able to reproduce the issue by modifying the example/? It would be great if you could attach a patch with the changes needed to reproduce the compilation error

@JoshuaVandaele

JoshuaVandaele commented Dec 7, 2025 •

Copy link
Copy Markdown

I've narrowed it down quite a lot, so much so that I can place the reproduction program here:

#include <cmath>

#include "implot.h"
#include "implot_internal.h"

int main() { return 0; }

Here is what happens when I try to compile this program using glibc and gcc/g++ from x64 to ARM64:

Log Output
[master] orbit@ArchPuppy:~/Contributing/implot/example/build $ rm -fr * && cmake .. --toolchain ../Toolchain.cmake && cmake --build .
-- The CXX compiler identification is GNU 15.1.0
-- The C compiler identification is GNU 15.1.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/aarch64-linux-gnu-g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/aarch64-linux-gnu-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Found OpenGL: /opt/sysroots/ArchLinuxARM/usr/lib/libOpenGL.so
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE
-- Found Doxygen: /usr/bin/doxygen (found version "1.15.0") found components: doxygen
-- Using X11 for window creation
-- Found X11: /opt/sysroots/ArchLinuxARM/usr/include
-- Looking for XOpenDisplay in /opt/sysroots/ArchLinuxARM/usr/lib/libX11.so;/opt/sysroots/ArchLinuxARM/usr/lib/libXext.so
-- Looking for XOpenDisplay in /opt/sysroots/ArchLinuxARM/usr/lib/libX11.so;/opt/sysroots/ArchLinuxARM/usr/lib/libXext.so - found
-- Looking for gethostbyname
-- Looking for gethostbyname - found
-- Looking for connect
-- Looking for connect - found
-- Looking for remove
-- Looking for remove - found
-- Looking for shmat
-- Looking for shmat - found
-- Looking for IceConnectionNumber in ICE
-- Looking for IceConnectionNumber in ICE - found
-- Configuring done (8.8s)
-- Generating done (0.1s)
-- Build files have been written to: /home/orbit/Contributing/implot/example/build
[  1%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/context.c.o
[  1%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/init.c.o
[  2%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/input.c.o
[  3%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/monitor.c.o
[  4%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/vulkan.c.o
[  4%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/window.c.o
[  5%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/x11_init.c.o
[  6%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/x11_monitor.c.o
[  7%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/x11_window.c.o
[  7%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/xkb_unicode.c.o
[  8%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/posix_time.c.o
[  9%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/posix_thread.c.o
[ 10%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/glx_context.c.o
[ 10%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/egl_context.c.o
[ 11%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/osmesa_context.c.o
[ 12%] Building C object _deps/glfw-build/src/CMakeFiles/glfw.dir/linux_joystick.c.o
[ 13%] Linking C static library libglfw3.a
[ 13%] Built target glfw
[ 14%] Building CXX object CMakeFiles/imgui.dir/_deps/imgui-src/imgui.cpp.o
[ 14%] Building CXX object CMakeFiles/imgui.dir/_deps/imgui-src/imgui_demo.cpp.o
[ 15%] Building CXX object CMakeFiles/imgui.dir/_deps/imgui-src/imgui_draw.cpp.o
[ 16%] Building CXX object CMakeFiles/imgui.dir/_deps/imgui-src/imgui_tables.cpp.o
[ 17%] Building CXX object CMakeFiles/imgui.dir/_deps/imgui-src/imgui_widgets.cpp.o
[ 17%] Building CXX object CMakeFiles/imgui.dir/_deps/imgui-src/backends/imgui_impl_glfw.cpp.o
[ 18%] Building CXX object CMakeFiles/imgui.dir/_deps/imgui-src/backends/imgui_impl_opengl3.cpp.o
[ 19%] Linking CXX static library libimgui.a
[ 19%] Built target imgui
[ 20%] Building CXX object CMakeFiles/implot.dir/home/orbit/Contributing/implot/implot.cpp.o
[ 20%] Building CXX object CMakeFiles/implot.dir/home/orbit/Contributing/implot/implot_demo.cpp.o
[ 21%] Building CXX object CMakeFiles/implot.dir/home/orbit/Contributing/implot/implot_items.cpp.o
[ 22%] Linking CXX static library libimplot.a
[ 22%] Built target implot
[ 23%] Building CXX object CMakeFiles/example.dir/main.cpp.o
In file included from /home/orbit/Contributing/implot/example/main.cpp:26:
/home/orbit/Contributing/implot/example/../implot_internal.h: In function 'bool ImNan(double)':
/home/orbit/Contributing/implot/example/../implot_internal.h:124:47: error: 'isnan' was not declared in this scope; did you mean 'std::isnan'?
  124 | static inline bool ImNan(double val) { return isnan(val); }
      |                                               ^~~~~
      |                                               std::isnan
In file included from /home/orbit/Contributing/implot/example/main.cpp:23:
/opt/sysroots/ArchLinuxARM/usr/include/c++/15.2.1/cmath:1203:5: note: 'std::isnan' declared here
 1203 |     isnan(_Tp)
      |     ^~~~~
make[2]: *** [CMakeFiles/example.dir/build.make:79: CMakeFiles/example.dir/main.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:353: CMakeFiles/example.dir/all] Error 2
make: *** [Makefile:136: all] Error 2

It seems including cmath before implot_internal.h causes the issue to show up. This issue does not happen when compiling without a sysroot and cross-compiler, though.

JoshuaVandaele added a commit to JoshuaVandaele/dolphin that referenced this pull request Dec 9, 2025
JoshuaVandaele added a commit to JoshuaVandaele/dolphin that referenced this pull request Dec 9, 2025
JoshuaVandaele added a commit to JoshuaVandaele/dolphin that referenced this pull request Dec 9, 2025
JoshuaVandaele added a commit to JoshuaVandaele/dolphin that referenced this pull request Dec 9, 2025
Comment thread implot_internal.h Resolved
@brenocq

brenocq commented Dec 21, 2025

Copy link
Copy Markdown
Collaborator

I've narrowed it down quite a lot, so much so that I can place the reproduction program here:

Yep, that looks pretty narrow to me...

@brenocq brenocq closed this Feb 13, 2026
@brenocq brenocq added comment:wontfix This issue will not be addressed or resolved and removed type:fix Something isn't working prio:high High priority status:review The task is under review labels Feb 13, 2026
@brenocq brenocq reopened this Feb 13, 2026
@brenocq brenocq removed the comment:wontfix This issue will not be addressed or resolved label Feb 13, 2026
@brenocq brenocq added type:fix Something isn't working prio:high High priority status:doing Work is currently in progress labels Feb 13, 2026
@brenocq

brenocq commented Feb 13, 2026

Copy link
Copy Markdown
Collaborator

I was planning to close this issue since I can't reproduce it, but I've got an idea. Can someone (@guijan @JoshuaVandaele) try to add #include <math.h> before #include "imgui_internal.h" in implot_internal.h?

#ifndef IMPLOT_VERSION
#error Must include implot.h before implot_internal.h
#endif

#ifndef IMGUI_DISABLE
#include <time.h>
+#include <math.h>
#include "imgui_internal.h"

// Support for pre-1.84 versions. ImPool's GetSize() -> GetBufSize()
#if (IMGUI_VERSION_NUM < 18303)
#define GetBufSize GetSize
#endif

@JoshuaVandaele

Copy link
Copy Markdown

Can someone try to add #include <math.h> before #include "imgui_internal.h" in implot_internal.h?

Sadly this does not fix the issue. Using either std::isnan(val) with #include <cmath>, or (val != val) have been confirmed to resolve this though.

std::isnan alone still fails in the configuration that broke the
original isnan: when the C library's <math.h> is found before the C++
standard library's (a sysroot or system include directory passed with
-I), isnan stays a macro and std::isnan expands to the invalid
std::__builtin_isnan, and with libc++ std::isnan is not declared at all
unless <cmath> is included first. Including <cmath> ourselves is not
enough either, since libc++ rejects that include order outright.

ImNan and ImNanOrInf now copy the double into an ImU64 and test the
IEEE-754 exponent and mantissa, which needs no <cmath> and no isnan
macro or function. Unlike isnan, std::isnan or val != val, the bit test
also keeps working under -ffast-math and /fp:fast, where the compiler
may fold floating-point NaN checks to false and NaN gaps in plots
silently disappear. Under -ffinite-math-only an empty asm statement
hides the bits from the optimizer, because clang otherwise folds the
exponent test to false as well.
@brenocq

brenocq commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

What was wrong. isnan only works if the right headers come in the right order. With some setups (like the ARM64 sysroot build above) it isn't declared at all, and std::isnan breaks on other setups (for example libc++ without <cmath>). There was also a second problem: with -ffast-math, the compiler is allowed to assume that NaN never exists, so it may delete isnan checks entirely, and NaN points then get plotted instead of skipped.

What the fix does. I added a commit on top of yours that stops asking the compiler "is this NaN?". It reads the 64 bits of the double and checks the pattern directly: a NaN is a value whose exponent bits are all ones and whose remaining bits aren't all zero.

static inline bool ImNan(double val) { return (ImDoubleBits(val) & 0x7FFFFFFFFFFFFFFFull) > 0x7FF0000000000000ull; }

This needs no math header, so include order doesn't matter. GCC treats it as plain integer math, so fast-math leaves it alone. Clang would still fold it away, so the commit adds a tiny optimizer barrier that is only active in fast-math builds. ImNanOrInf uses the same idea.

How I tested it.

  • A tiny program that includes <cmath>, then implot.h and implot_internal.h, with glibc's math.h put first on the include path. This reproduces the original error, and with this commit it builds.
  • GCC 16 (64-bit and -m32), and Clang 23 with libstdc++ and with libc++, at C++11, 17, 20 and 2c, with -Wall -Wextra -Werror -pedantic.
  • A run-time check of NaN, infinity and ordinary values at -O0 to -O3, with and without -ffast-math. NaN is detected every time, whereas the old check returned false for NaN under fast-math.

I'll port the same change to ImPlot3D. Let me know if you guys still experience the issue!

@brenocq
brenocq merged commit 1c44811 into epezent:master Oct 25, 2026
19 checks passed
oliverdixon pushed a commit to oliverdixon/implot3d that referenced this pull request Nov 2, 2026
ImNan called the unqualified isnan, which fails to build when <cmath>
is included before ImPlot3D and the C library's <math.h> is found ahead
of the C++ one (a sysroot or system include directory passed with -I):
isnan is then neither a macro nor a global function. Switching to
std::isnan only moves the failure to other setups, such as libc++
without <cmath>.

ImNan and ImNanOrInf now copy the double into an ImU64 and test the
IEEE-754 exponent and mantissa, so they need no math header, and unlike
isnan or val != val they keep detecting NaN under -ffast-math and
/fp:fast, where the compiler may delete floating-point NaN checks.
Under -ffinite-math-only an empty asm statement hides the bits from
the optimizer, since clang otherwise folds the exponent test to false.
Mirrors the same fix in ImPlot (epezent/implot#565).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prio:high High priority status:doing Work is currently in progress type:fix Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants