<p>In C a signed integer is just a binary number and all binary numbers are valid integers. There is no <code>NaN</code> value.</p>
<p>C uses 2s complement for signed integers meaning there is one more negative than positive integers. It have been possible to reserve the most negative integer <code>100...000</code> to indicate an invalid integer. This would have provided a <code>NaN</code> value that could be returned from functions like <code>atoi()</code> as well the (OCD placating) feature of making the number of positive and negative integers the same.</p>
<p>I realise there may be no answer to the question <em>why wasn't this done</em>, but if there are reasons why this would have been a bad idea I would be interested to hear them. The question applies to any languages that represent signed integers in the same way that C does.</p>
<p><strong>Update</strong>: Michael comments that <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2412.pdf" rel="noreferrer">the next version of C</a> explicitly forbids the use of <code>1000...000</code> as an invalid value:</p>
<blockquote>
<ol start="3">
<li>TIGHTENING OF INTEGER REPRESENTATIONS<br />
3.1. Minimum values of signed integer types<br />
Even for two’s complement representation C17 allowed that the value with sign bit 1 and all other bits 0 might be a trap representation. We change this and are thereby in line with the changes in C++. We force that for integer types with a width of <em>N</em> the minimum value is forced to −2<sup><em>N</em>−1</sup> (and the maximum value remains at 2<sup><em>N</em>−1</sup> − 1).</li>
</ol>
</blockquote>
<p>I would be interested to hear why this decision was made. Well, it was made to comply with C++, but then the question is why does C++ do this?</p>

<c><integers>
Invalid signed integer in C and similar languages
CC BY-SA 4.0
2023-08-17T18:19:07.733Z
Ack!  In my [earlier comment](https://langdev.stackexchange.com/questions/2738/invalid-signed-integer-in-c-and-similar-languages?noredirect=1#comment5676_2738) I typed "stop" by mistake, meant "the hardware would *still* propagate quiet NaN"
Ben Voigt
CC BY-SA 4.0
2023-08-18T09:19:42.903Z
I'd see such `NaN` as a variation of `null` references, "a billion-dollar mistake".
yeputons
CC BY-SA 4.0
2023-08-18T12:20:08.503Z
Putting everything else aside, having a NaN value for integers is a terrible, I'm glad it's not a thing. If you want an "invalid" value for some reason, combine your integer with a boolean to denote "invalid" when needed; à la `std::optional` in C++.
Aykhan Hagverdili
CC BY-SA 4.0
2023-08-16T08:46:18.150Z
C doesn't actually require two's complement, it's just the most common implementation today; the next version [is *likely* to mandate it](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2412.pdf), but that hasn't happened yet, and when it does it's actually going to *ban* using that value as a trap representation.
Michael Homer
CC BY-SA 4.0
2023-08-16T13:14:18.783Z
I'm not sure what you have in mind for this. Processors don't have instructions for doing 2's-complement arithmetic where `INT_MIN` is supposed to be special, so there would be no hardware support for NaN-like behaviour; so either every arithmetic operation would have to be checked for NaN (which would be very un-C-like), or arithmetic on NaN could be undefined behaviour (making it the programmer's responsibility to not produce INT_MIN from arithmetic by accident), or it could be specified as arithmetically behaving like INT_MIN, in which case why make it a special case in the spec?
kaya3
CC BY-SA 4.0
2023-08-16T13:15:54.583Z
For `x = calc(a,b) * calc(c,d);`, wouldn't a NaN return simply get destroyed by the multiplication? To avoid that, wouldn't a lot of extra code have to be generated with every operation?
Ray Butterworth
CC BY-SA 4.0
2023-08-16T13:59:03.737Z
@Ray, only if the hardware has no support for a trap.  It's not mandated that an implementation has NaNs for floating-point - they are provided only if convenient, so why would an integer NaN be different?  That said, I'm not aware of any ISA that supports such a thing, hence no need to be able to represent it in C.
Toby Speight
CC BY-SA 4.0
2023-08-17T14:35:29.917Z
@TobySpeight: Note that "special marker value" and "trap" are different things.  IEEE floating-point has both (quiet NaN vs signalling NaN).  You could remove the trap (signalling NaN) support and the hardware would stop propagate quiet NaN.
Ben Voigt
CC BY-SA 4.0
2023-08-17T14:46:32.470Z
That's true @Ben.  The main thing is that if there's no hardware support for treating this differently from normal values, it's (possibly prohibitively) expensive to support special treatment for this value, and that's contrary to the lightweight, close-to-the-metal philosophy of C.
Toby Speight
CC BY-SA 4.0
2023-08-17T14:48:45.437Z
@Tody: Agreed that a special infectious value needs hardware support to perform well.  It's just that the hardware support need not be associated with a "trap" (processor exception)
Ben Voigt
CC BY-SA 4.0
2023-08-17T16:05:31.650Z
@BenVoigt: An implementation might also specify that INT_MIN == -INT_MAX because some computations (most likely division and remainder) might otherwise require special code to handle such values.  For example, the easiest way to handle a negative divisor if the dividend isn't -INT_MAX-1 would be to invert both divisor and dividend, but that approach would fail if the dividend is -INT_MAX-1.
supercat
<p>It always seemed self-evident to me that 2's complement makes the hardware &quot;mechanics&quot; of integer arithmetic very simple. Overflow and wraparound happen correctly and consistently &quot;automatically&quot;. The carry bit is simply discarded if there is no bit left for it. Treating the last value specially breaks the entire elegance and efficiency and would be terribly expensive, either in hardware or in software.</p>
<p>The answer to the related question why then there are NaNs in floating point implementations is: Floating point arithmetics were always terribly expensive to begin with; NaNs didn't make a dent but are useful.</p>

CC BY-SA 4.0
2023-08-17T09:35:14.473Z
2023-08-17T09:35:14.473Z
Peter - Reinstate Monica
CC BY-SA 4.0
2023-08-17T17:06:24.523Z
Yes, good point that signed 2's complement add/sub (and non-widening multiply) doesn't need special instructions, it's the same binary operation as for unsigned.  So in hardware, it wouldn't just be modifying how "the signed add instruction" worked, you'd need a whole new instruction as well as modified ALU internals.
Peter Cordes
CC BY-SA 4.0
2023-08-18T19:53:14.193Z
@PeterCordes: Did ones'-complement machines have instructions that could efficiently perform unsigned math?  So far as I can tell, any machine that could support `unsigned long long` would either be capable of handling two's-complement signed math about as efficiently as ones'-complement, or would need to have a word size of 65 bits or longer, and no ones'-complement machine has ever satisfied either criterion.
supercat
CC BY-SA 4.0
2023-08-18T21:08:44.507Z
@supercat: I don't know, I haven't studied the details of any such machines.  I just assumed they actually did have separate `add`/`sub` instructions for unsigned binary, but now that you mention it, it's possible they maybe *only* supported signed integers.  So memory address ranges were limited to signed positive?
Peter Cordes
CC BY-SA 4.0
2023-08-18T22:12:46.743Z
@PeterCordes: From what I can tell, 36 bits would have been a common word size, which would have supported a range of positive values much larger than any plausible number of usable bytes of address space.
supercat
CC BY-SA 4.0
2023-08-17T13:08:32.327Z
C standard was defined in times when 1's complement CPUs were still quite widespread (which is one of the reasons why C defines signed overflow as UB). Such CPUs do have a special value, the negative zero.
Dmitry Grigoryev
CC BY-SA 4.0
2023-08-17T16:30:38.447Z
@DmitryGrigoryev: On the flip side, the only intended effects of making signed overflow UB were to (1) say that code which relies upon it should be recognized as being potentially unusable on platforms that work differently in relevant cases; (2) say that implementations may issue diagnostics if signed overflow occurs, if configured for tasks where producing seemingly-valid-but-wrong results, untrapped unintentional overflows would be Not Acceptable, and avoiding "deliberate" integer overflows would be cheaper than writing code to detect all the situations where signed overflow might occur.
supercat
<h2>Non-C23 Implementations are Allowed to Do That</h2>
<p>The C17 standard says:</p>
<blockquote>
<p>For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; [...]</p>
</blockquote>
<p>It allows a representation to use either two’s-complement, sign-and-magnitude or one’s-complement arithmetic.  Then, it says:</p>
<blockquote>
<p>Which of these applies is implementation-defined, as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones’ complement), is a trap representation or a normal value.</p>
</blockquote>
<p>A footnote also says:</p>
<blockquote>
<p>Some combinations of padding bits might generate trap representations, for example, if one padding bit is a parity bit.</p>
</blockquote>
<p>The Burroughs Large Systems series of mainframes is an example of a computer that was in current use in 1973, when C was written, that tagged each integer with a complex descriptor.  It continued to be supported into this century (as UniSys ClearPath emode).  Implementations for machines that had integer trap values historically used the native instructions for arithmetic.</p>
<p>This cluse the native ALU instructions for basic arithmetic. As all companies that still support those architectures do so in emulation, this has been removed in the C23 draft standard.</p>

