User avatar
jahboater
Posts: 8829
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: Countdown using unsigned in C

Tue Sep 26, 2023 4:48 am

ejolson wrote:
Mon Sep 25, 2023 8:33 pm
it seems to work.

Even so, I'm suspicious since the dog developer is always exhibiting unexpected behavior.
It does work perfectly. Anything may happen with UB and that includes correct operation!

Try the static analyzer though - which goes bonkers and gives incorrect warnings ...

It thinks y points to a string literal (so cant be written to) and doesn't realize that *(y+o) or y[o] point to something else which is OK for writes.

It also assumes that since o has not been written to, the array s[64] is used uninitialized when passed to puts().

Code: Select all

gcc -Wall -Wextra -fanalyzer -O3 try.c -o try
In function 'dogcopy',
    inlined from 'main' at try.c:11:5:
try.c:5:14: warning: write to string literal [-Wanalyzer-write-to-string-literal]
    5 |     do *(y+o)=*y;
      |        ~~~~~~^~~
  'main': event 1
    |
    |   11 |     dogcopy(s,"scratch woof meow bark");
    |      |     ^
    |      |     |
    |      |     (1) inlined call to 'dogcopy' from 'main'
    |
    +--> 'dogcopy': event 2
           |
           |    5 |     do *(y+o)=*y;
           |      |        ~~~~~~^~~
           |      |              |
           |      |              (2) write to string literal here
           |
try.c: In function 'main':
try.c:12:5: warning: 's' is used uninitialized [-Wuninitialized]
   12 |     puts(s);
      |     ^~~~~~~
In file included from try.c:1:
/usr/include/stdio.h:632:12: note: by argument 1 of type 'const char *' to 'puts' declared here
  632 | extern int puts (const char *__s);
      |            ^~~~
try.c:10:10: note: 's' declared here
   10 |     char s[64];
      |          ^

dsyleixa123
Posts: 2211
Joined: Mon Jun 11, 2018 11:22 am

Re: Countdown using unsigned in C

Tue Sep 26, 2023 9:07 am

Now what is the conclusion regarding possible overflow of counting variables:
Which now is better or safer in some respect, signed or unsigned int?
hobby programming retiree, ♂, GER
"A programming language that needs left side whitespace or tabs for code block structuring and nesting is ridiculous."

swampdog
Posts: 1178
Joined: Fri Dec 04, 2015 11:22 am

Re: Countdown using unsigned in C

Tue Sep 26, 2023 9:36 am

ejolson wrote:
Mon Sep 25, 2023 8:33 pm
swampdog wrote:
Mon Sep 25, 2023 6:09 pm
My K&R C compiler really was little more than a gloried assembler so I understand where the idea originated. You really could write..

Code: Select all

void
main()
{
 "Hello"[1] = 'E';
}
..and it would generate code to change 'e' to 'E'. With enough effort..

Code: Select all

int main()
{
 ((char*)("hello"))[1] = 'E';
}
..will compile but damn these pesky compilers for putting it in a read-only section and damn pesky hardware protection (sigsegv). Not allowed any fun any more!
I think this is currently a problem on the Pico where the compiler puts string constants in flash for functions that are supposed to be fully RAM resident.

Along slightly different lines, here is Fido's string copy routine:

Code: Select all

#include <stdio.h>

void dogcopy(char *x,char *y){
    long int o=x-y;
    do *(y+o)=*y;
    while(*(y++));
}

int main(){
    char s[64];
    dogcopy(s,"scratch woof meow bark");
    puts(s);
}
As shown by

Code: Select all

$ gcc -Wall -Wextra dogcopy.c
$ ./a.out 
scratch woof meow bark
it seems to work.

Even so, I'm suspicious since the dog developer is always exhibiting unexpected behavior.
That one makes my brain hurt. I think you've won the prize! :-)

Heater
Posts: 19672
Joined: Tue Jul 17, 2012 3:02 pm

Re: Countdown using unsigned in C

Tue Sep 26, 2023 10:40 am

dsyleixa123 wrote:
Tue Sep 26, 2023 9:07 am
Now what is the conclusion regarding possible overflow of counting variables:
Which now is better or safer in some respect, signed or unsigned int?
My advice is "Don't do it". Any kind of arithmetic in C is fraught with unfathomable dangers.

