Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to add ssize_t a by size_t b?

220 views
Skip to first unread message

wij

unread,
Oct 1, 2021, 11:32:11 AM10/1/21
to
To simply the question of "a+b":

ssize_t add(ssize_t a, size_t b) {
if(a+b would overflow) { set errno=ERANGE; }
a+=b; // ?
return a;
}

Another example:
ssize_t a=SSIZE_T_MIN;
size_t b=SIZE_T_MAX;
a+=b; // Is this OK? Or, How the addition is done correctly?

Guillaume

unread,
Oct 1, 2021, 1:39:40 PM10/1/21
to
ssize_t is not standard C. It's defined in POSIX.
While it's a signed integer, whereas size_t is unsigned, I don't know
how it relates to size_t on a given implementation in terms of width.
Mixing the two doesn't seem like a good idea. At least if you want
portability or even just want to know when a mixed sum is going to overflow.

With that said, a more general fact is that artihmetic operations
involving unsigned integers only can never "overflow" in C, because the
result is always modulo N: quoting the standard: "A computation
involving unsigned operands can never overflow, because a result
that cannot be represented by the resulting unsigned integer
type is reduced modulo the number that is one greater than the
largest value that can be represented by the resulting type."

Now, except for the case of all operands being unsigned, all other
combinations would yield an "undefined behavior", as said here: "If an
exceptional condition occurs during the evaluation of an expression
(that is, if the result is not mathematically defined or not in the
range of representable values for its type), the behavior is undefined."

In short, if 'a+b' can't be represented with the resulting type (which
you call "would overflow"), then it's 'a+b (mod 2^N)' with N being the
bit width of the resulting integer type, if both a and b are unsigned.
In any other case, including your case above, it's undefined behavior.

So, the result of adding a ssize_t integer to a size_t integer is
undefined per the standard if the sum "would overflow". All you can
safely do here is test before adding.

Note that while, on many platforms, the result will effectively be
computed modulo 2^N as with unsigned operands only (because it's often
the cheapest way of doing it with many ISAs), you can't count on this
according to the standard. Not just that, but the operation itself could
well raise an exception, even though it's uncommon. So ideally, for
portable code, you should test *first* and only issue the operation if
the result "would not overflow".

Scott Lurndal

unread,
Oct 1, 2021, 2:31:18 PM10/1/21
to
Guillaume <mes...@bottle.org> writes:
>Le 01/10/2021 à 17:32, wij a écrit :
>> To simply the question of "a+b":
>>
>> ssize_t add(ssize_t a, size_t b) {
>> if(a+b would overflow) { set errno=ERANGE; }
>> a+=b; // ?
>> return a;
>> }
>>
>> Another example:
>> ssize_t a=SSIZE_T_MIN;
>> size_t b=SIZE_T_MAX;
>> a+=b; // Is this OK? Or, How the addition is done correctly?
>
>ssize_t is not standard C. It's defined in POSIX.
>While it's a signed integer, whereas size_t is unsigned, I don't know
>how it relates to size_t on a given implementation in terms of width.

POSIX specifies that sizeof(ssize_t) == sizeof(size_t).

>Mixing the two doesn't seem like a good idea. At least if you want
>portability or even just want to know when a mixed sum is going to overflow.

The C rules on mixing signed and unsigned values are well defined.

If the operand that has unsigned integer type has rank greater
than or equal to the rank of the type of the other operand, the
operand with signed integer type is converted to the type of
the operand with unsigned integer type.

James Kuyper

unread,
Oct 1, 2021, 2:53:26 PM10/1/21
to
On 10/1/21 1:39 PM, Guillaume wrote:
> Le 01/10/2021 à 17:32, wij a écrit :
>> To simply the question of "a+b":
>>
>> ssize_t add(ssize_t a, size_t b) {
>> if(a+b would overflow) { set errno=ERANGE; }
>> a+=b; // ?
>> return a;
>> }
>>
>> Another example:
>> ssize_t a=SSIZE_T_MIN;
>> size_t b=SIZE_T_MAX;
>> a+=b; // Is this OK? Or, How the addition is done correctly?
>
> ssize_t is not standard C. It's defined in POSIX.
> While it's a signed integer, whereas size_t is unsigned, I don't know
> how it relates to size_t on a given implementation in terms of width.