CC BY-SA 4.0
2023-08-17T15:07:12.040Z
2023-08-17T19:27:53.623Z
2023-08-17T19:27:53.623Z
Davislor
CC BY-SA 4.0
2023-08-17T16:59:14.370Z
@Peter-ReinstateMonica Corrected, although the Burroughs mainframes were being manufactured in both years (and in 1989, when ANSI C was standardized).
Davislor
CC BY-SA 4.0
2023-08-17T16:56:14.417Z
In 1973, the C programming language was written ;-) ; *The C Programming Language*, by contrast, was published in 1978, and almost certainly not in the process of being written in 1973.
Peter - Reinstate Monica
<p>Many of the compromises in the design of the C Standard were predicated upon what seemed a reasonable assumption at the time: people who write compilers will treat the programmers who use them as customers.  Compiler writers could be expected to uphold behavioral precedents in cases where doing so would be more useful to their customers than doing anything else, and in cases where customers would regard some other behavior as genuinely more useful, they would probably be better placed than the Committee to judge the pros and cons of that alternative.</p>
<p>Even if a platform uses two's-complement arithmetic, that does not necessarily imply that the platform's most efficient means of processing all operations for operands other than <code>-INT_MAX-1</code> will handle that value in a manner consistent with its numerical meaning.  If, for example, a platform has a &quot;signed number divided by non-negative number&quot; operation, processing <code>x/-n</code> as <code>(-x)/n</code> may be more efficient than having to set a flag, compute <code>x/n</code>, and invert the result, but using that approach when computing <code>(-INT_MAX-1)/-2</code> erroneously yield -(INT_MAX/2)-1.</p>
<p>If code will never use a divided lower than -INT_MAX, any extra machine code to handle such dividends would make the program bigger and slower while offering zero benefit.  If an implementation specifies that <code>INT_MIN</code> is <code>-INT_MAX</code>, but in all cases not involving a dividend of <code>-INT_MAX-1</code> behaved using normal two's-complement arithmetic, such an implementation might thus be more useful for many tasks than one which adds extra machine code to correctly handle all corner cases associated with divisions involving the value <code>-INT_MAX-1</code>.</p>
<p>The only times a compiler writer for a two's-complement platform would have been be expected to even <em>care</em> about whether <code>INT_MIN</code> was <code>-INT_MAX</code> or <code>-INT_MAX-1</code> would have been those where treating it as <code>-INT_MAX-1</code> would be meaningfully more expensive than treating it as <code>-INT_MAX</code>.  The notion that some compiler writers would decide that &quot;non-portable or erroneous&quot; really meant &quot;non-portable and therefore erroneous&quot;, and would go out of their way to avoid behaving meaningfully in situations not addressed by the Standard would have been regarded as absurd, and nobody imagined compiler writers doing such a thing.</p>

CC BY-SA 4.0
2023-08-16T20:25:30.497Z
2023-08-16T20:25:30.497Z
supercat
<p>I'd challenge the usefulness of &quot;special values&quot; representing invalidity.</p>
<p><strong>During computation</strong></p>
<p>During some computation, a step that cannot produce a valid result typically means that it's useless to continue with the following steps, and that the method containing these steps can't fulfill its contract, and so the next higher method in the call stack, and so on. In well-behaved code, this propagates up to a point where some method can deal with the failure of its subordinate method call. That's exactly the behavior we get for free from exceptions, and that needs code heavily cluttered with special-case handling in a special-value based approach. So, I'd recommend to turn failed calculations into exceptions as early as possible. Alas, this isn't supported well in our current CPUs.</p>
<p><strong>As stored value</strong></p>
<p>When storing a value, we often need to also express the situation that this value is unavailable, unknown, not applicable, or whatever. We want a robust way to represent that, one that makes any calculation made with invalid data fail early.</p>
<p>Without redesign of our CPUs, just declaring some of the bit patterns as &quot;invalid&quot; can't achieve that. The CPU will happily do calculations with e.g. the 1000...0000 pattern, giving results that now look perfectly valid although they are nonsense.</p>
<p>So, you need a storage scheme that can express that, e.g. a reference that can be <code>null</code>, an <code>Optional&lt;int&gt;</code>, a struct with an additional <code>bool valid</code> field, or whatever. All this comes with a cost, as it isn't directly supported by our CPUs.</p>

CC BY-SA 4.0
2023-08-17T08:56:40.223Z
2023-08-17T08:56:40.223Z
Ralf Kleberhoff
CC BY-SA 4.0
2023-08-18T07:38:21.673Z
There's always a tradeoff between things like performance, language abstraction level, code readability, debuggability, robustness and so on. In my sector of software development, we are always willing to sacrifice a few percent of performance to gain some other desirable properties. But there will be other sectors having a different point of view. So, if OP is targetting a number-crunching audience, exceptions might not be the way to go.
Ralf Kleberhoff
CC BY-SA 4.0
2023-08-17T16:18:05.330Z
A costly aspect of exceptions is that they force an ordering on many otherwise-side-effect-free operations.  If a calculation would yield NaN in case of overflow, and a compiler can show that the result of a calculation will only be used in certain circumstances, it can skip the calculation *entirely* any time those circumstances don't apply.  If, however, overflow behavior was defined as yielding an exception, then such calculations would need to be performed without regard for whether anything would care about the results thus produced.
supercat
CC BY-SA 4.0
2023-08-17T16:24:44.067Z
If allowing looser execution sequencing would let a compiler generate code that was 5% faster than would otherwise be possible, and less than 1% of runs would have any overflow, having the 99 out of 100 runs that don't overflow take an hour each, and having the one that yields overflow also take an hour but yield NaN and need to be rerun to find out where the overflow occurred, taking an extra 63 minutes, would be more efficient than having all runs take 63 minutes.
supercat
<h2>C is about what the machine <em>may</em> have (even today)</h2>
<p>A little history, before the answer. C is a direct successor of B, and everything in B has really only one type: the machine register. With only this &quot;numeric&quot; type, it was possible to execute machine implemented numeric operations (register used as a number), and also was possible to execute machine memory operations (register used as memory address).</p>
<p>That's it. All of everything has to be implemented in terms of this simple abstraction. All variables, all parameters, all state: it is just abstracted hardware numeric integers of some size. And people are using this to show it is possible to write an entire operating system for a big computer that (for a time) has no hard drive!</p>
<p>One guy of the team took a vacation and when he returned, B was already transmuted to C, because the team are writing a operating system for <em>another</em> machine, and by then it was observed that:</p>
<ul>
<li><p>The language abstraction of <em>not</em> knowing the machine register size facilitates a lot of code <strike>reuse</strike> porting;</p>
</li>
<li><p>The new machines are showing things like floating point numbers and sub-word sized numbers, that in turn <em>may</em> have sizes and operations distinct from the machine register.</p>
</li>
</ul>
<p>Because of this, it was necessary to <em>annotate</em> what parts of code are using these newfangled features, with distinct tags: <strike><code>reg</code></strike> <code>int</code>, <code>float</code>, <code>char</code>.</p>
<p>With this in mind:</p>
<blockquote>
<p>It have been possible to reserve the most negative integer <code>100...000</code> to indicate an invalid integer.</p>
</blockquote>
<p>And:</p>
<blockquote>
<p>I realise there may be no answer to the question why wasn't this done, but if there are reasons why this would have been a bad idea I would be interested to hear them.</p>
</blockquote>
<p>In general, machines don't reserve an entire number for invalid register/integer value at hardware level, so there was no demand to represent this on B or C, then or now.</p>
<p>At same time, C was and is used in a wide range of machines, with different register sizes and different register formats (one-complement, two-complement, big-endian, little-endian, and so on), so reserving an entire number for <code>NaN</code> purpose will generate all sorts of discontinuity, for little gain, because...</p>
<blockquote>
<p>This would have provided a <code>NaN</code> value that could be returned from functions like <code>atoi()</code>.</p>
</blockquote>
<p>... failure reporting in C utilizes a different <a href="https://en.wikipedia.org/wiki/Programming_idiom" rel="nofollow noreferrer">idiom</a>. In a function where failure is possible, it is preferable to have any normal return operated as an argument pointer, and the success or failure status reported as the function return.</p>
<pre class="lang-c prettyprint-override"><code>status func( arg , &amp; ret )
</code></pre>

