Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #162907 > unrolled thread

How to add ssize_t a by size_t b?

Started bywij <wyniijj@gmail.com>
First post2021-10-01 08:32 -0700
Last post2021-10-06 04:06 -0700
Articles 14 — 7 participants

Back to article view | Back to comp.lang.c


Contents

  How to add ssize_t a by size_t b? wij <wyniijj@gmail.com> - 2021-10-01 08:32 -0700
    Re: How to add ssize_t a by size_t b? Guillaume <message@bottle.org> - 2021-10-01 19:39 +0200
      Re: How to add ssize_t a by size_t b? scott@slp53.sl.home (Scott Lurndal) - 2021-10-01 18:31 +0000
        Re: How to add ssize_t a by size_t b? Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-10-01 14:29 -0700
          Re: How to add ssize_t a by size_t b? James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-10-01 20:17 -0400
            Re: How to add ssize_t a by size_t b? Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-10-01 18:42 -0700
              Re: How to add ssize_t a by size_t b? Manfred <noname@add.invalid> - 2021-10-02 20:17 +0200
                Re: How to add ssize_t a by size_t b? James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-10-02 15:03 -0400
      Re: How to add ssize_t a by size_t b? James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-10-01 14:53 -0400
        Re: How to add ssize_t a by size_t b? Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-10-02 15:13 -0700
    Re: How to add ssize_t a by size_t b? Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-10-01 14:13 -0700
      Re: How to add ssize_t a by size_t b? Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-10-02 14:52 -0700
    Re: How to add ssize_t a by size_t b? Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-10-02 14:47 -0700
    Re: How to add ssize_t a by size_t b? Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-10-06 04:06 -0700

#162907 — How to add ssize_t a by size_t b?

Fromwij <wyniijj@gmail.com>
Date2021-10-01 08:32 -0700
SubjectHow to add ssize_t a by size_t b?
Message-ID<c64a2f9b-4745-42f4-9bd4-ce093fbc2d68n@googlegroups.com>
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?

[toc] | [next] | [standalone]


#162912

FromGuillaume <message@bottle.org>
Date2021-10-01 19:39 +0200
Message-ID<sj7h4g$19gs$1@gioia.aioe.org>
In reply to#162907
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. 
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".

[toc] | [prev] | [next] | [standalone]


#162914

Fromscott@slp53.sl.home (Scott Lurndal)
Date2021-10-01 18:31 +0000
Message-ID<JtI5J.65761$ol1.6157@fx42.iad>
In reply to#162912
Guillaume <message@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.

[toc] | [prev] | [next] | [standalone]


#162921

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-10-01 14:29 -0700
Message-ID<87pmsoh1st.fsf@nosuchdomain.example.com>
In reply to#162914
scott@slp53.sl.home (Scott Lurndal) writes:
> Guillaume <message@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

[...]

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

[toc] | [prev] | [next] | [standalone]


#162927

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-10-01 20:17 -0400
Message-ID<sj88eh$aub$1@dont-email.me>
In reply to#162921
On 10/1/21 5:29 PM, Keith Thompson wrote:
> scott@slp53.sl.home (Scott Lurndal) writes:
>> Guillaume <message@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.

[toc] | [prev] | [next] | [standalone]


#162929

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-10-01 18:42 -0700
Message-ID<87lf3cgq2u.fsf@nosuchdomain.example.com>
In reply to#162927
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
> On 10/1/21 5:29 PM, Keith Thompson wrote:
>> scott@slp53.sl.home (Scott Lurndal) writes:
>>> Guillaume <message@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.

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

[toc] | [prev] | [next] | [standalone]


#162943

FromManfred <noname@add.invalid>
Date2021-10-02 20:17 +0200
Message-ID<sja7ni$1445$1@gioia.aioe.org>
In reply to#162929
On 10/2/2021 3:42 AM, Keith Thompson wrote:
> James Kuyper <jameskuyper@alumni.caltech.edu> writes:
>> On 10/1/21 5:29 PM, Keith Thompson wrote:
>>> scott@slp53.sl.home (Scott Lurndal) writes:
>>>> Guillaume <message@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"?

[toc] | [prev] | [next] | [standalone]


#162944

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-10-02 15:03 -0400
Message-ID<sjaaea$8oh$1@dont-email.me>
In reply to#162943
On 10/2/21 2:17 PM, Manfred wrote:
> On 10/2/2021 3:42 AM, Keith Thompson wrote:
>> James Kuyper <jameskuyper@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.

[toc] | [prev] | [next] | [standalone]


#162915

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-10-01 14:53 -0400
Message-ID<sj7ler$sk8$1@dont-email.me>
In reply to#162912
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.

[toc] | [prev] | [next] | [standalone]


#162948

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-10-02 15:13 -0700
Message-ID<864k9zrs6i.fsf@linuxsc.com>
In reply to#162915
James Kuyper <jameskuyper@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.

[toc] | [prev] | [next] | [standalone]


#162919

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-10-01 14:13 -0700
Message-ID<87y27ch2id.fsf@nosuchdomain.example.com>
In reply to#162907
wij <wyniijj@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.  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.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#162946

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-10-02 14:52 -0700
Message-ID<868rzbrt5f.fsf@linuxsc.com>
In reply to#162919
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

> wij <wyniijj@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.

[toc] | [prev] | [next] | [standalone]


#162945

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-10-02 14:47 -0700
Message-ID<86czonrtdl.fsf@linuxsc.com>
In reply to#162907
wij <wyniijj@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?

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?

[toc] | [prev] | [next] | [standalone]


#163016

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-10-06 04:06 -0700
Message-ID<86mtnmquno.fsf@linuxsc.com>
In reply to#162907
wij <wyniijj@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?

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
}

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web