If you really have to count anything in C then:

1) Use the biggest types you can find, uint64_t, int64_t so as to have less chance of running out of headroom. Unless you have huge amounts of data and need to use smaller types.

2) If you are counting up and down in steps of one remember to check for some limit to avoid overflow or underflow. INT_MAX or whatever.

3) If you are counting in bigger steps ensure your additions do not over flow. Either use regular C as shown here: "INT32-C. Ensure that operations on signed integers do not result in overflow": https://cwe.mitre.org/data/definitions/738.html Or use the new "cod..." functions in C23 to perform checked arithmetic.

4) Read the entirety of "SEI CERT C Coding Standard" document here: https://wiki.sei.cmu.edu/confluence/dis ... g+Standard and the same for C++ if you have the misfortune too use that language: https://wiki.sei.cmu.edu/confluence/pag ... d=88046682.

5) As for signed vs unsigned that depends on if you qualities can actually be negative or not.

Or take the easy way out, switch to a safer language, like Rust.
Slava Ukrayini.

dsyleixa123
Posts: 2211
Joined: Mon Jun 11, 2018 11:22 am

Re: Countdown using unsigned in C

Tue Sep 26, 2023 11:04 am

just as for signed vs unsigned, i.e.
ad 5) for all cases, generally, when sth unexpected might happen faultily (overflow, underflow, <0, ...)

Nonetheless, under no circumstances should it come to UB, e.g. for subsequent sanity checks or for reproducable aborts.
So which one to choose?
hobby programming retiree, ♂, GER
"A programming language that needs left side whitespace or tabs for code block structuring and nesting is ridiculous."

User avatar
jahboater
Posts: 8829
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: Countdown using unsigned in C

Tue Sep 26, 2023 11:59 am

dsyleixa123 wrote:
Tue Sep 26, 2023 11:04 am
Nonetheless, under no circumstances should it come to UB, e.g. for subsequent sanity checks or for reproducable aborts.
So which one to choose?
Which is best for your algorithm ?

Although unsigned has no UB, the results of UINT_MAX + 1 or 0U - 1 are quite dramatic.
If you understand whats going on you can exploit it safely.

That's not true of signed arithmetic, where the advice for C is simply to avoid overflow at all costs (also avoid abs(INT_MIN), -INT_MIN, and especially INT_MIN / -1 which on x86 raises a floating-point exception!).
To repeat heaters suggestions above, use checked arithmetic, and/or use large sized integers, and always use -ftrapv to catch anything you have missed.

dsyleixa123
Posts: 2211
Joined: Mon Jun 11, 2018 11:22 am

Re: Countdown using unsigned in C

Tue Sep 26, 2023 1:13 pm

jahboater wrote:
Tue Sep 26, 2023 11:59 am
Although unsigned has no UB, the results of UINT_MAX + 1 or 0U - 1 are quite dramatic.
If you understand whats going on you can exploit it safely.
checked arithmetic is too weird and cumbersome.
So I'll take appropriate big-sized unsigned ints in doubt.
Last edited by dsyleixa123 on Tue Sep 26, 2023 1:23 pm, edited 2 times in total.
hobby programming retiree, ♂, GER
"A programming language that needs left side whitespace or tabs for code block structuring and nesting is ridiculous."

User avatar
jahboater
Posts: 8829
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: Countdown using unsigned in C

Tue Sep 26, 2023 1:19 pm

Heater wrote:
Tue Sep 26, 2023 10:40 am
Or take the easy way out, switch to a safer language, like Rust.
By the looks of it RFC560 says that in debug mode SO panics, and in release mode it silently does a twos-complement wrap.

There is a method called i32::wrapping_add() which does the wrap without failing in debug mode.
wrapping_add() is a bit like ckd_add() in C but doesnt report if overflow happened which is disappointing.

Because its stated that twos-complement wrap happens there are no issues with compiler optimizations.

Because Rust is new, it has never had to support all the various representations for signed integers.

User avatar
jahboater
Posts: 8829
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: Countdown using unsigned in C

