November 30, 2021·4 min read·engineeringcstandardsintegerssafety

N2867 questions whether checked arithmetic includes bit-precise integers

Two accepted C proposals leave an interaction unresolved. N2867 offers wording to exclude bit-precise integer types from checked arithmetic until their combined behavior has more support.

The phrase "any integer type" changes meaning when a language adds another family of integer types. Two proposals can each receive approval while leaving their intersection unexplored. That is the issue David Svoboda raises in N2867. The checked arithmetic wording covers integer types broadly, and the newly accepted bit-precise types appear to enter that category without a separate decision about supporting them.

The WG14 document log lists the paper on November 30. N2867, "Checked N-Bit Integers?", is dated November 28. Its question mark is substantive. The paper does not supply an implementation design for checked bit-precise arithmetic. It offers restrictive wording if the committee decides that the two accepted facilities should not yet interact.

I find this a useful case in language design because the problem comes from composition. A short rule can look complete when reviewed against the types that existed when it was written. Adding another type family requires revisiting that quantifier, even if the original wording does not change.

Two June decisions create the question

N2867 reports that the June meeting accepted N2709's language-wording alternative for N-bit integers. It also accepted the core integer-safety proposal from N2683 together with N2776. Although the authors knew about each other's work, Svoboda says that awareness did not appear in the normative wording of either document.

The checked-operation declarations describe ckd_add, ckd_sub, and ckd_mul in the proposed stdckdint.h header. Each accepts a result pointer and two input operands. The input types need not match. The quoted wording permits any integer types except plain char, bool, and enumeration types, with a similar restriction on the modifiable result object.

Before the new family entered the draft, that category excluded bit-precise types by their absence. After both proposals were accepted, the same words appear to include them. An unchanged sentence therefore acquires a new implementation obligation. N2867 asks whether that consequence reflects a deliberate committee decision or an unexamined interaction.

The distinction matters to implementers. Supporting a generic arithmetic macro over a known collection of types is a different task from supporting every available bit width. The paper cites limited implementation experience as a reason the committee might choose to restrict the intersection. It does not establish that such support is impossible.

Checked operations use the result's range

The earlier N2683 integer-safety paper explains the core arithmetic model. These operations calculate as if their inputs had an infinitely ranged integer type, then compare the mathematical result with what the destination type can represent. The boolean return reports whether the result is inexact for that destination.

This differs from performing an ordinary C addition and checking afterward. Ordinary arithmetic conversions can select an intermediate type narrower than the destination. Storing that intermediate result in a larger object does not retroactively widen the operation. The checked operation takes the destination type into account through its result pointer.

The paper also identifies a limit that remains important regardless of N2867. In an expression such as ckd_add(&result, x + y, z), the addition x + y still occurs under ordinary arithmetic rules before it becomes an input. The checked macro governs its own operation. It does not transform arbitrary arithmetic already present in an argument into checked arithmetic.

An illustrative sequence using the proposed core interface separates both additions:

#include <stdckdint.h>

bool add_three(int x, int y, int z, int *result)
{
    int partial;
    if (ckd_add(&partial, x, y))
        return true;
    return ckd_add(result, partial, z);
}

This illustrates proposed interfaces rather than a portable C17 program. It reports failure if either intermediate addition is inexact. That policy is part of this example. An algorithm concerned only with the final mathematical sum might need a different strategy, because an intermediate sum can exceed a selected type even if later cancellation would bring the final value back into range.

The proposed edit excludes the new family

N2867's candidate wording adds "a bit-precise integer type" to the excluded input and result types. It preserves the existing ability to use different permitted input types. The edit narrows the macro's domain without changing the mathematical model for the types that remain supported.

This is the opposite of a proposal to introduce checked N-bit operations. The paper acknowledges that supporting them would be desirable, but says more work and perhaps implementation experience would be needed. It presents exclusion as an available committee choice, not as a fact already settled by the paper's publication.

The distinction also separates precision from checking. A bit-precise type describes an arithmetic width, while a checked operation reports whether a mathematical result fits its selected destination. Merely using a type with a carefully chosen number of bits does not make its ordinary operators checked. Conversely, a checked operation on ordinary integer types can report an unrepresentable result without introducing a new bit-precise type. Combining the two facilities needs a specification for their joint domain, even though their purposes are compatible in principle. N2867 focuses on that domain question rather than claiming that either facility replaces the other or removes the need for the other's rules.

The acknowledgements trace the question to Joseph Myers's reflector observation about the supplemental integer-safety proposal. Further discussion found that the core proposal had the same omission. This history explains why the paper is about an interaction rather than a defect in one operator's individual arithmetic definition.

I prefer that interaction to be settled explicitly. A promise of support for every integer type has to account for the types actually in the language. Quietly leaving implementers to infer the intended scope risks disagreement between headers and compiler facilities, precisely where checked arithmetic is meant to give programs a dependable operation.

The immediate consequence for a reader of the developing C draft is to distinguish the accepted facilities from the unresolved combination. Bit-precise integers and checked operations each have their own design. N2867 records the missing decision about their intersection and provides concrete wording for restricting it. It supplies no basis for assuming that the checked macros already support arbitrary bit-precise operands and destinations.