<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
  <title>sebsite.pw</title>
  <link>https://aggr.aymericbeaumet.com/sources/sebsite.pw/</link>
  <description>Browse aymeric&#39;s, an independent, searchable archive of readable snapshots from followed feeds, preserved in Git with aggr.</description>
  <language>en</language>
  <atom:link href="https://aggr.aymericbeaumet.com/sources/sebsite.pw/rss.xml" rel="self" type="application/rss+xml"/>
  <lastBuildDate>Tue, 13 Oct 2026 13:36:21 +0000</lastBuildDate>
  <generator>aggr</generator>
  <item>
    <title>Your C is not standard C</title>
    <link>https://aggr.aymericbeaumet.com/items/hnrss-org/2026-10-13-your-c-is-not-standard-c/</link>
    <guid isPermaLink="false">urn:aggr:item:a1bbc7e07f353e77b8f5028c1d935f6f76ebef79</guid>
    <atom:link href="https://sebsite.pw/w/20261012-notstdc.html" rel="via" type="text/html"/>
    <pubDate>Tue, 13 Oct 2026 13:36:21 +0000</pubDate>
    <dc:creator>dverlaeckt80</dc:creator>
    <description>The committee has created an unreal language that no one can or will actually use. - Dennis Ritchie (out of context) this is an incomplete list of some incompatibilities between c as it&#39;s written in the standard and c as it exists in the…</description>
    <content:encoded>&lt;p&gt;The committee has created an unreal language that no one can or will actually use.&lt;br /&gt;
- &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://port70.net/~nsz/c/c89/dmr-on-noalias.html&quot;&gt;Dennis Ritchie&lt;/a&gt; (out of context)&lt;/p&gt;
&lt;p&gt;this is an incomplete list of some incompatibilities between c as it&#39;s written in the standard and c as it exists in the real world for people who use gcc/clang + glibc/musl.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;asm&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;by default, gcc and clang recognize the &lt;code&gt;asm&lt;/code&gt; keyword as an extension. this is not strictly conformant, because in standard c, &lt;code&gt;asm&lt;/code&gt; is a valid non-reserved identifier. so code which conforms to the standard may not compile with gcc/clang with their default settings.&lt;/p&gt;
&lt;p&gt;and like, whatever, that&#39;s fine, that&#39;s why &lt;code&gt;-std&lt;/code&gt; exists, but 1. it&#39;s notable that compilers aren&#39;t conformant &lt;em&gt;by default&lt;/em&gt;, and 2. why doesn&#39;t the standard just make &lt;code&gt;asm&lt;/code&gt; a reserved word? c++ does that; i&#39;m not sure why c doesn&#39;t.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;FLT_ROUNDS&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;the standard specifies the &lt;code&gt;FLT_ROUNDS&lt;/code&gt; macro in &lt;code&gt;float.h&lt;/code&gt; as follows:&lt;/p&gt;
&lt;p&gt;5.3.5.3.3p23:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The rounding mode for floating-point addition for standard floating types is characterized by the implementation-defined value of &lt;code&gt;FLT_ROUNDS&lt;/code&gt;. Evaluation of &lt;code&gt;FLT_ROUNDS&lt;/code&gt; correctly reflects any execution-time change of rounding mode through the function &lt;code&gt;fesetround&lt;/code&gt; in &lt;code&gt;&amp;lt;fenv.h&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;notably, the standard requires that the value dynamically changes with the runtime floating environment. gcc, however, always defines &lt;code&gt;FLT_ROUNDS&lt;/code&gt; to 1 (rounds to nearest), regardless of the runtime rounding mode:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;/* Addition rounds to 0: zero, 1: nearest, 2: +inf, 3: -inf, -1: unknown.  */
/* ??? This is supposed to change with calls to fesetround in &amp;lt;fenv.h&amp;gt;.  */
#undef FLT_ROUNDS
#define FLT_ROUNDS 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;clang defines a builtin for this (&lt;code&gt;__builtin_flt_rounds()&lt;/code&gt;), but gcc doesn&#39;t, for some reason.&lt;/p&gt;
&lt;h2&gt;standard pragmas&lt;/h2&gt;
&lt;p&gt;at some point in the future i&#39;m gonna make an entire blog post dedicated to just this, because i kinda find it fascinating. but the super duper short version is this:&lt;/p&gt;
&lt;p&gt;the c standard specifies five standard pragmas to control the behavior of floating point arithmetic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;// added in c99:
// switch can be ON, OFF, or DEFAULT
#pragma STDC FP_CONTACT ON
#pragma STDC FENV_ACCESS ON
#pragma STDC CX_LIMITED_RANGE ON