CC BY-SA 4.0
2023-08-16T13:26:06.793Z
2024-10-11T02:30:57.907Z
2024-10-11T02:30:57.907Z
André L F S Bacci
CC BY-SA 4.0
2023-08-18T19:44:54.523Z
The evolution of modern C is based on the assumption that the only situations where some observable deviations from precise sequential program execution might be acceptable are those where no possible behaviors would be viewed as unacceptable.  Most platforms could at minimal cost offer a guarantee that a race condition on a read would have no side effect beyond yielding possibly meaningless data, but there's no standard means of indicating a compiler should process code written using "ordinary" syntax in such fashion.
supercat
CC BY-SA 4.0
2023-08-16T16:51:53.747Z
The history of C and CPUs is fairly intertwined. C was designed as a high-level assembly language for the CPUs of its time, then newer CPUs were designed to run C efficiently -- including instructions to build `strlen`. In this sense, I guess the answer to the OP is therefore: doesn't exist on mainstream CPUs.
Matthieu M.
CC BY-SA 4.0
2023-08-17T16:12:54.193Z
@MatthieuM.: Perhaps a better example of processors designing around C is that x86's SSE/SSE2 vector and scalar FP math instructions include an instruction for FP to int conversion with truncation toward zero, for `(int)my_float`.   Previously, compilers had to change the x87 rounding mode to truncation and then back, or use multiple instructions.  For `strlen`, are you thinking of 8086 `repne scasb`? It was designed between 76 and 78, very early years for C (1972), and was aimed at Pascal explicit-length strings. `repne scasb` didn't get fast-strings microcode in P6, not until Sapphire Rapids
Peter Cordes
CC BY-SA 4.0
2023-08-17T16:14:08.790Z
AArch64's memory ordering is another example of designing a CPU around high-level languages.  They provide release-store and acquire-load that avoid StoreLoad reordering wrt. each other but not other instructions, giving seq_cst as cheaply as possible (not much stronger than C/C++ requires).  Of course the fact that they didn't initially provide weaker `acquire`/`release` (until ARMv8.3 `ldapr` which can StoreLoad reorder including with `stlr`) might have been a sign they were catering to Java more than C++11 / C11.
Peter Cordes
<h2>Use <a href="https://www.r-project.org/" rel="noreferrer">the R language</a></h2>
<p>R has <a href="https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/NA" rel="noreferrer"><code>NA</code></a>, which can be an integer and has the semantics of NaN. R's <code>NA</code> can also be a boolean (logical) and it's the result of comparisons on <code>NA</code>. You can check whether a value is <code>NA</code> using <code>is.na</code>.</p>
<pre class="lang-r prettyprint-override"><code>NA + 5L # NA
5L + NA # NA
typeof(NA + 5L) # &quot;integer&quot;
NA &gt; NA # NA
typeof(NA &gt; NA) # &quot;logical&quot;
</code></pre>
<p>Internally, R's <code>NA_INTEGER</code> is represented by <code>INT_MIN</code>. Another fun fact: <code>NA_REAL</code> is completely separate from <code>NaN</code> (the &quot;regular&quot; floating-point NaN), and they have some odd behavior...</p>
<pre class="lang-r prettyprint-override"><code>NaN + NA # NaN
NA + NaN # NA
typeof(5L + NaN) # &quot;double&quot;
is.na(NaN) # TRUE
NaN == NaN # NA
</code></pre>
<h2>Make your own (using wrappers)</h2>
<p>Create a class which wraps integers and reserves some bytes to represent NaN. The constructor takes a regular integer and either returns your language's equivalent of &quot;None&quot; or errors if given something out-of-range.</p>
<pre class="lang-cpp prettyprint-override"><code>class NanInt {
    int repr;
    explicit NanInt(int value) : repr(value) {}
  public:
    static NanInt regular(int value) {
        // You could also throw a C++ exception or use std::optional
        assert(value != INT_MIN &amp;&amp; &quot;INT_MIN is reserved for NaN&quot;);
        return NanInt(value);
    }
    static NanInt NAN = NanInt(INT_MIN);
    bool isNan() { return repr == INT_MIN; }
    int toRegular() {
        assert(repr != INT_MIN &amp;&amp; &quot;can't convert, this is NaN&quot;);
        return repr;
    }
    NanInt operator+(NanInt other) {
        // In the rare case `repr + other.repr == INT_MIN`, let it be
        return repr == INT_MIN || other.repr == INT_MIN || 
            add_overflows(repr, other.repr) ? NAN : NanInt(repr + other.repr);
    }
    NanInt operator==(NanInt other) {
        // Preserve the behavior where NaN != NaN
        return repr != INT_MIN &amp;&amp; other.repr != INT_MIN &amp;&amp; repr == other.repr;
    }
    // ...
}
</code></pre>
<h3>Pros</h3>
<ul>
<li>Possible to some extent in nearly any language: any language with classes, and even a language without classes this can be emulated using functions.</li>
<li>In languages which support wrapper classes which have the same representation as the data they wrap (<a href="https://kotlinlang.org/docs/inline-classes.html" rel="noreferrer">Kotlin</a>, <a href="https://docs.scala-lang.org/scala3/book/types-opaque-types.html" rel="noreferrer">Scala</a>, Rust and C/C++), this is a zero-cost abstraction</li>
<li>Many languages let you overload operators and implement one or more <a href="https://learn.microsoft.com/en-us/dotnet/standard/generics/math#numeric-interfaces" rel="noreferrer"><code>Numeric</code> interfaces</a> which are used by arithmetic functions like <code>max</code> and <code>Iterable.sum</code>, so that your custom class can have the same API and be used in the same functions as regular integers</li>
<li>Some languages, like Haskell, even support custom types for integer literals (so you can write something like <code>4 :: NanInt</code>).</li>
</ul>
<h3>Cons</h3>
<ul>
<li>Not all languages let you define zero-cost wrappers (in particular dynamic ones), and not all let you overload operators. In Java or JavaScript, your &quot;wrapper&quot; will have considerable overhead due to being a heap-allocated class;  it must be have its own functions <code>NanInt#add(NanInt)</code>, <code>NanInt#equals(NanInt)</code>; lenient casting means you must be careful to use these functions and not the standard <code>+</code> and <code>==</code> operators (which will compile but produce incorrect behavior); and you must explicitly handle when your <code>NanInt</code> is <code>null</code>.</li>
<li>You have to override all the operators you plan to use, which is a lot of boilerplate. In languages without <code>Numeric</code> interfaces, you must also define your own functions like <code>max</code> and <code>Iterable.sum</code>.</li>
<li>Rarely does a language support checking integer literals or constructors at compile-time, so if you somehow manage to accidentally define <code>SHOULD_BE_REGULAR = NanInt::regular(INT_MIN)</code>, it will crash at runtime.</li>
<li>Related, constructing one of your special integers has (albeit minor) runtime overhead.</li>
</ul>
<h2>Non-2^n-bit integers</h2>
<p>Augmenting the above, there are languages which let you write integers which don't fit entirely within 32 or 64 bits, so that the extra bits can be used to represent invalid or other kinds of values. This lets you write a wrapper for a type which is guaranteed to not be Nan, which can be embedded into other types and not take up the full value.</p>
<h3>Rust non-zero integers</h3>
<p>Ex: <a href="https://doc.rust-lang.org/stable/std/num/struct.NonZeroI32.html" rel="noreferrer">NonZeroI32</a>, <a href="https://doc.rust-lang.org/stable/std/num/struct.NonZeroUsize.html" rel="noreferrer">NonZeroUsize</a> (there are ones for every signed and unsigned integer type). More information in the <a href="https://rust-lang.github.io/rfcs/2307-concrete-nonzero-types.html" rel="noreferrer">RFC</a></p>
<p>These are special wrappers for integers which exclude <code>0</code>. They are &quot;special&quot; in that an <code>Option&lt;NonZeroInt&gt;</code> has the same memory layout as <code>NonZeroInt</code>, and <code>None</code> represents the data <code>0x0</code>.</p>
<p>While these wrappers only exclude 0, you can define your own wrapper which excludes a different number (like <code>INT_MIN == 0</code>, and every other integer below 1 is subtracted by 1). As long as you use <code>repr(transparent)</code> (and probably even if not), you get the same special behavior when your wrapper is used within an <code>Option</code>.</p>
<pre class="lang-rust prettyprint-override"><code>use std::num::NonZeroI32;
use std::cmp::Ordering;
use std::ops::Add;
use std::fmt::{Display, Formatter};

#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct NotNanI32(NonZeroI32);

#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
struct NanI32(Option&lt;NonNanI32&gt;);

impl NonNanI32 {
  pub fn try_new(x: i32) -&gt; Option&lt;Self&gt; {
    if (x == i32::MIN) {
      None
    } else if (x &lt; 1) {
      Some(Self(unsafe { NonZeroI32::new_unchecked(x.unchecked_sub(1)) }))
    } else {
      Some(Self(unsafe { NonZeroI32::new_unchecked(x) }))
    }
  }

  pub fn get(self) -&gt; i32 {
    if (self.0.get() &lt; 0) {
      unsafe { self.0.get().unchecked_add(1) }
    } else {
      self.0.get()
    }
  }
}

impl Add for NonNanI32 {
  // Standard rust panics on overflow and underflow, so we do the same,
  // but you could also make this return Option&lt;NonNanI32&gt;
  type Output = Self;
  fn add(self, rhs: Self) -&gt; Self {
    let lhs = self.0.get();
    let rhs = rhs.0.get();
    match lhs.checked_add(rhs) {
      None if lhs.saturating_add(rhs) == i32::MIN =&gt; panic!(&quot;underflow&quot;),
      None =&gt; panic!(&quot;overflow&quot;),
      Some(x) if x == i32::MIN =&gt; panic!(&quot;underflow&quot;),
      Some(x) if x &lt; 1 =&gt; {
        Self(unsafe { NonZeroI32::new_unchecked(x.unchecked_sub(1)) })
      }
      Some(x) =&gt; Self(unsafe { NonZeroI32::new_unchecked(x) })
    }
  }
}
impl Display for NonNanI32 {
  fn fmt(&amp;self, f: &amp;mut Formatter&lt;'_&gt;) -&gt; std::fmt::Result {
    write!(f, &quot;{}&quot;, self.0)
  }
}
// ...

impl NanI32 {
  pub fn try_regular(x: i32) -&gt; Option&lt;Self&gt; {
    NonNanI32::try_new(x).map(Self)
  }

  pub const NAN: Self = Self(None);

  pub fn regular(self) -&gt; Option&lt;NonNanI32&gt; {
    self.0
  }

  pub fn is_nan(self) -&gt; bool {
    self.0.is_none()
  }
}

impl Add for NanI32 {
  // Unlike the C++ version, we still panic on overflow
  type Output = Self;
  fn add(self, other: Self) -&gt; Self {
    match (self.0, other.0) {
      (Some(lhs), Some(rhs)) =&gt; Self(Some(lhs + rhs)),
      _ =&gt; Self::NAN
    } 
  }
}
impl PartialEq for NanI32 {
  fn eq(&amp;self, other: Self) -&gt; bool {
    !self.is_nan() &amp;&amp; !other.is_nan() &amp;&amp; self.0 == other.0
  }
}
impl PartialOrd for NanI32 {
  fn partial_cmp(&amp;self, other: Self) -&gt; Option&lt;Ordering&gt; {
    match (self.0, other.0) {
      (Some(lhs), Some(rhs)) =&gt; Some(lhs.cmp(rhs)),
      _ =&gt; None
    }
  }
}
impl Display for NanI32 {
  fn fmt(&amp;self, f: &amp;mut Formatter&lt;'_&gt;) -&gt; std::fmt::Result {
    match (self.0) {
      None =&gt; write!(f, &quot;NaN&quot;),
      Some(regular) =&gt; write!(f, &quot;{}&quot;, regular)
    }
  }
}
// ...
</code></pre>
<h3>31-bit or 63-bit integers</h3>
<p>Some languages' native integer types are 31 bits (<a href="https://en.wikipedia.org/wiki/31-bit_computing#Ruby_&amp;_Smalltalk" rel="noreferrer">Ruby and Smalltalk</a>) or 63 bits (<a href="https://blog.janestreet.com/what-is-gained-and-lost-with-63-bit-integers/" rel="noreferrer">OCaml</a>). In this case, the extra bit represents a tag to determine if the data is boxed (i.e. whether it's an integer or object).</p>
<p>If you write a custom language, the extra bit may represent whether the integer is NaN.</p>
<p>Unfortunately, most existing languages don't support custom-width integers, so a custom language is the only way to use this so far. But it may be coming in the future, most notably to Zig: see <a href="https://github.com/ziglang/zig/issues/3806" rel="noreferrer">this open issue</a> and <a href="https://alic.dev/blog/custom-bitwidth" rel="noreferrer">this blog post</a></p>
<h2>Use a language like JavaScript which doesn't distinguish between integers and other values, so all numbers permit NaN</h2>
<p>This is one of the benefits of representing all numbers (including integers) within <code>double</code>. Whenever a function expects an integer, you can pass <code>NaN</code>. However, while the compiler will always let you do this, it may not work for all functions, because the developer may not have planned for <code>NaN</code> when writing them.</p>
<p>It's also one of the benefits of untyped language in general: you can simply pass a value like <code>Float.Nan</code> or a custom <code>Nan</code> with overloaded equality which has the semantics you want.</p>
<pre class="lang-python prettyprint-override"><code>class Nan:
    def __eq__(self, other):
        return False
    def __lt__(self, other):
        return False
    # ...
    def __str__(self):
        return &quot;Nan&quot;
    def __add__(self, other):
        return Nan
    # ...