Tue Sep 26, 2023 1:25 pm

dsyleixa123 wrote:
Tue Sep 26, 2023 1:13 pm
checked arithmetic is too weird and cumbersome, but for results of INT_MAX+1 or INT_MIN-1 or <0 I can check before it becomes dramatic.
Unsigned wrap is easy to check beforehand:-

To do "a + b"

if( a > UINT_MAX - b ) wrap will happen

but the ckd_add() will be more efficient.

if( ckd_add(&a, a,b) ) wrap happened (and the wrapped value goes in "a")

Its much harder for signed arithmetic because you have to deal with the four combinations of signs, so ckd_add is very very useful.
Last edited by jahboater on Tue Sep 26, 2023 1:53 pm, edited 1 time in total.

Heater
Posts: 19672
Joined: Tue Jul 17, 2012 3:02 pm

Re: Countdown using unsigned in C

Tue Sep 26, 2023 1:52 pm

In reply to someone's now deleted question about Rust volatile and overflow let me present:

Code: Select all

    let mut n: i64 = i64::MAX;
    let n_ptr = &mut n as *mut i64;

    unsafe {
        write_volatile(n_ptr, read_volatile(n_ptr) + 1);
    }
    println!("{n}");
Which produces -9223372036854775808 in release builds but panics in debug builds. (It is possible to enable panics in debug builds as well)

We are using "unsafe" here because we are dealing with a raw pointer. Typically a pointer to some I/O address. Anything I/O is necessarily outside what Rust can do safety checks on, hence "unsafe".

Rust does not have the equivalent of a volatile variable. Rather we make volatile access to some address through a pointer. I don't really see the use of volatile variables (as opposed to pointers) in C
Last edited by Heater on Wed Sep 27, 2023 8:29 am, edited 1 time in total.
Slava Ukrayini.

User avatar
jahboater
Posts: 8829
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: Countdown using unsigned in C

Tue Sep 26, 2023 2:03 pm

Heater wrote:
Tue Sep 26, 2023 1:52 pm
In reply to someone's now deleted question about Rust volatile and overflow let me present:
Sorry, I realized I could read up on the subject, and so deleted the post to avoid wasting your time!

Without the volatile, or some mechanism for ensuring the values are unknown to the compiler, it would simply remove all the arithmetic and return the result.
Heater wrote:
Tue Sep 26, 2023 1:52 pm

Code: Select all

    let mut n: i64 = i64::MAX;
    let n_ptr = &mut n as *mut i64;

    unsafe {
        write_volatile(n_ptr, read_volatile(n_ptr) + 1);
    }
    println!("{n}");
Which produces -9223372036854775808 in debug builds but panics in release builds. (It is possible to enable panics in debug builds as well)
I guess "checking" is something different from debug mode ?
I mean, it sounds like the opposite of RFC560 which says:
The operations +, -, *, can underflow and overflow. When checking is enabled this will panic. When checking is disabled this will two's complement wrap.
Last edited by jahboater on Tue Sep 26, 2023 2:05 pm, edited 2 times in total.

dsyleixa123
Posts: 2211
Joined: Mon Jun 11, 2018 11:22 am

Re: Countdown using unsigned in C

Tue Sep 26, 2023 2:04 pm

jahboater wrote:
Tue Sep 26, 2023 1:25 pm
Unsigned wrap is easy to check beforehand:-

To do "a + b"
if( a > UINT_MAX - b ) wrap will happen
but the ckd_add() will be more efficient.
if( ckd_add(&a, a,b) ) wrap happened (and the wrapped value goes in "a")
Its much harder for signed arithmetic because you have to deal with the four combinations of signs, so ckd_add is very very useful.
You're absolutely right, and there was also a subsequent edit because I had previously misread it
hobby programming retiree, ♂, GER
"A programming language that needs left side whitespace or tabs for code block structuring and nesting is ridiculous."

ejolson
Posts: 11711
Joined: Tue Mar 18, 2014 11:47 am

Re: Countdown using unsigned in C

Tue Sep 26, 2023 2:10 pm

