skeeto / w64devkit

Portable C and C++ Development Kit for x64 (and x86) Windows
The Unlicense
2.66k stars 185 forks source link

Standard library `atof` is unable to parse `nan`. Use `ucrtbase.dll` instead of `msvcrt.dll`. #98

Open MarekKnapek opened 6 months ago

MarekKnapek commented 6 months ago

Steps to reproduce:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    double dbl;

    dbl = atof("nan");
    if(dbl != dbl)
    {
        puts("gud");
    }
    else
    {
        puts("bad");
    }
}

Expected result:

Actual result:

Repeat this experiment with the MSVC and cl.exe C compiler and the result is as expected.

The reason for this bad behavior is that the w64devkit is using the msvcrt.dll C run-time library as its libc. I believe that this library is quite old, unmaintained and buggy. Only god knows how many other issues is waiting for us inside this library. Especially related to new functionality added in later C standards. It is sad, that the gcc is able to compile newer C but is held back by old libc.

Thus, in this issue, I'm suggesting to replace dependency on msvcrt.dll by dependency on ucrtbase.dll.

This library is considered as part of Windows since Windows 10 and can be installed by Windows Update on Windows XP and later. So there should be no downsides by changing this dependency.

skeeto commented 6 months ago

Thanks for the heads up, Marek. This is the second practical difference between MSVCRT and UCRT of which I've become aware, the first being the assert() macro. However, this meager list of two tiny benefits is hardly worth the costs.

Needing to package and run a redistributable runtime installer alongside your own application installation is hardly "no downsides." (Just consider how many "Microsoft Visual C++ Redistributable" you have installed on your system right now. I count 19 on mine.) If you truly believe this, then try producing a UCRT w64devkit-i686.zip that works on Windows XP or 7. If it requires running an installer, that's a substantial downside. At the very least it would require bundling proprietary software. Currently everything included in the kit is built from source and 100% customizable.

My view is that both CRTs are quite poor and not worth using — not even worth linking — and that relying on libc is fraught on any platform. Take atof: The behavior depends on global state (locale), and the input must be null terminated. The interface is deeply flawed and unsuitable for serious use. If you require robust float parsing then you need to embed a custom parser, such as David M. Gay's float parser, regardless of the platform. See also c17f5ca and https://nullprogram.com/blog/2023/02/11/

If UCRT is important, then perhaps w64devkit isn't for you. Consider WinLibs instead.

MarekKnapek commented 6 months ago

I'm big fan of your blog for many years. I forgot about it and re-discovered it last year.

I thought that dependency on ucrtbase.dll is not big deal as it is on "every computer" (meaning since Win10, 8 years already), no need for redist. It seems that supporting older systems is more important to you - that is great.

In fact I kind of share your point of view, as in my free time I switched form C++ to C and I'm trying to write only standards compliant C89 code. Re-implementing the ideas from C++'s STL and other stuff from scratch. I discovered this atof issue in my tests as I created float -> string conversion routine using only integer math. I was unable to create string -> float functionality (yet, heh).

Didn't know about WinLibs, I give it a try. Thank you for the pointer to the parser.

Again, thank you very much for your blog.

Peter0x44 commented 6 months ago

To add to this, this doesn't work for inf either. I didn't check if this worked with UCRT, but I assume it does. Just something else for users to be aware of.

wajap commented 6 months ago

To avoid this issue one can use strtod using this macro #define atof(S) (strtod((S),(char **)NULL))