Nan + 3 # Nan
Nan() == Nan() # False
</code></pre>
<hr />
<h2>Aside: Why do floating-points have a defined NaN, and integers don't?</h2>
<p>The answer for floats seems to be: convention; floating points more often need NaN; floating points already represent infinity and much larger/smaller numbers than integers; and the floating point representation has extra bits which allow for NaN. For integers: because it's rare to need a number which can be NaN but can't be represented by a floating-point; it's rarer to need such a number that also won't sometimes be too small or large to fit in a fixed-width; and if one really needs such a number, one can simply define a custom wrapper, which in many languages will be zero-cost and have nearly the same API as regular integer.</p>
<p>See also:</p>
<ul>
<li><a href="https://stackoverflow.com/questions/17615634/why-doesnt-integer-represent-nan-in-java">Why doesn't integer represent NaN in Java?</a></li>
<li><a href="https://stackoverflow.com/questions/74393426/why-are-nan-floats">Why are NaN floats?</a></li>
<li><a href="https://en.wikipedia.org/wiki/NaN" rel="noreferrer">Wikipedia for NaN (including float and integer representations)</a></li>
</ul>

CC BY-SA 4.0
2023-08-16T14:02:17.840Z
2023-08-16T14:07:59.750Z
2023-08-16T14:07:59.750Z
tarzh
<p>C was designed as an abstraction which covered features of hardware existing at the time. Since there were no mainstream CPUs that would produce a special <code>NaN</code> value as a result of a division by zero, overflows, etc., no such feature was introduced in C. Introducing a feature for which there is no widespread hardware support would instantly take C out of the &quot;high-level assembler&quot; league, as building even the simplest C program would require to link it against the NaN emulation library, and, more importantly, make it much slower as each integer operation would now require several assembly instructions which produce NaN values when required.</p>
<p>By the way, NaN is not the most useful feature in integer arithmetic which lacks most operations able to produce it, such as <code>log</code>, <code>sqrt</code>, etc. Divisions by zero and overflows would benefit from <code>+Inf</code> and <code>-Inf</code> values much more, a feature which was later implemented in the form of <a href="https://en.wikipedia.org/wiki/Saturation_arithmetic" rel="noreferrer">Saturation arithmetic</a>.</p>