Heater wrote:
Tue Sep 26, 2023 10:40 am
dsyleixa123 wrote:
Tue Sep 26, 2023 9:07 am
Now what is the conclusion regarding possible overflow of counting variables:
Which now is better or safer in some respect, signed or unsigned int?
My advice is "Don't do it". Any kind of arithmetic in C is fraught with unfathomable dangers.
My impression is the option -fwrapv prevents the optimisation of signed integer arithmetic from removing sanity checks and simplifying expressions based on the idea signed overflow is impossible. With this option GCC behaves like compilers for other languages.

Note that -fwrapv doesn't make overflow go away, it only makes the outcome part of the computational model so it's possible to deal with in your program in case it happens.

ejolson
Posts: 11711
Joined: Tue Mar 18, 2014 11:47 am

Re: Countdown using unsigned in C

Tue Sep 26, 2023 7:24 pm

jahboater wrote:
Tue Sep 26, 2023 4:48 am
ejolson wrote:
Mon Sep 25, 2023 8:33 pm
it seems to work.

Even so, I'm suspicious since the dog developer is always exhibiting unexpected behavior.
It does work perfectly. Anything may happen with UB and that includes correct operation!

Try the static analyzer though - which goes bonkers and gives incorrect warnings ...

It thinks y points to a string literal (so cant be written to) and doesn't realize that *(y+o) or y[o] point to something else which is OK for writes.

It also assumes that since o has not been written to, the array s[64] is used uninitialized when passed to puts().

Code: Select all

gcc -Wall -Wextra -fanalyzer -O3 try.c -o try
In function 'dogcopy',
    inlined from 'main' at try.c:11:5:
try.c:5:14: warning: write to string literal [-Wanalyzer-write-to-string-literal]
    5 |     do *(y+o)=*y;
      |        ~~~~~~^~~
  'main': event 1
    |
    |   11 |     dogcopy(s,"scratch woof meow bark");
    |      |     ^
    |      |     |
    |      |     (1) inlined call to 'dogcopy' from 'main'
    |
    +--> 'dogcopy': event 2
           |
           |    5 |     do *(y+o)=*y;
           |      |        ~~~~~~^~~
           |      |              |
           |      |              (2) write to string literal here
           |
try.c: In function 'main':
try.c:12:5: warning: 's' is used uninitialized [-Wuninitialized]
   12 |     puts(s);
      |     ^~~~~~~
In file included from try.c:1:
/usr/include/stdio.h:632:12: note: by argument 1 of type 'const char *' to 'puts' declared here
  632 | extern int puts (const char *__s);
      |            ^~~~
try.c:10:10: note: 's' declared here
   10 |     char s[64];
      |          ^
I showed the static analyzer output to the dog developer who spent the rest of the day barking about how domestication of the cat in ancient Egypt led to adversarial policies in supply chain management and the current shortages. At this I asked if it would have been better to befriend the mice and rats instead.

There was silence followed just in time by some unexpected behavior.

The revised code now looks like

Code: Select all

#include <stdio.h>

void catcopy(char *x,char *y){
    long int o=y-x;
    do *x=*(x+o);
    while(*(x++));
}

int main(){
    char s[64]={0};
    catcopy(s,"scratch woof meow bark");
    puts(s);
}
and runs as

Code: Select all

$ gcc -Wall -Wextra -fanalyzer -O3 catcopy.c
$ ./a.out
scratch woof meow bark
I'm still suspicious.

User avatar
Paeryn
Posts: 3587
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Countdown using unsigned in C

Tue Sep 26, 2023 9:42 pm

ejolson wrote:
Tue Sep 26, 2023 7:24 pm
jahboater wrote:
Tue Sep 26, 2023 4:48 am
ejolson wrote:
Mon Sep 25, 2023 8:33 pm
it seems to work.

Even so, I'm suspicious since the dog developer is always exhibiting unexpected behavior.
It does work perfectly. Anything may happen with UB and that includes correct operation!

Try the static analyzer though - which goes bonkers and gives incorrect warnings ...

It thinks y points to a string literal (so cant be written to) and doesn't realize that *(y+o) or y[o] point to something else which is OK for writes.

It also assumes that since o has not been written to, the array s[64] is used uninitialized when passed to puts().