// added in c23:
// direction can be FE_DYNAMIC, FE_DOWNWARD, FE_TONEAREST,
// FE_TONEARESTFROMZERO, FE_TOWARDZERO, FE_UPWARD, or some
// other implementation-defined value
#pragma STDC FENV_ROUND FE_DYNAMIC
#pragma STDC FENV_DEC_ROUND FE_DYNAMIC
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;however, over 27 years after c99 was standardized, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gcc.gnu.org/projects/c-status.html&quot;&gt;gcc still doesn&#39;t support any of the standard pragmas&lt;/a&gt;. they&#39;re just no-ops in gcc.&lt;/p&gt;
&lt;p&gt;some (but not all) of the functionality can be replicated with command-line flags, but those operate on the entire translation unit, whereas the pragmas can apply to individual functions and blocks.&lt;/p&gt;
&lt;p&gt;clang mostly supports the c99 pragmas, but still doesn&#39;t support the c23 pragmas.&lt;/p&gt;
&lt;p&gt;there&#39;s actually &lt;em&gt;so much&lt;/em&gt; more to say about these. i &lt;em&gt;will&lt;/em&gt; be writing more about them at some point, but rn i don&#39;t really have the motivation cuz it&#39;s such a big thing lol&lt;/p&gt;
&lt;h2&gt;qualifiers in function return types&lt;/h2&gt;
&lt;p&gt;the standard requires that qualifiers in function return types are discarded:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If, in the declaration &amp;quot;&lt;code&gt;T D1&lt;/code&gt;&amp;quot;, &lt;code&gt;D1&lt;/code&gt; has the form&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;D ( parameter-type-listopt ) attribute-specifier-sequenceopt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and the type specified for &lt;em&gt;ident&lt;/em&gt; in the declaration &amp;quot;&lt;code&gt;T D&lt;/code&gt;&amp;quot; is &amp;quot;&lt;em&gt;derived-declarator-type-list &lt;code&gt;T&lt;/code&gt;&lt;/em&gt;&amp;quot;, then the type specified for &lt;em&gt;ident&lt;/em&gt; is &amp;quot;&lt;em&gt;derived-declarator-type-list&lt;/em&gt; function returning the unqualified, non-atomic version of &lt;code&gt;T&lt;/code&gt;&amp;quot;. The optional attribute specifier appertains to the function type.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;but clang treats them as significant:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; cat t.c&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;int&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; f(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-continue syntax-shell&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;const&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; int f(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-continue syntax-shell&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clang t.c&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;t.c:2:11:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; error: conflicting types for &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;f&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;2&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;const&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; int f(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-continue syntax-shell&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt;           &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;t.c:1:5:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; note: previous declaration is here&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;1&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;int&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; f(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-continue syntax-shell&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; error generated.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the two declarations should be identical, but clang thinks they&#39;re incompatible.&lt;/p&gt;
&lt;h2&gt;outdated &lt;code&gt;va_start&lt;/code&gt; definition on musl&lt;/h2&gt;
&lt;p&gt;for some reason, musl provides its own versions of some headers which the compiler already provides itself. one of these is &lt;code&gt;stdarg.h&lt;/code&gt;, which is required to be supported by freestanding implementations and so is virtually guaranteed to already be provided by the compiler. but because it&#39;s provided by musl, compilers will use that header instead of the builtin one.&lt;/p&gt;
&lt;p&gt;this is a problem because musl&#39;s header is strictly worse than the builtin header. here&#39;s how musl defines &lt;code&gt;va_start&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;#define va_start(v,l)   __builtin_va_start(v,l)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this definition is incorrect, because c23 allows the second argument to be omitted. it should be defined like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;#if __STDC_VERSION__ &amp;gt;= 202311L
#define va_start(...) __builtin_c23_va_start(__VA_ARGS__)
#else
#define va_start(v,l) __builtin_va_start(v,l)
#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the result is that, when compiling for musl, c23&#39;s single-argument &lt;code&gt;va_start&lt;/code&gt; can&#39;t be used, even if it&#39;s supported by the compiler.&lt;/p&gt;
&lt;h2&gt;see also&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sebsite.pw/w/20261004-math_errhandling.html&quot;&gt;notes on how glibc and musl handle math errors&lt;/a&gt; (&lt;code&gt;math_errhandling&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sebsite.pw/w/20261009-strtod.html&quot;&gt;oh, apparently it&#39;s not possible to portably check for string-to-float conversion errors in standard c&lt;/a&gt;: a case where posix arguably specifies a family of functions incompatibly from standard c&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sebsite.pw/w/20260708-badstdcxx.html&quot;&gt;neither gcc nor clang are compliant with standard c++&lt;/a&gt;: gcc and clang don&#39;t store language linkage information with function types in c++, contrary to what the standard requires&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sebsite.pw/w/20261014-initstate.html&quot;&gt;yet another place where a linux man page is inaccurate&lt;/a&gt; (containing a deleted section from this post!)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
    <category>news</category>
  </item>
  <item>
    <title>oh, apparently it&#39;s not possible to portably check for string-to-float conversion errors in standard c</title>
    <link>https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-10-oh-apparently-it-s-not-possible-to-portably-check-for/</link>
    <guid isPermaLink="false">urn:aggr:item:c78d940b0932c9345eb7771586abb8d2aa7752f8</guid>
    <atom:link href="https://sebsite.pw/w/20261009-strtod.html" rel="via" type="text/html"/>
    <pubDate>Sat, 10 Oct 2026 05:37:18 +0000</pubDate>
    <dc:creator>sebsite.pw by seb</dc:creator>
    <description>Comments</description>
    <content:encoded>&lt;p&gt;this is kinda a sequel-ish to my &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sebsite.pw/w/20261004-math_errhandling.html&quot;&gt;previous post&lt;/a&gt;, in which i go over the &lt;code&gt;math_errhandling&lt;/code&gt; macro, and how math error handling is done in glibc and musl (as well as how it&#39;s specified in the standard).&lt;/p&gt;
&lt;p&gt;to summarize: &lt;code&gt;math_errhandling&lt;/code&gt; is a macro which indicates which error-handling mechanisms are supported by &lt;code&gt;math.h&lt;/code&gt; functions: &lt;code&gt;errno&lt;/code&gt; (&lt;code&gt;MATH_ERRNO&lt;/code&gt;) and/or floating-point exceptions (&lt;code&gt;MATH_ERREXCEPT&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;there&#39;s one thing i didn&#39;t mention in my previous post: there&#39;s one family of functions which is affected by &lt;code&gt;math_errhandling&lt;/code&gt; but which &lt;em&gt;isn&#39;t&lt;/em&gt; in &lt;code&gt;math.h&lt;/code&gt;: the string to float conversion functions &lt;code&gt;strtod&lt;/code&gt;, &lt;code&gt;strtof&lt;/code&gt;, &lt;code&gt;strtold&lt;/code&gt;, &lt;code&gt;strtod32&lt;/code&gt;, &lt;code&gt;strtod64&lt;/code&gt;, and &lt;code&gt;strtod128&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;7.25.2.6p12:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the correct value overflows and default rounding is in effect (7.12.2), plus or minus &lt;code&gt;HUGE_VAL&lt;/code&gt;, &lt;code&gt;HUGE_VALF&lt;/code&gt;, or &lt;code&gt;HUGE_VALL&lt;/code&gt; is returned (according to the return type and sign of the value); if the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERRNO&lt;/code&gt; is nonzero, the integer expression &lt;code&gt;errno&lt;/code&gt; acquires the value of &lt;code&gt;ERANGE&lt;/code&gt;; if the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERREXCEPT&lt;/code&gt; is nonzero, the &amp;quot;overflow&amp;quot; floating-point exception is raised.&lt;/p&gt;
&lt;p&gt;If the result underflows (7.12.2), the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; if the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERRNO&lt;/code&gt; is nonzero, whether &lt;code&gt;errno&lt;/code&gt; acquires the value &lt;code&gt;ERANGE&lt;/code&gt; is implementation-defined; if the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERREXCEPT&lt;/code&gt; is nonzero, whether the &amp;quot;underflow&amp;quot; floating-point exception is raised is implementation-defined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the linux man pages for these functions literally don&#39;t mention this &lt;em&gt;at all&lt;/em&gt;, and there actually is a reason why which i&#39;ll get to in a sec, but what the standard is saying is, if &lt;code&gt;math_errhandling&lt;/code&gt; doesn&#39;t advertise support for &lt;code&gt;errno&lt;/code&gt; (as is the case on musl, for instance), the string to float conversion functions &lt;strong&gt;don&#39;t set &lt;code&gt;errno&lt;/code&gt; on error&lt;/strong&gt;. furthermore, if the result underflows, the function &lt;strong&gt;isn&#39;t required to report an error at all&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;keep in mind that the return value alone isn&#39;t enough to determine if an error occurred, so to test for overflow, you &lt;em&gt;need&lt;/em&gt; to use one of the two error handling mechanisms.&lt;/p&gt;
&lt;p&gt;here is my attempt at a standards-blessed way of portably checking for overflow/underflow errors in the string to float functions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;feclearexcept(FE_OVERFLOW | FE_UNDERFLOW);
errno = 0;
double x = strtod(s, nullptr);
bool errored = math_errhandling &amp;amp; MATH_ERRNO
	? errno == ERANGE
	: fetestexcept(FE_OVERFLOW | FE_UNDERFLOW);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;note that this still doesn&#39;t guarantee that underflow is detected, since reporting underflow is entirely optional for the implementation.&lt;/p&gt;
&lt;p&gt;the reason you&#39;ve never done this ever (and the reason the man pages don&#39;t mention this) is that posix specifies the functions differently:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the correct value is outside the range of representable values, ±HUGE_VAL, ±HUGE_VALF, or ±HUGE_VALL shall be returned (according to the sign of the value), and &lt;code&gt;errno&lt;/code&gt; shall be set to &lt;code&gt;[ERANGE]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the correct value would cause an underflow, a value whose magnitude is no greater than the smallest normalized positive number in the return type shall be returned and &lt;code&gt;errno&lt;/code&gt; set to &lt;code&gt;[ERANGE]&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;so posix doesn&#39;t give a shit about &lt;code&gt;math_errhandling&lt;/code&gt;; it always requires the implementation to set &lt;code&gt;errno&lt;/code&gt; if overflow or underflow occurs. while it&#39;s not uncommon for posix to specify stricter requirements on functions than standard c, the fact that the man page never makes any note of this being an extension (neither &lt;code&gt;strtod(3)&lt;/code&gt; nor the posix specification &lt;code&gt;strtod(3p)&lt;/code&gt;) is really notable to me.&lt;/p&gt;
&lt;p&gt;whether or not posix&#39;s behavior is even compatible with standard c is... unclear. at least for &lt;code&gt;math.h&lt;/code&gt; functions, setting &lt;code&gt;errno&lt;/code&gt; regardless of the value of &lt;code&gt;math_errhandling&lt;/code&gt; is permitted:&lt;/p&gt;
&lt;p&gt;7.12.2p8:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a domain, pole, or range error occurs and the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERRNO&lt;/code&gt; is zero, then &lt;code&gt;errno&lt;/code&gt; shall either be set to the value corresponding to the error or left unmodified.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;but earlier on, when specifying &lt;code&gt;errno.h&lt;/code&gt;, the standard says this:&lt;/p&gt;
&lt;p&gt;7.5p3:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[...] The value of &lt;code&gt;errno&lt;/code&gt; may be set to nonzero by a library function call whether or not there is an error, provided the use of &lt;code&gt;errno&lt;/code&gt; is not documented in the description of the function in this document.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;errno&lt;/code&gt; &lt;em&gt;is documented&lt;/em&gt; in the description for these functions, and that description makes no mention of setting &lt;code&gt;errno&lt;/code&gt; when &lt;code&gt;math_errhandling &amp;amp; MATH_ERRNO&lt;/code&gt; is zero. so that suggests that posix&#39;s behavior is non-conformant.&lt;/p&gt;
&lt;p&gt;but hang on! everything i&#39;ve talked about so far is only for overflow and underflow. but if the string is malformed and can&#39;t be parsed as a number, then the standard doesn&#39;t specify any error at all:&lt;/p&gt;
&lt;p&gt;7.25.2.6p11:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The functions return the converted value, if any. If no conversion could be performed, positive or unsigned zero is returned.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;instead, you&#39;re supposed to use the &lt;code&gt;endptr&lt;/code&gt; parameter, and check afterward if &lt;code&gt;endptr == nptr&lt;/code&gt; (i.e. the end pointer is the same as the start pointer, so no data was parsed):&lt;/p&gt;
&lt;p&gt;7.25.2.6p8:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the subject sequence is empty or does not have the expected form, no conversion is performed; the value of &lt;code&gt;nptr&lt;/code&gt; is stored in the object pointed to by &lt;code&gt;endptr&lt;/code&gt;, provided that &lt;code&gt;endptr&lt;/code&gt; is not a null pointer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the man page &lt;code&gt;strtod(3)&lt;/code&gt; says something similar:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If no conversion is performed, zero is returned and (unless &lt;code&gt;endptr&lt;/code&gt; is null) the value of &lt;code&gt;nptr&lt;/code&gt; is stored in the location referenced by &lt;code&gt;endptr&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;but check out what posix says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Upon successful completion, these functions shall return the converted value. If no conversion could be performed, 0 shall be returned, and &lt;code&gt;errno&lt;/code&gt; may be set to &lt;code&gt;[EINVAL]&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the word &amp;quot;may&amp;quot; basically means that it&#39;s implementation-defined. but this is a big deal, because it&#39;s pretty common to check for errors by doing something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;errno = 0;
double x = strtod(s, nullptr);
if (errno != 0) {
	// ...
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;strtod(3)&lt;/code&gt; suggests doing exactly this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since 0 can be legitimately returned on both success and failure, the calling program should set &lt;code&gt;errno&lt;/code&gt; to 0 before the call, and then determine if an error occurred by checking whether &lt;code&gt;errno&lt;/code&gt; has a nonzero value after the call.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;but even for posix-compatible libcs, this isn&#39;t portable! different conforming libcs may behave differently if no conversion can be performed. in fact...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;errno = 0;
strtod(&amp;quot;x&amp;quot;, nullptr);
printf(&amp;quot;%d\n&amp;quot;, errno);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;on glibc, this prints 0, because glibc&#39;s &lt;code&gt;strtod&lt;/code&gt; never sets &lt;code&gt;errno&lt;/code&gt; to &lt;code&gt;EINVAL&lt;/code&gt;. musl, on the other hand, &lt;em&gt;does&lt;/em&gt; set &lt;code&gt;errno&lt;/code&gt; to &lt;code&gt;EINVAL&lt;/code&gt;, so this prints &amp;quot;22&amp;quot;. &lt;strong&gt;this is completely undocumented on the linux man page&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;it&#39;s also, from my reading of the standard, not conformant to standard c, because it&#39;s setting &lt;code&gt;errno&lt;/code&gt; to a nonzero value in a function with other documented error conditions (and as far as standard c is concerned, invalid input isn&#39;t an error condition). musl &lt;em&gt;could&lt;/em&gt; defend itself by saying that, because it doesn&#39;t set &lt;code&gt;errno&lt;/code&gt; in its &lt;code&gt;math.h&lt;/code&gt; functions, the &lt;code&gt;errno&lt;/code&gt; condition in &lt;code&gt;strtod&lt;/code&gt;&#39;s description no longer applies, therefore there&#39;s no documented use for &lt;code&gt;errno&lt;/code&gt; (since the use of &lt;code&gt;errno&lt;/code&gt; is dependent on the value of &lt;code&gt;math_errhandling&lt;/code&gt;). but that&#39;s clearly a stretch.&lt;/p&gt;
&lt;p&gt;either way, it&#39;s clear to me that the standard needs clearer wording here.&lt;/p&gt;
&lt;h2&gt;conclusion&lt;/h2&gt;
&lt;p&gt;just for fun, i figured i&#39;d conclude with a list of all the &amp;quot;correct&amp;quot; ways to check for errors in the string to float conversion functions, just to hammer the point home:&lt;/p&gt;
&lt;h3&gt;overflow&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;if you&#39;re targeting posix, set &lt;code&gt;errno&lt;/code&gt; to 0 before the call, and afterward check &lt;code&gt;copysign(result, 1.0) == HUGE_VAL &amp;amp;&amp;amp; errno == ERANGE&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;otherwise, set &lt;code&gt;errno&lt;/code&gt; to 0 &lt;em&gt;and&lt;/em&gt; call &lt;code&gt;feclearexcept(FE_OVERFLOW)&lt;/code&gt; before the call, and, if &lt;code&gt;copysign(result, 1.0) == HUGE_VAL&lt;/code&gt;, either check &lt;code&gt;errno == ERANGE&lt;/code&gt; or &lt;code&gt;fetestexcept(FE_OVERFLOW)&lt;/code&gt; after the call, depending on the value of &lt;code&gt;math_errhandling&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;if you&#39;re targeting just one implementation, and you know which error-reporting methods it supports beforehand, you can skip the &lt;code&gt;math_errhandling&lt;/code&gt; check and only support one of the two methods.&lt;/p&gt;
&lt;p&gt;if you&#39;re using &lt;code&gt;feclearexcept&lt;/code&gt;/&lt;code&gt;fetestexcept&lt;/code&gt;, make sure that the compiler knows you may be accessing the floating point environment: on gcc the relevant flag is &lt;code&gt;-ftrapping-math&lt;/code&gt;, which is the default (unless you&#39;re using &lt;code&gt;-ffast-math&lt;/code&gt;). there&#39;s also a standard pragma for this purpose: &lt;code&gt;#pragma STDC FENV_ACCESS ON&lt;/code&gt;. this pragma isn&#39;t supported by gcc though.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;underflow&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;if you&#39;re targeting posix, set &lt;code&gt;errno&lt;/code&gt; to 0 before the call, and afterward check &lt;code&gt;result == 0.0 &amp;amp;&amp;amp; errno == ERANGE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;be sure to check specifically that &lt;code&gt;errno&lt;/code&gt; is &lt;code&gt;ERANGE&lt;/code&gt;, not just that it&#39;s nonzero. otherwise you&#39;ll end up with nonportable behavior.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;otherwise, if you&#39;re just targeting one implementation, check if it documents its behavior on underflow in &lt;code&gt;math.h&lt;/code&gt; functions. the behavior is implementation-defined, so technically it&#39;s required to document it. but in practice, even clang doesn&#39;t bother documenting its implementation-defined shit, so don&#39;t hold your breath.&lt;/p&gt;
&lt;p&gt;but if it is documented, and it reports underflow errors, then, depending on the implementation&#39;s value of &lt;code&gt;math_errhandling&lt;/code&gt;, either set &lt;code&gt;errno&lt;/code&gt; to 0 before the call and check &lt;code&gt;result == 0.0 &amp;amp;&amp;amp; errno == ERANGE&lt;/code&gt; after the call, or call &lt;code&gt;feclearexcept(FE_UNDERFLOW)&lt;/code&gt; before the call and check &lt;code&gt;fetestexcept(FE_UNDERFLOW)&lt;/code&gt; after the call.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;otherwise you&#39;re SOL.&lt;/p&gt;
&lt;p&gt;if you &lt;em&gt;reeally&lt;/em&gt; need to check for underflow, and you &lt;em&gt;need&lt;/em&gt; to use the libc function for some reason, here&#39;s the only option i can think of: check if &lt;code&gt;result == 0.0&lt;/code&gt;, and if so, check yourself if the input string contains any nonzero digits before the exponent. if so, underflow occurred. this is difficult to get right though; read the description of &lt;code&gt;strtod&lt;/code&gt; et al from the standard and make sure you cover all edge-cases.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;invalid input (no conversion)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;pass in &lt;code&gt;&amp;amp;endptr&lt;/code&gt; as a second argument to the function, and then check if &lt;code&gt;endptr == nptr&lt;/code&gt;. &lt;code&gt;errno&lt;/code&gt; can&#39;t be relied on.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
    <category>news</category>
    <category>c</category>
  </item>
</channel>
</rss>