CC BY-SA 4.0
2023-08-17T08:12:34.250Z
2023-08-17T08:17:55.120Z
2023-08-17T08:17:55.120Z
Dmitry Grigoryev
CC BY-SA 4.0
2023-08-18T07:40:32.253Z
@supercat "NaN would be a useful feature for integer types" - that's what I'm disagreeing with in the second paragraph. What would you do with a NaN in your program that you couldn't achieve with writing a code which doesn't overflow in the first place (and have it tested with polyspace and `-ftrapv`)? Inf is useful: if you're decoding colors, you can't go brighter than pure white. But having a NaN instead means you failed to decode the colors and need to fix your program.
Dmitry Grigoryev
CC BY-SA 4.0
2023-08-18T17:43:22.760Z
Languages with defined trap-on-overflow semantics severely restrict the ability of compilers to process operations in parallel or defer operations until their results will be used (and skip them entirely if the results never end up being used).  Efficiently supporting NaN behavior with integers would require hardware support (just as with floating-point), but making hardware for a parallel-add instruction which will set the upper bits to 10000000... if an overflow occurs, or if a source operand's upper bits are already 10000000 operate as fast as an ordinary "add" would not be....
supercat
CC BY-SA 4.0
2023-08-18T17:45:39.183Z
...as hard as trying to design one which would in case of overflow allow the generated code to behave as though only the actions which should be treated as happening "before" the overflow would actually have any effect.
supercat
CC BY-SA 4.0
2023-08-20T09:30:57.397Z
@supercat You don't use overflow traps in production code, instead, you run a debug version with overflow traps through a bunch of tests (or use a code analysis tool capable to detect overflows) and make sure your code is overflow-free. So in production here is no penalty to parallel processing, or performance in general, and you don't need NaNs either. And during tests, the performance advantage of NaNs will be gone once you realize you need to scan all intermediate results for NaNs in order to find bugs where they happen, not at the very end.
Dmitry Grigoryev
CC BY-SA 4.0
2023-08-21T05:25:07.500Z
Such a philosophy is dangerous when applied to programs that receive input from potential;ly malicious sources, or programs where the effects of overflow could be worse than a forced abnormal program termination.  A good language should a=make overflow checking cheap enough that for many tasks it should be used unless its effects on performance are significant and unacceptable.
supercat
CC BY-SA 4.0
2023-08-21T09:09:01.620Z
@supercat You seem to imply that it's impossible to guarantee that an algorithm is overflow-free. This is not true, there are even tools that perform formal verification w.r.t overflows. Where I work (automotive) being overflow-free is one of the requirements before you can release your SW.
Dmitry Grigoryev
CC BY-SA 4.0
2023-08-21T14:43:09.693Z
In many cases, the fastest machine code that could be produced from source that could be guaranteed to be overflow free for all possible inputs would be slower than the fastest machine code that could be guaranteed to prevent all situations where a program might otherwise produce numerically incorrect output as a consequence of integer overflow.  If overflow trapping is a language-level feature, a compiler can skip overflow checking on computations whose output will be ignored, or substitute computations that won't overflow.  For example, if a program computes `ushort1*1234500/100`...
supercat
CC BY-SA 4.0
2023-08-21T14:44:56.583Z
...letting the compiler replace the calculation with `ushort1*12345` without regard for whether the original computation would have overflowed would yield better code than a compiler could achieve if the source program had included overflow-checking logic, but without ever producing seemingly-valid-but-wrong results.  Likewise, adding a group of numbers using 64-bit math and then bounds-checking the result may be faster than overflow-checking each individual addition, but again without sacrificing numerical correctness.
supercat
CC BY-SA 4.0
2023-08-23T07:26:56.160Z
@supercat I'm not sure I get the point of your example. First, unsigned overflow is not UB, so `ushort1*1234500/100` might be the correct code, and replacing it with `ushort1*12345` would be wrong. Second, if overflow trapping is enabled, the compiler should compile `ushort1*1234500/100` to `ushort1?overflow_trap():0`, not to `ushort1*12345`. And finally "overflow-free code" means the programmer is not allowed to write `ushort1*1234500/100`, so they replace it with `ushort1*12345`, it that's what they meant.
Dmitry Grigoryev
CC BY-SA 4.0
2023-08-23T14:53:35.333Z
Because of promotion rules, the computation `ushort1*1234500/100` would be performed using signed arithmetic. Further, in most scenarios that use overflow trapping, producing arithmetically-correct results is desirable, and refusing to produce any results is acceptable; the only unacceptable outcome would be producing arithmetically-incorrect results. If handling all possible inputs in arithmetically-correct fashion would be cheaper than trapping on inputs that would yield overflow if processed using minimal specified width types, optimal machine code to meet requirements would do the former.
supercat
CC BY-SA 4.0
2023-08-23T15:00:10.707Z
If the programmer didn't know that the result of the multiplication would be divided by 100, but did know that all inputs for which the program would be required to produce results would 1739 or less, code would be "overflow free" if the programmer included code to force an error if `ushort1` is 1740 or greater.  Doing so, however, would prevent the compiler that did know the result would be divided by 100 from satisfying application requirements more efficiently.  Further, if the results of the calculation would be sometimes used and sometimes ignored, putting the compiler in charge...
supercat
CC BY-SA 4.0
2023-08-23T15:02:27.007Z
...of overflow trapping would allow it to defer the multiplication and any overflow trapping until the result is found to be necessary (or skip the multiply and the trap it if it isn't).  If the trap logic had been part of "user code", a compiler would have to ensure that no code following the trap logic would run in cases where the out-of-range trap would fire, even if the results of the computation would otherwise be ignored.
supercat
CC BY-SA 4.0
2023-08-17T16:14:30.600Z
NaN would be a useful feature for integer types, if the required maximum supported negative value for signed long long were relaxed to 0x7F00000000000000, so that any value could be forced to NaN by writing its upper byte, and if there were a recognized category of implementations where integer overflow was defined as yielding NaN.  When performing multi-word arithmetic, some platforms would benefit from writing the lower portions of a result before performing computations on the upper portions, before they could know if overflow would occur or had occurred.  If a high word...
supercat
CC BY-SA 4.0
2023-08-17T16:15:11.050Z
...which is the ones'-complement of the largest positive value were used as indicating NaN, however, that would greatly facilitate latching NaN behavior.
supercat
<p>Making equal numbers of positive and negative numbers is not necessarily a pleasing mathematical property. You get rid of the case that &quot;INT_MIN is its own negation&quot; but at a cost. Two's complement uses all the bits to nicely implement a cyclic group of order a power of two by shifting the equivalence class representatives from &quot;unsigned&quot; {0, ..., 2^n-1} to &quot;signed&quot; {-2^(n-1), ..., 2^(n-1)-1}. If one value is not a number, then you are missing one equivalence class. I suppose if you never intend to use overflow (C signed overflow is currently undefined behavior), then it doesn't matter. You could even have multiple invalid integers as long as you never step out of your &quot;valid range&quot;.</p>
<p>Relevant: <a href="https://jeremykun.com/2023/07/10/twos-complement-and-group-theory" rel="noreferrer">https://jeremykun.com/2023/07/10/twos-complement-and-group-theory</a></p>

CC BY-SA 4.0
2023-08-16T18:53:16.187Z
2023-08-16T19:55:07.500Z
2023-08-16T19:55:07.500Z
qwr
# Invalid signed integer in C and similar languages
Posted by: username_0 on 2023-08-16
In C a signed integer is just a binary number and all binary numbers are valid integers. There is no `NaN` value.

C uses 2s complement for signed integers meaning there is one more negative than positive integers. It have been possible to reserve the most negative integer `100...000` to indicate an invalid integer. This would have provided a `NaN` value that could be returned from functions like `atoi()` as well the (OCD placating) feature of making the number of positive and negative integers the same.

I realise there may be no answer to the question *why wasn't this done*, but if there are reasons why this would have been a bad idea I would be interested to hear them. The question applies to any languages that represent signed integers in the same way that C does.

**Update**: Michael comments that [the next version of C](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2412.pdf) explicitly forbids the use of `1000...000` as an invalid value:

> 3. TIGHTENING OF INTEGER REPRESENTATIONS  
>    3.1. Minimum values of signed integer types  
>    Even for two’s complement representation C17 allowed that the value with sign bit 1 and all other bits 0 might be a trap representation. We change this and are thereby in line with the changes in C++. We force that for integer types with a width of *N* the minimum value is forced to −2*N*−1 (and the maximum value remains at 2*N*−1 − 1).

I would be interested to hear why this decision was made. Well, it was made to comply with C++, but then the question is why does C++ do this?
**Tags:** c integers
> **Comments:**
> **username_4**: C doesn't actually require two's complement, it's just the most common implementation today; the next version [is *likely* to mandate it](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2412.pdf), but that hasn't happened yet, and when it does it's actually going to *ban* using that value as a trap representation.  
> **username_5**: I'm not sure what you have in mind for this. Processors don't have instructions for doing 2's-complement arithmetic where `INT_MIN` is supposed to be special, so there would be no hardware support for NaN-like behaviour; so either every arithmetic operation would have to be checked for NaN (which would be very un-C-like), or arithmetic on NaN could be undefined behaviour (making it the programmer's responsibility to not produce INT_MIN from arithmetic by accident), or it could be specified as arithmetically behaving like INT_MIN, in which case why make it a special case in the spec?  
> **username_6**: For `x = calc(a,b) * calc(c,d);`, wouldn't a NaN return simply get destroyed by the multiplication? To avoid that, wouldn't a lot of extra code have to be generated with every operation?  
> **username_7**: @Ray, only if the hardware has no support for a trap.  It's not mandated that an implementation has NaNs for floating-point - they are provided only if convenient, so why would an integer NaN be different?  That said, I'm not aware of any ISA that supports such a thing, hence no need to be able to represent it in C.  
> **username_1**: @TobySpeight: Note that "special marker value" and "trap" are different things.  IEEE floating-point has both (quiet NaN vs signalling NaN).  You could remove the trap (signalling NaN) support and the hardware would stop propagate quiet NaN.  
> **username_7**: That's true @Ben.  The main thing is that if there's no hardware support for treating this differently from normal values, it's (possibly prohibitively) expensive to support special treatment for this value, and that's contrary to the lightweight, close-to-the-metal philosophy of C.  
> **username_1**: @Tody: Agreed that a special infectious value needs hardware support to perform well.  It's just that the hardware support need not be associated with a "trap" (processor exception)  
> **username_8**: @BenVoigt: An implementation might also specify that INT_MIN == -INT_MAX because some computations (most likely division and remainder) might otherwise require special code to handle such values.  For example, the easiest way to handle a negative divisor if the dividend isn't -INT_MAX-1 would be to invert both divisor and dividend, but that approach would fail if the dividend is -INT_MAX-1.  
> **username_1**: Ack!  In my [earlier comment](https://langdev.stackexchange.com/questions/2738/invalid-signed-integer-in-c-and-similar-languages?noredirect=1#comment5676_2738) I typed "stop" by mistake, meant "the hardware would *still* propagate quiet NaN"  
> **username_2**: I'd see such `NaN` as a variation of `null` references, "a billion-dollar mistake".  
> **username_3**: Putting everything else aside, having a NaN value for integers is a terrible, I'm glad it's not a thing. If you want an "invalid" value for some reason, combine your integer with a boolean to denote "invalid" when needed; à la `std::optional` in C++.  
---
## 8 answers
### Answer by username_14 on 2023-08-16. Score: 14
C is about what the machine may have (even today)
A little history, before the answer. C is a direct successor of B, and everything in B has really only one type: the machine register. With only this "numeric" type, it was possible to execute machine implemented numeric operations (register used as a number), and also was possible to execute machine memory operations (register used as memory address).
That's it. All of everything has to be implemented in terms of this simple abstraction. All variables, all parameters, all state: it is just abstracted hardware numeric integers of some size. And people are using this to show it is possible to write an entire operating system for a big computer that (for a time) has no hard drive!
One guy of the team took a vacation and when he returned, B was already transmuted to C, because the team are writing a operating system for another machine, and by then it was observed that:

The language abstraction of not knowing the machine register size facilitates a lot of code reuse porting;

The new machines are showing things like floating point numbers and sub-word sized numbers, that in turn may have sizes and operations distinct from the machine register.


Because of this, it was necessary to annotate what parts of code are using these newfangled features, with distinct tags: reg int, float, char.
With this in mind:

It have been possible to reserve the most negative integer 100...000 to indicate an invalid integer.

And:

I realise there may be no answer to the question why wasn't this done, but if there are reasons why this would have been a bad idea I would be interested to hear them.

In general, machines don't reserve an entire number for invalid register/integer value at hardware level, so there was no demand to represent this on B or C, then or now.
At same time, C was and is used in a wide range of machines, with different register sizes and different register formats (one-complement, two-complement, big-endian, little-endian, and so on), so reserving an entire number for NaN purpose will generate all sorts of discontinuity, for little gain, because...

This would have provided a NaN value that could be returned from functions like atoi().

... failure reporting in C utilizes a different idiom. In a function where failure is possible, it is preferable to have any normal return operated as an argument pointer, and the success or failure status reported as the function return.
status func( arg , & ret )
> **Comments:**
> **username_15** (2023-08-16): The history of C and CPUs is fairly intertwined. C was designed as a high-level assembly language for the CPUs of its time, then newer CPUs were designed to run C efficiently -- including instructions to build `strlen`. In this sense, I guess the answer to the OP is therefore: doesn't exist on mainstream CPUs.  
> **username_10** (2023-08-17): @MatthieuM.: Perhaps a better example of processors designing around C is that x86's SSE/SSE2 vector and scalar FP math instructions include an instruction for FP to int conversion with truncation toward zero, for `(int)my_float`.   Previously, compilers had to change the x87 rounding mode to truncation and then back, or use multiple instructions.  For `strlen`, are you thinking of 8086 `repne scasb`? It was designed between 76 and 78, very early years for C (1972), and was aimed at Pascal explicit-length strings. `repne scasb` didn't get fast-strings microcode in P6, not until Sapphire Rapids  
> **username_10** (2023-08-17): AArch64's memory ordering is another example of designing a CPU around high-level languages.  They provide release-store and acquire-load that avoid StoreLoad reordering wrt. each other but not other instructions, giving seq_cst as cheaply as possible (not much stronger than C/C++ requires).  Of course the fact that they didn't initially provide weaker `acquire`/`release` (until ARMv8.3 `ldapr` which can StoreLoad reorder including with `stlr`) might have been a sign they were catering to Java more than C++11 / C11.  
> **username_8** (2023-08-18): The evolution of modern C is based on the assumption that the only situations where some observable deviations from precise sequential program execution might be acceptable are those where no possible behaviors would be viewed as unacceptable.  Most platforms could at minimal cost offer a guarantee that a race condition on a read would have no side effect beyond yielding possibly meaningless data, but there's no standard means of indicating a compiler should process code written using "ordinary" syntax in such fashion.  
---
### Answer by username_11 on 2023-08-17. Score: 7
C was designed as an abstraction which covered features of hardware existing at the time. Since there were no mainstream CPUs that would produce a special `NaN` value as a result of a division by zero, overflows, etc., no such feature was introduced in C. Introducing a feature for which there is no widespread hardware support would instantly take C out of the "high-level assembler" league, as building even the simplest C program would require to link it against the NaN emulation library, and, more importantly, make it much slower as each integer operation would now require several assembly instructions which produce NaN values when required.

By the way, NaN is not the most useful feature in integer arithmetic which lacks most operations able to produce it, such as `log`, `sqrt`, etc. Divisions by zero and overflows would benefit from `+Inf` and `-Inf` values much more, a feature which was later implemented in the form of [Saturation arithmetic](https://en.wikipedia.org/wiki/Saturation_arithmetic).
> **Comments:**
> **username_8** (2023-08-17): NaN would be a useful feature for integer types, if the required maximum supported negative value for signed long long were relaxed to 0x7F00000000000000, so that any value could be forced to NaN by writing its upper byte, and if there were a recognized category of implementations where integer overflow was defined as yielding NaN.  When performing multi-word arithmetic, some platforms would benefit from writing the lower portions of a result before performing computations on the upper portions, before they could know if overflow would occur or had occurred.  If a high word...  
> **username_8** (2023-08-17): ...which is the ones'-complement of the largest positive value were used as indicating NaN, however, that would greatly facilitate latching NaN behavior.  
> **username_11** (2023-08-18): @supercat "NaN would be a useful feature for integer types" - that's what I'm disagreeing with in the second paragraph. What would you do with a NaN in your program that you couldn't achieve with writing a code which doesn't overflow in the first place (and have it tested with polyspace and `-ftrapv`)? Inf is useful: if you're decoding colors, you can't go brighter than pure white. But having a NaN instead means you failed to decode the colors and need to fix your program.  
> **username_8** (2023-08-18): Languages with defined trap-on-overflow semantics severely restrict the ability of compilers to process operations in parallel or defer operations until their results will be used (and skip them entirely if the results never end up being used).  Efficiently supporting NaN behavior with integers would require hardware support (just as with floating-point), but making hardware for a parallel-add instruction which will set the upper bits to 10000000... if an overflow occurs, or if a source operand's upper bits are already 10000000 operate as fast as an ordinary "add" would not be....  
> **username_8** (2023-08-18): ...as hard as trying to design one which would in case of overflow allow the generated code to behave as though only the actions which should be treated as happening "before" the overflow would actually have any effect.  
> **username_11** (2023-08-20): @supercat You don't use overflow traps in production code, instead, you run a debug version with overflow traps through a bunch of tests (or use a code analysis tool capable to detect overflows) and make sure your code is overflow-free. So in production here is no penalty to parallel processing, or performance in general, and you don't need NaNs either. And during tests, the performance advantage of NaNs will be gone once you realize you need to scan all intermediate results for NaNs in order to find bugs where they happen, not at the very end.  
> **username_8** (2023-08-21): Such a philosophy is dangerous when applied to programs that receive input from potential;ly malicious sources, or programs where the effects of overflow could be worse than a forced abnormal program termination.  A good language should a=make overflow checking cheap enough that for many tasks it should be used unless its effects on performance are significant and unacceptable.  
> **username_11** (2023-08-21): @supercat You seem to imply that it's impossible to guarantee that an algorithm is overflow-free. This is not true, there are even tools that perform formal verification w.r.t overflows. Where I work (automotive) being overflow-free is one of the requirements before you can release your SW.  
> **username_8** (2023-08-21): In many cases, the fastest machine code that could be produced from source that could be guaranteed to be overflow free for all possible inputs would be slower than the fastest machine code that could be guaranteed to prevent all situations where a program might otherwise produce numerically incorrect output as a consequence of integer overflow.  If overflow trapping is a language-level feature, a compiler can skip overflow checking on computations whose output will be ignored, or substitute computations that won't overflow.  For example, if a program computes `ushort1*1234500/100`...  
> **username_8** (2023-08-21): ...letting the compiler replace the calculation with `ushort1*12345` without regard for whether the original computation would have overflowed would yield better code than a compiler could achieve if the source program had included overflow-checking logic, but without ever producing seemingly-valid-but-wrong results.  Likewise, adding a group of numbers using 64-bit math and then bounds-checking the result may be faster than overflow-checking each individual addition, but again without sacrificing numerical correctness.  
> **username_11** (2023-08-23): @supercat I'm not sure I get the point of your example. First, unsigned overflow is not UB, so `ushort1*1234500/100` might be the correct code, and replacing it with `ushort1*12345` would be wrong. Second, if overflow trapping is enabled, the compiler should compile `ushort1*1234500/100` to `ushort1?overflow_trap():0`, not to `ushort1*12345`. And finally "overflow-free code" means the programmer is not allowed to write `ushort1*1234500/100`, so they replace it with `ushort1*12345`, it that's what they meant.  
> **username_8** (2023-08-23): Because of promotion rules, the computation `ushort1*1234500/100` would be performed using signed arithmetic. Further, in most scenarios that use overflow trapping, producing arithmetically-correct results is desirable, and refusing to produce any results is acceptable; the only unacceptable outcome would be producing arithmetically-incorrect results. If handling all possible inputs in arithmetically-correct fashion would be cheaper than trapping on inputs that would yield overflow if processed using minimal specified width types, optimal machine code to meet requirements would do the former.  
> **username_8** (2023-08-23): If the programmer didn't know that the result of the multiplication would be divided by 100, but did know that all inputs for which the program would be required to produce results would 1739 or less, code would be "overflow free" if the programmer included code to force an error if `ushort1` is 1740 or greater.  Doing so, however, would prevent the compiler that did know the result would be divided by 100 from satisfying application requirements more efficiently.  Further, if the results of the calculation would be sometimes used and sometimes ignored, putting the compiler in charge...  
> **username_8** (2023-08-23): ...of overflow trapping would allow it to defer the multiplication and any overflow trapping until the result is found to be necessary (or skip the multiply and the trap it if it isn't).  If the trap logic had been part of "user code", a compiler would have to ensure that no code following the trap logic would run in cases where the out-of-range trap would fire, even if the results of the computation would otherwise be ignored.  
---
### Answer by username_17 on 2023-08-16. Score: 7
Making equal numbers of positive and negative numbers is not necessarily a pleasing mathematical property. You get rid of the case that "INT\_MIN is its own negation" but at a cost. Two's complement uses all the bits to nicely implement a cyclic group of order a power of two by shifting the equivalence class representatives from "unsigned" {0, ..., 2^n-1} to "signed" {-2^(n-1), ..., 2^(n-1)-1}. If one value is not a number, then you are missing one equivalence class. I suppose if you never intend to use overflow (C signed overflow is currently undefined behavior), then it doesn't matter. You could even have multiple invalid integers as long as you never step out of your "valid range".

Relevant: <https://jeremykun.com/2023/07/10/twos-complement-and-group-theory>
---
### Answer by username_16 on 2023-08-16. Score: 6
Use the R language
R has NA, which can be an integer and has the semantics of NaN. R's NA can also be a boolean (logical) and it's the result of comparisons on NA. You can check whether a value is NA using is.na.
NA + 5L # NA
5L + NA # NA
typeof(NA + 5L) # "integer"
NA > NA # NA
typeof(NA > NA) # "logical"

Internally, R's NA_INTEGER is represented by INT_MIN. Another fun fact: NA_REAL is completely separate from NaN (the "regular" floating-point NaN), and they have some odd behavior...
NaN + NA # NaN
NA + NaN # NA
typeof(5L + NaN) # "double"
is.na(NaN) # TRUE
NaN == NaN # NA

Make your own (using wrappers)
Create a class which wraps integers and reserves some bytes to represent NaN. The constructor takes a regular integer and either returns your language's equivalent of "None" or errors if given something out-of-range.
class NanInt {
    int repr;
    explicit NanInt(int value) : repr(value) {}
  public:
    static NanInt regular(int value) {
        // You could also throw a C++ exception or use std::optional
        assert(value != INT_MIN && "INT_MIN is reserved for NaN");
        return NanInt(value);
    }
    static NanInt NAN = NanInt(INT_MIN);
    bool isNan() { return repr == INT_MIN; }
    int toRegular() {
        assert(repr != INT_MIN && "can't convert, this is NaN");
        return repr;
    }
    NanInt operator+(NanInt other) {
        // In the rare case `repr + other.repr == INT_MIN`, let it be
        return repr == INT_MIN || other.repr == INT_MIN || 
            add_overflows(repr, other.repr) ? NAN : NanInt(repr + other.repr);
    }
    NanInt operator==(NanInt other) {
        // Preserve the behavior where NaN != NaN
        return repr != INT_MIN && other.repr != INT_MIN && repr == other.repr;
    }
    // ...
}

Pros

Possible to some extent in nearly any language: any language with classes, and even a language without classes this can be emulated using functions.
In languages which support wrapper classes which have the same representation as the data they wrap (Kotlin, Scala, Rust and C/C++), this is a zero-cost abstraction
Many languages let you overload operators and implement one or more Numeric interfaces which are used by arithmetic functions like max and Iterable.sum, so that your custom class can have the same API and be used in the same functions as regular integers
Some languages, like Haskell, even support custom types for integer literals (so you can write something like 4 :: NanInt).

Cons

Not all languages let you define zero-cost wrappers (in particular dynamic ones), and not all let you overload operators. In Java or JavaScript, your "wrapper" will have considerable overhead due to being a heap-allocated class;  it must be have its own functions NanInt#add(NanInt), NanInt#equals(NanInt); lenient casting means you must be careful to use these functions and not the standard + and == operators (which will compile but produce incorrect behavior); and you must explicitly handle when your NanInt is null.
You have to override all the operators you plan to use, which is a lot of boilerplate. In languages without Numeric interfaces, you must also define your own functions like max and Iterable.sum.
Rarely does a language support checking integer literals or constructors at compile-time, so if you somehow manage to accidentally define SHOULD_BE_REGULAR = NanInt::regular(INT_MIN), it will crash at runtime.
Related, constructing one of your special integers has (albeit minor) runtime overhead.

Non-2^n-bit integers
Augmenting the above, there are languages which let you write integers which don't fit entirely within 32 or 64 bits, so that the extra bits can be used to represent invalid or other kinds of values. This lets you write a wrapper for a type which is guaranteed to not be Nan, which can be embedded into other types and not take up the full value.
Rust non-zero integers
Ex: NonZeroI32, NonZeroUsize (there are ones for every signed and unsigned integer type). More information in the RFC
These are special wrappers for integers which exclude 0. They are "special" in that an Option has the same memory layout as NonZeroInt, and None represents the data 0x0.
While these wrappers only exclude 0, you can define your own wrapper which excludes a different number (like INT_MIN == 0, and every other integer below 1 is subtracted by 1). As long as you use repr(transparent) (and probably even if not), you get the same special behavior when your wrapper is used within an Option.
use std::num::NonZeroI32;
use std::cmp::Ordering;
use std::ops::Add;
use std::fmt::{Display, Formatter};

#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct NotNanI32(NonZeroI32);

#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
struct NanI32(Option);

impl NonNanI32 {
  pub fn try_new(x: i32) -> Option {
    if (x == i32::MIN) {
      None
    } else if (x < 1) {
      Some(Self(unsafe { NonZeroI32::new_unchecked(x.unchecked_sub(1)) }))
    } else {
      Some(Self(unsafe { NonZeroI32::new_unchecked(x) }))
    }
  }

  pub fn get(self) -> i32 {
    if (self.0.get() < 0) {
      unsafe { self.0.get().unchecked_add(1) }
    } else {
      self.0.get()
    }
  }
}

impl Add for NonNanI32 {
  // Standard rust panics on overflow and underflow, so we do the same,
  // but you could also make this return Option
  type Output = Self;
  fn add(self, rhs: Self) -> Self {
    let lhs = self.0.get();
    let rhs = rhs.0.get();
    match lhs.checked_add(rhs) {
      None if lhs.saturating_add(rhs) == i32::MIN => panic!("underflow"),
      None => panic!("overflow"),
      Some(x) if x == i32::MIN => panic!("underflow"),
      Some(x) if x < 1 => {
        Self(unsafe { NonZeroI32::new_unchecked(x.unchecked_sub(1)) })
      }
      Some(x) => Self(unsafe { NonZeroI32::new_unchecked(x) })
    }
  }
}
impl Display for NonNanI32 {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    write!(f, "{}", self.0)
  }
}
// ...

impl NanI32 {
  pub fn try_regular(x: i32) -> Option {
    NonNanI32::try_new(x).map(Self)
  }

  pub const NAN: Self = Self(None);

  pub fn regular(self) -> Option {
    self.0
  }

  pub fn is_nan(self) -> bool {
    self.0.is_none()
  }
}

impl Add for NanI32 {
  // Unlike the C++ version, we still panic on overflow
  type Output = Self;
  fn add(self, other: Self) -> Self {
    match (self.0, other.0) {
      (Some(lhs), Some(rhs)) => Self(Some(lhs + rhs)),
      _ => Self::NAN
    } 
  }
}
impl PartialEq for NanI32 {
  fn eq(&self, other: Self) -> bool {
    !self.is_nan() && !other.is_nan() && self.0 == other.0
  }
}
impl PartialOrd for NanI32 {
  fn partial_cmp(&self, other: Self) -> Option {
    match (self.0, other.0) {
      (Some(lhs), Some(rhs)) => Some(lhs.cmp(rhs)),
      _ => None
    }
  }
}
impl Display for NanI32 {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    match (self.0) {
      None => write!(f, "NaN"),
      Some(regular) => write!(f, "{}", regular)
    }
  }
}
// ...

31-bit or 63-bit integers
Some languages' native integer types are 31 bits (Ruby and Smalltalk) or 63 bits (OCaml). In this case, the extra bit represents a tag to determine if the data is boxed (i.e. whether it's an integer or object).
If you write a custom language, the extra bit may represent whether the integer is NaN.
Unfortunately, most existing languages don't support custom-width integers, so a custom language is the only way to use this so far. But it may be coming in the future, most notably to Zig: see this open issue and this blog post
Use a language like JavaScript which doesn't distinguish between integers and other values, so all numbers permit NaN
This is one of the benefits of representing all numbers (including integers) within double. Whenever a function expects an integer, you can pass NaN. However, while the compiler will always let you do this, it may not work for all functions, because the developer may not have planned for NaN when writing them.
It's also one of the benefits of untyped language in general: you can simply pass a value like Float.Nan or a custom Nan with overloaded equality which has the semantics you want.
class Nan:
    def __eq__(self, other):
        return False
    def __lt__(self, other):
        return False
    # ...
    def __str__(self):
        return "Nan"
    def __add__(self, other):
        return Nan
    # ...