Code: Select all

gcc -Wall -Wextra -fanalyzer -O3 try.c -o try
In function 'dogcopy',
    inlined from 'main' at try.c:11:5:
try.c:5:14: warning: write to string literal [-Wanalyzer-write-to-string-literal]
    5 |     do *(y+o)=*y;
      |        ~~~~~~^~~
  'main': event 1
    |
    |   11 |     dogcopy(s,"scratch woof meow bark");
    |      |     ^
    |      |     |
    |      |     (1) inlined call to 'dogcopy' from 'main'
    |
    +--> 'dogcopy': event 2
           |
           |    5 |     do *(y+o)=*y;
           |      |        ~~~~~~^~~
           |      |              |
           |      |              (2) write to string literal here
           |
try.c: In function 'main':
try.c:12:5: warning: 's' is used uninitialized [-Wuninitialized]
   12 |     puts(s);
      |     ^~~~~~~
In file included from try.c:1:
/usr/include/stdio.h:632:12: note: by argument 1 of type 'const char *' to 'puts' declared here
  632 | extern int puts (const char *__s);
      |            ^~~~
try.c:10:10: note: 's' declared here
   10 |     char s[64];
      |          ^
I showed the static analyzer output to the dog developer who spent the rest of the day barking about how domestication of the cat in ancient Egypt led to adversarial policies in supply chain management and the current shortages. At this I asked if it would have been better to befriend the mice and rats instead.

There was silence followed just in time by some unexpected behavior.

The revised code now looks like

Code: Select all

#include <stdio.h>

void catcopy(char *x,char *y){
    long int o=y-x;
    do *x=*(x+o);
    while(*(x++));
}

int main(){
    char s[64]={0};
    catcopy(s,"scratch woof meow bark");
    puts(s);
}
and runs as

Code: Select all

$ gcc -Wall -Wextra -fanalyzer -O3 catcopy.c
$ ./a.out
scratch woof meow bark
I'm still suspicious.
Kira miaowed loudly when he saw this code and swiftly swiped my phone onto the floor. After five minutes of pouncing on and kicking the phone around the room I found he'd actually made a correction.

Code: Select all

#include <stddef.h>
#include <stdio.h>

void catcopy(char *x,char *y){
    ptrdiff_t o=y-x;
    do *x=*(x+o);
    while(*(x++));
}

int main(){
    char s[64]={0};
    catcopy(s,"scratch woof meow bark");
    puts(s);
}
Though he was still hissing that really you're not meant to subtract two pointers unless they both point into either the same object or objects in the same array.
She who travels light — forgot something.

Heater
Posts: 19672
Joined: Tue Jul 17, 2012 3:02 pm

Re: Countdown using unsigned in C

Wed Sep 27, 2023 8:35 am

jahboater wrote:
Tue Sep 26, 2023 2:03 pm
Without the volatile, or some mechanism for ensuring the values are unknown to the compiler, it would simply remove all the arithmetic and return the result.
Yes but what is wrong with that? There is no observable difference to your program behaviour.
jahboater wrote:
Tue Sep 26, 2023 2:03 pm
I mean, it sounds like the opposite of RFC560 which says:...
Err...yeah. Because I goofed. I should have said: "Which produces -9223372036854775808 in release builds but panics in debug builds. (It is possible to enable panics in release builds as well)"
Slava Ukrayini.

User avatar
jahboater
Posts: 8829
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: Countdown using unsigned in C

Wed Sep 27, 2023 10:54 am

Heater wrote:
Wed Sep 27, 2023 8:35 am
jahboater wrote:
Tue Sep 26, 2023 2:03 pm
Without the volatile, or some mechanism for ensuring the values are unknown to the compiler, it would simply remove all the arithmetic and return the result.
Yes but what is wrong with that? There is no observable difference to your program behaviour.
That's fine, I'm glad compilers do that, and see the new "constexpr" feature in C.
But there are differences. Floating-point arithimetic is done to arbitrary precision (with MPFR) and exceptions don't get raised (for example 2.0/3.0 does not raise FE_INEXACT which is a shame). Integer arithmetic doesn't set flags but can overflow I think.