ssize_t is a signed integer type with the same width as size_t.
Therefore, they should have the same integer conversion rank. Since the
sign bit is included in the width, SSIZE_MAX is guaranteed to be smaller
than SIZE_MAX.

The usual arithmetic conversions apply (6.5.6p5). The ssize_t value is
first converted to size_t (6.3.1.8p1). That is a well-defined conversion
- negative ssize_t values have SIZE_MAX+1 added to them (6.3.1.3p2). The
sum is calculated using size_t math, which will produce what I assume is
the desired result even if a is negative, so long as it is smaller than b.

Upon assignment, the result is converted to ssize_t. Values greater than
SSIZE_MAX would not result in undefined behavior, as you suggested.
Instead, they would result in an implementation-defined value or the
raising of an implementation-defined signal (6.3.1.3p3). This code will
probably not work as desired on an implementation that chooses to raise
a signal, and the implementation-defined value is not guaranteed to be
the one that he wants. Therefore, setting errno would not be sufficient,
the final conversion must be actively prevented from occurring if it
would otherwise overflow.

I would write the function as:

size_t c = a + b;
if(c > SSIZE_MAX)
return overflow_value;
else
return c;

where overflow_value is whatever value he wants add() to return when
there's an overflow. Depending upon the intended use, it might be either
a constant, or a value calculated from the value of c, but either way it
must be a valid ssize_t value.

Keith Thompson

unread,
Oct 1, 2021, 5:13:58 PM10/1/21
to
In standard C, you don't, because ssize_t doesn't exist (unless you
define it yourself).

However, POSIX does define a type ssize_t, which is a signed integer
type that can represent values up to SSIZE_MAX.

The rules are given in N1570 6.3.1.8, "Usual arithmetic conversions".

It's likely, but not guaranteed, that size_t and ssize_t have the same
rank. If so, then the ssize_t value is converted to size_t, and
assuming SSIZE_MAX <= SIZE_MAX the conversion does not change the
value. The addition is then done with two size_t (unsigned) operands,
with the usual wraparound semantics if the result exceeds SIZE_MAX.

It's very likely that ssize_t is the signed type that corresponds to the
unsigned type size_t; for example, if size_t is unsigned long, then
ssize_t is probably long. But I don't think POSIX actually guarantees
that. (Which means, for example, that a "%zd" format specifier isn't
guaranteed to be correct for value of type ssize_t.)

This assumes that size_t and ssize_t have a rank greater than or equal
to that of int, which is not guaranteed but is almost certain. If not
then both operands are promoted via the "integer promotions" before the
"usual arithmetic conversions" are applied. (This is relevant if, for
example, you want to add operands of types short and unsigned short.)

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

Keith Thompson

unread,
Oct 1, 2021, 5:29:17 PM10/1/21
to
sc...@slp53.sl.home (Scott Lurndal) writes:
> Guillaume <mes...@bottle.org> writes:
[...]
>>ssize_t is not standard C. It's defined in POSIX.
>>While it's a signed integer, whereas size_t is unsigned, I don't know
>>how it relates to size_t on a given implementation in terms of width.
>
> POSIX specifies that sizeof(ssize_t) == sizeof(size_t).

Does it? I couldn't find any such guarantee.

https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html

[...]

James Kuyper

unread,
Oct 1, 2021, 8:17:32 PM10/1/21
to
On 10/1/21 5:29 PM, Keith Thompson wrote:
> sc...@slp53.sl.home (Scott Lurndal) writes:
>> Guillaume <mes...@bottle.org> writes:
> [...]
>>> ssize_t is not standard C. It's defined in POSIX.
>>> While it's a signed integer, whereas size_t is unsigned, I don't know
>>> how it relates to size_t on a given implementation in terms of width.
>>
>> POSIX specifies that sizeof(ssize_t) == sizeof(size_t).
>
> Does it? I couldn't find any such guarantee.
>
> https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html