Nan + 3 # Nan
Nan() == Nan() # False


Aside: Why do floating-points have a defined NaN, and integers don't?
The answer for floats seems to be: convention; floating points more often need NaN; floating points already represent infinity and much larger/smaller numbers than integers; and the floating point representation has extra bits which allow for NaN. For integers: because it's rare to need a number which can be NaN but can't be represented by a floating-point; it's rarer to need such a number that also won't sometimes be too small or large to fit in a fixed-width; and if one really needs such a number, one can simply define a custom wrapper, which in many languages will be zero-cost and have nearly the same API as regular integer.
See also:

Why doesn't integer represent NaN in Java?
Why are NaN floats?
Wikipedia for NaN (including float and integer representations)
---
### Answer by username_9 on 2023-08-17. Score: 3
It always seemed self-evident to me that 2's complement makes the hardware "mechanics" of integer arithmetic very simple. Overflow and wraparound happen correctly and consistently "automatically". The carry bit is simply discarded if there is no bit left for it. Treating the last value specially breaks the entire elegance and efficiency and would be terribly expensive, either in hardware or in software.

The answer to the related question why then there are NaNs in floating point implementations is: Floating point arithmetics were always terribly expensive to begin with; NaNs didn't make a dent but are useful.
> **Comments:**
> **username_11** (2023-08-17): C standard was defined in times when 1's complement CPUs were still quite widespread (which is one of the reasons why C defines signed overflow as UB). Such CPUs do have a special value, the negative zero.  
> **username_8** (2023-08-17): @DmitryGrigoryev: On the flip side, the only intended effects of making signed overflow UB were to (1) say that code which relies upon it should be recognized as being potentially unusable on platforms that work differently in relevant cases; (2) say that implementations may issue diagnostics if signed overflow occurs, if configured for tasks where producing seemingly-valid-but-wrong results, untrapped unintentional overflows would be Not Acceptable, and avoiding "deliberate" integer overflows would be cheaper than writing code to detect all the situations where signed overflow might occur.  
> **username_10** (2023-08-17): Yes, good point that signed 2's complement add/sub (and non-widening multiply) doesn't need special instructions, it's the same binary operation as for unsigned.  So in hardware, it wouldn't just be modifying how "the signed add instruction" worked, you'd need a whole new instruction as well as modified ALU internals.  
> **username_8** (2023-08-18): @PeterCordes: Did ones'-complement machines have instructions that could efficiently perform unsigned math?  So far as I can tell, any machine that could support `unsigned long long` would either be capable of handling two's-complement signed math about as efficiently as ones'-complement, or would need to have a word size of 65 bits or longer, and no ones'-complement machine has ever satisfied either criterion.  
> **username_10** (2023-08-18): @supercat: I don't know, I haven't studied the details of any such machines.  I just assumed they actually did have separate `add`/`sub` instructions for unsigned binary, but now that you mention it, it's possible they maybe *only* supported signed integers.  So memory address ranges were limited to signed positive?  
> **username_8** (2023-08-18): @PeterCordes: From what I can tell, 36 bits would have been a common word size, which would have supported a range of positive values much larger than any plausible number of usable bytes of address space.  
---
### Answer by username_12 on 2023-08-17. Score: 3
## Non-C23 Implementations are Allowed to Do That