In these small test programs I usually use "argc" or something else the compiler cannot predict, otherwise they often just get reduced
to "return 1" or similar, that tells you nothing about how the arithmetic was done.
Heater wrote:
Wed Sep 27, 2023 8:35 am
"Which produces -9223372036854775808 in release builds but panics in debug builds. (It is possible to enable panics in release builds as well)"
That's the way it should be IMHO. And they have the wrapped_add() things when overflow is intentional.

Its roughly what C would have done if they had simply stated what happens in the real world, like they do for unsigned.
I think the WG might have been pressured by the compiler devs, who love the optimizations....

C gets by though, what with ckd_add() etc, -ftrapv, -fwrapv, -Wstrict-overflow it is possible to write overflow-secure programs.

User avatar
jahboater
Posts: 8829
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: Countdown using unsigned in C

Wed Sep 27, 2023 11:19 am

Paeryn wrote:
Tue Sep 26, 2023 9:42 pm
Kira miaowed loudly when he saw this code and swiftly swiped my phone onto the floor. After five minutes of pouncing on and kicking the phone around the room I found he'd actually made a correction.
With the copy that way round you can use the full function signature for strcpy().
A few const's added for luck.

Code: Select all

void catcopy(char * restrict x, char const * const restrict y) {
    const ptrdiff_t o=y-x;
    do
      *x = x[o];
    while(*(x++));
}

dsyleixa123
Posts: 2211
Joined: Mon Jun 11, 2018 11:22 am

Re: Countdown using unsigned in C

Wed Sep 27, 2023 8:09 pm

Bets are still being accepted as to whether 50 pages will be reached!
Odds currently 1:4.
hobby programming retiree, ♂, GER
"A programming language that needs left side whitespace or tabs for code block structuring and nesting is ridiculous."

dsyleixa123
Posts: 2211
Joined: Mon Jun 11, 2018 11:22 am

Re: Countdown using unsigned in C

Sat Oct 21, 2023 7:32 am

heading the 50s...:

as to signed int overflow I tested this:

Code: Select all

#include <limits.h>
#include <stdio.h>
#include <stdint.h>

int main() {

   int16_t s = SHRT_MAX;

   printf("SHRT_MAX = %d   \n", s);

   s++;
   if(s<0) printf("overflow! ");
   printf("SHRT_MAX+1=%d   \n", s);

   s--;
   if(s>0) printf("overflow! ");
   printf("SHRT_MAX+1-1=%d   \n", s);

   return 0;
}
output:

Code: Select all

SHRT_MAX = 32767
overflow! SHRT_MAX+1=-32768
overflow! SHRT_MAX+1-1=32767
that is how I expected it for 2's complements, so is this now defined behaviour or is it a UB by a coincidentally correct result?
hobby programming retiree, ♂, GER
"A programming language that needs left side whitespace or tabs for code block structuring and nesting is ridiculous."

User avatar
jahboater
Posts: 8829
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: Countdown using unsigned in C

Sat Oct 21, 2023 8:36 am

Sounds like its UB that just happens to work.

I tried detecting this.
-ftrapv misses it, probably because its done at compile time.

You can check for overflow and at the same time get a properly "defined" result (no UB):

Code: Select all

#include <stdio.h>
#include <limits.h>
#include <inttypes.h>

#ifdef __STDC_VERSION_STDCKDINT_H__
// These new C23 versions support most integer types.
#define add(a,b) ckd_add(&a,a,b)
#define sub(a,b) ckd_sub(&a,a,b)
#define mul(a,b) ckd_mul(&a,a,b)
#else
// Sadly the gcc builtin's don't support anything smaller than an int.  
#define add(a,b) __builtin_add_overflow(a,b,&a)
#define sub(a,b) __builtin_sub_overflow(a,b,&a)
#define mul(a,b) __builtin_mul_overflow(a,b,&a)
#endif

int main( void ) {
   int16_t s = SHRT_MAX;
   printf("SHRT_MAX = %"PRId16"\n", s);

   if( add(s, 1) )
     printf("overflow! ");
   printf("SHRT_MAX+1 = %"PRId16"\n", s);

   if( sub(s, 1) )
     printf("overflow! ");
   printf("SHRT_MAX+1-1 = %"PRId16"\n", s);
}
so, until GCC supports stdckdint.h, you have to check by hand for 16-bit numbers:

Code: Select all

#include <stdio.h>
#include <limits.h>
#include <inttypes.h>

int main( void ) {
   int16_t s = SHRT_MAX;
   printf("SHRT_MAX = %"PRId16"\n", s);

   if( s > SHRT_MAX - 1 )
     printf("overflow! ");
   ++s;
   printf("SHRT_MAX+1 = %"PRId16"\n", s);

   if( s < SHRT_MIN + 1 )
     printf("overflow! ");
   --s;
   printf("SHRT_MAX+1-1 = %"PRId16"\n", s);
}

dsyleixa123
Posts: 2211
Joined: Mon Jun 11, 2018 11:22 am

Re: Countdown using unsigned in C

Sat Oct 21, 2023 9:03 am

no idea IIUYC, it doesn't seem to be an int16_t issue, for int it's the same...:

Code: Select all

SHRT_MAX = 32767
overflow! SHRT_MAX+1=-32768
overflow! SHRT_MAX+1-1=32767

INT_MAX = 2147483647
overflow! INT_MAX+1=-2147483648
overflow! INT_MAX+1-1=2147483647
of course I never use ckd_add in my programs, I simply add or subtract or multiply.
It finally works, but no idea if UB or not or coincidentally correct though...
hobby programming retiree, ♂, GER
"A programming language that needs left side whitespace or tabs for code block structuring and nesting is ridiculous."

Heater
Posts: 19672
Joined: Tue Jul 17, 2012 3:02 pm

Re: Countdown using unsigned in C

Sat Oct 21, 2023 9:21 am

dsyleixa123 wrote:
Sat Oct 21, 2023 7:32 am
that is how I expected it for 2's complements, so is this now defined behaviour or is it a UB by a coincidentally correct result?
The C standard says that overflowing signed integers results in undefined behaviour. I'm sure this has been pointed out in this thread already.

However, as far as I can make out the undefined behaviour need not show itself as a result that is not the expected 2's comp. wrap around. The problem is that the behaviour of your whole program becomes undefined after such an operation.

I suggest this because the optimisers in the compiler are allowed to assume that your program will not provoke undefined behaviour and therefor they can make optimisations that they could not make if the behaviour was defined. Which might show up as a failure somewhere else other than the operation on the int.

If you see what I mean.
Slava Ukrayini.

User avatar
Paeryn
Posts: 3587
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Countdown using unsigned in C

Sat Oct 21, 2023 9:23 am

dsyleixa123 wrote:
Sat Oct 21, 2023 7:32 am
heading the 50s...:

as to signed int overflow I tested this:

Code: Select all

#include <limits.h>
#include <stdio.h>
#include <stdint.h>

int main() {

   int16_t s = SHRT_MAX;

   printf("SHRT_MAX = %d   \n", s);

   s++;
   if(s<0) printf("overflow! ");
   printf("SHRT_MAX+1=%d   \n", s);

   s--;
   if(s>0) printf("overflow! ");
   printf("SHRT_MAX+1-1=%d   \n", s);

   return 0;
}
output:

Code: Select all

SHRT_MAX = 32767
overflow! SHRT_MAX+1=-32768
overflow! SHRT_MAX+1-1=32767
that is how I expected it for 2's complements, so is this now defined behaviour or is it a UB by a coincidentally correct result?
Signed integers are now / will be defined to be represented by 2's complement but overflow or underflow is still considered undefined behaviour (primarily because it is mathematically incorrect).
She who travels light — forgot something.

dsyleixa123
Posts: 2211
Joined: Mon Jun 11, 2018 11:22 am

Re: Countdown using unsigned in C

Sat Oct 21, 2023 9:23 am

Heater wrote:
Sat Oct 21, 2023 9:21 am
If you see what I mean.
actually, tbh:
no. ;)
hobby programming retiree, ♂, GER
"A programming language that needs left side whitespace or tabs for code block structuring and nesting is ridiculous."

Return to “C/C++”