In <https://pubs.opengroup.org/onlinepubs/9699919799/toc.htm> it says
the following about ssize_t:

"The wording is such that an implementation may either choose to use a
longer type or simply to use the signed version of the type that
underlies size_t."

Therefore, sizeof(ssize_t) > sizeof(size_t) is allowed. I have not yet
located "the wording" that referred to above.

Keith Thompson

unread,
Oct 1, 2021, 9:42:30 PM10/1/21
to
Because of the way the site uses frames, that URL isn't meaningful. The
quote is from
https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html

I couldn't find "the wording" either.

Manfred

unread,
Oct 2, 2021, 2:17:36 PM10/2/21
to
On 10/2/2021 3:42 AM, Keith Thompson wrote:
> James Kuyper <james...@alumni.caltech.edu> writes:
>> On 10/1/21 5:29 PM, Keith Thompson wrote:
>>> sc...@slp53.sl.home (Scott Lurndal) writes:
>>>> Guillaume <mes...@bottle.org> writes:
>>> [...]
>>>>> ssize_t is not standard C. It's defined in POSIX.
>>>>> While it's a signed integer, whereas size_t is unsigned, I don't know
>>>>> how it relates to size_t on a given implementation in terms of width.
>>>>
>>>> POSIX specifies that sizeof(ssize_t) == sizeof(size_t).
>>>
>>> Does it? I couldn't find any such guarantee.
>>>
>>> https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html
>>
>> In <https://pubs.opengroup.org/onlinepubs/9699919799/toc.htm> it says
>> the following about ssize_t:
>>
>> "The wording is such that an implementation may either choose to use a
>> longer type or simply to use the signed version of the type that
>> underlies size_t."
>>
>> Therefore, sizeof(ssize_t) > sizeof(size_t) is allowed. I have not yet
>> located "the wording" that referred to above.
>
> Because of the way the site uses frames, that URL isn't meaningful. The
> quote is from
> https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html
>
> I couldn't find "the wording" either.
>

Maybe the 'wording' is "a signed analog of size_t"?

James Kuyper

unread,
Oct 2, 2021, 3:03:48 PM10/2/21
to
On 10/2/21 2:17 PM, Manfred wrote:
> On 10/2/2021 3:42 AM, Keith Thompson wrote:
>> James Kuyper <james...@alumni.caltech.edu> writes:
...
>>> In <https://pubs.opengroup.org/onlinepubs/9699919799/toc.htm> it says
>>> the following about ssize_t:
>>>
>>> "The wording is such that an implementation may either choose to use a
>>> longer type or simply to use the signed version of the type that
>>> underlies size_t."
>>>
>>> Therefore, sizeof(ssize_t) > sizeof(size_t) is allowed. I have not yet
>>> located "the wording" that referred to above.
>>
>> Because of the way the site uses frames, that URL isn't meaningful. The
>> quote is from
>> https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html
>>
>> I couldn't find "the wording" either.
>>
>
> Maybe the 'wording' is "a signed analog of size_t"?

No, that's too vague to support such a detailed conclusion.

Tim Rentsch

unread,
Oct 2, 2021, 5:48:03 PM10/2/21
to
This problem is doable, but it is not trivial. It makes a good
challenge problem for C experts.

Is it really important for you to find an answer?

Tim Rentsch

unread,
Oct 2, 2021, 5:52:54 PM10/2/21
to
Keith Thompson <Keith.S.T...@gmail.com> writes:

> wij <wyn...@gmail.com> writes:
>
>> To simply the question of "a+b":
>>
>> ssize_t add(ssize_t a, size_t b) {
>> if(a+b would overflow) { set errno=ERANGE; }
>> a+=b; // ?
>> return a;
>> }
>>
>> Another example:
>> ssize_t a=SSIZE_T_MIN;
>> size_t b=SIZE_T_MAX;
>> a+=b; // Is this OK? Or, How the addition is done correctly?
>
> In standard C, you don't, because ssize_t doesn't exist (unless you
> define it yourself).
>
> However, POSIX does define a type ssize_t, which is a signed integer
> type that can represent values up to SSIZE_MAX.
>
> The rules are given in N1570 6.3.1.8, "Usual arithmetic conversions".
>
> It's likely, but not guaranteed, that size_t and ssize_t have the same
> rank. If so, then the ssize_t value is converted to size_t, and
> assuming SSIZE_MAX <= SIZE_MAX the conversion does not change the
> value. [...]

If size_t and ssize_t have the same integer conversion rank then
SSIZE_MAX <= SIZE_MAX is guaranteed. However, converting a value
of type ssize_t to type size_t may indeed change the value,
because the value might be negative.

Tim Rentsch

unread,
Oct 2, 2021, 6:13:59 PM10/2/21
to
James Kuyper <james...@alumni.caltech.edu> writes:

> On 10/1/21 1:39 PM, Guillaume wrote:
>
>> Le 01/10/2021 at 17:32, wij a ecrit:
>>
>>> To simply the question of "a+b":
>>>
>>> ssize_t add(ssize_t a, size_t b) {
>>> if(a+b would overflow) { set errno=ERANGE; }
>>> a+=b; // ?
>>> return a;
>>> }
>>>
>>> Another example:
>>> ssize_t a=SSIZE_T_MIN;
>>> size_t b=SIZE_T_MAX;
>>> a+=b; // Is this OK? Or, How the addition is done correctly?
>>
>> ssize_t is not standard C. It's defined in POSIX.
>> While it's a signed integer, whereas size_t is unsigned, I don't know
>> how it relates to size_t on a given implementation in terms of width.
>
> ssize_t is a signed integer type with the same width as size_t.

I don't see anything on the pubs.opengroup.org website that
requires that.

> Therefore, they should have the same integer conversion rank. Since
> the sign bit is included in the width, SSIZE_MAX is guaranteed to be
> smaller than SIZE_MAX.
>
> The usual arithmetic conversions apply (6.5.6p5). The ssize_t value
> is first converted to size_t (6.3.1.8p1). That is a well-defined
> conversion - negative ssize_t values have SIZE_MAX+1 added to them
> (6.3.1.3p2). The sum is calculated using size_t math, which will
> produce what I assume is the desired result even if a is negative,
> so long as it is smaller than b.
>
> Upon assignment, the result is converted to ssize_t. Values greater
> than SSIZE_MAX would not result in undefined behavior, as you
> suggested. Instead, they would result in an implementation-defined
> value or the raising of an implementation-defined signal (6.3.1.3p3).
> This code will probably not work as desired on an implementation that
> chooses to raise a signal, and the implementation- defined value is
> not guaranteed to be the one that he wants. Therefore, setting errno
> would not be sufficient, the final conversion must be actively
> prevented from occurring if it would otherwise overflow.
>
> I would write the function as:
>
> size_t c = a + b;
> if(c > SSIZE_MAX)
> return overflow_value;
> else
> return c;
>
> where overflow_value is whatever value he wants add() to return
> when there's an overflow. [...]

An exemplary proposal: short, simple, and wrong.

Tim Rentsch

unread,
Oct 6, 2021, 7:06:58 AM10/6/21
to
For those who may be interested, here is a function to compute
the sum when the result is within the range of ssize_t, and
give an error indication when it isn't.


ssize_t
add_ssize_and_size( ssize_t const a, size_t const b ){
if( a >= 0 && SSIZE_MAX - a >= b ) return a + b;
if( a < 0 && b <= SSIZE_MAX ) return a + (ssize_t){ b };
if( a < 0 && b-1-SSIZE_MAX <= -(a+1) ) return b-1 - -(a+1);

return errno = ERANGE, SSIZE_MAX; // or some other suitable value
}
0 new messages