The C17 standard says:

> For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; [...]

It allows a representation to use either two’s-complement, sign-and-magnitude or one’s-complement arithmetic. Then, it says:

> Which of these applies is implementation-defined, as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones’ complement), is a trap representation or a normal value.

A footnote also says:

> Some combinations of padding bits might generate trap representations, for example, if one padding bit is a parity bit.

The Burroughs Large Systems series of mainframes is an example of a computer that was in current use in 1973, when C was written, that tagged each integer with a complex descriptor. It continued to be supported into this century (as UniSys ClearPath emode). Implementations for machines that had integer trap values historically used the native instructions for arithmetic.

This cluse the native ALU instructions for basic arithmetic. As all companies that still support those architectures do so in emulation, this has been removed in the C23 draft standard.
> **Comments:**
> **username_9** (2023-08-17): In 1973, the C programming language was written ;-) ; *The C Programming Language*, by contrast, was published in 1978, and almost certainly not in the process of being written in 1973.  
> **username_12** (2023-08-17): @Peter-ReinstateMonica Corrected, although the Burroughs mainframes were being manufactured in both years (and in 1989, when ANSI C was standardized).  
---
### Answer by username_8 on 2023-08-16. Score: 2
Many of the compromises in the design of the C Standard were predicated upon what seemed a reasonable assumption at the time: people who write compilers will treat the programmers who use them as customers. Compiler writers could be expected to uphold behavioral precedents in cases where doing so would be more useful to their customers than doing anything else, and in cases where customers would regard some other behavior as genuinely more useful, they would probably be better placed than the Committee to judge the pros and cons of that alternative.

Even if a platform uses two's-complement arithmetic, that does not necessarily imply that the platform's most efficient means of processing all operations for operands other than `-INT_MAX-1` will handle that value in a manner consistent with its numerical meaning. If, for example, a platform has a "signed number divided by non-negative number" operation, processing `x/-n` as `(-x)/n` may be more efficient than having to set a flag, compute `x/n`, and invert the result, but using that approach when computing `(-INT_MAX-1)/-2` erroneously yield -(INT\_MAX/2)-1.

If code will never use a divided lower than -INT\_MAX, any extra machine code to handle such dividends would make the program bigger and slower while offering zero benefit. If an implementation specifies that `INT_MIN` is `-INT_MAX`, but in all cases not involving a dividend of `-INT_MAX-1` behaved using normal two's-complement arithmetic, such an implementation might thus be more useful for many tasks than one which adds extra machine code to correctly handle all corner cases associated with divisions involving the value `-INT_MAX-1`.

The only times a compiler writer for a two's-complement platform would have been be expected to even *care* about whether `INT_MIN` was `-INT_MAX` or `-INT_MAX-1` would have been those where treating it as `-INT_MAX-1` would be meaningfully more expensive than treating it as `-INT_MAX`. The notion that some compiler writers would decide that "non-portable or erroneous" really meant "non-portable and therefore erroneous", and would go out of their way to avoid behaving meaningfully in situations not addressed by the Standard would have been regarded as absurd, and nobody imagined compiler writers doing such a thing.
---
### Answer by username_13 on 2023-08-17. Score: 2
I'd challenge the usefulness of "special values" representing invalidity.

**During computation**

During some computation, a step that cannot produce a valid result typically means that it's useless to continue with the following steps, and that the method containing these steps can't fulfill its contract, and so the next higher method in the call stack, and so on. In well-behaved code, this propagates up to a point where some method can deal with the failure of its subordinate method call. That's exactly the behavior we get for free from exceptions, and that needs code heavily cluttered with special-case handling in a special-value based approach. So, I'd recommend to turn failed calculations into exceptions as early as possible. Alas, this isn't supported well in our current CPUs.

**As stored value**

When storing a value, we often need to also express the situation that this value is unavailable, unknown, not applicable, or whatever. We want a robust way to represent that, one that makes any calculation made with invalid data fail early.

Without redesign of our CPUs, just declaring some of the bit patterns as "invalid" can't achieve that. The CPU will happily do calculations with e.g. the 1000...0000 pattern, giving results that now look perfectly valid although they are nonsense.

So, you need a storage scheme that can express that, e.g. a reference that can be `null`, an `Optional`, a struct with an additional `bool valid` field, or whatever. All this comes with a cost, as it isn't directly supported by our CPUs.
> **Comments:**
> **username_8** (2023-08-17): A costly aspect of exceptions is that they force an ordering on many otherwise-side-effect-free operations.  If a calculation would yield NaN in case of overflow, and a compiler can show that the result of a calculation will only be used in certain circumstances, it can skip the calculation *entirely* any time those circumstances don't apply.  If, however, overflow behavior was defined as yielding an exception, then such calculations would need to be performed without regard for whether anything would care about the results thus produced.  
> **username_8** (2023-08-17): If allowing looser execution sequencing would let a compiler generate code that was 5% faster than would otherwise be possible, and less than 1% of runs would have any overflow, having the 99 out of 100 runs that don't overflow take an hour each, and having the one that yields overflow also take an hour but yield NaN and need to be rerun to find out where the overflow occurred, taking an extra 63 minutes, would be more efficient than having all runs take 63 minutes.  
> **username_13** (2023-08-18): There's always a tradeoff between things like performance, language abstraction level, code readability, debuggability, robustness and so on. In my sector of software development, we are always willing to sacrifice a few percent of performance to gain some other desirable properties. But there will be other sectors having a different point of view. So, if OP is targetting a number-crunching audience, exceptions might not be the way to go.  
---
