Path: csiph.com!eternal-september.org!feeder.eternal-september.org!reader01.eternal-september.org!.POSTED!not-for-mail
From: Tim Rentsch
Newsgroups: comp.lang.c
Subject: Re: for-loop mutates to an endless-loop
Date: Sat, 11 Jan 2020 10:01:48 -0800
Organization: A noiseless patient Spider
Lines: 98
Message-ID: <86pnfp27oz.fsf@linuxsc.com>
References: <87ftgwpqbi.fsf@bsb.me.uk>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Injection-Info: reader02.eternal-september.org; posting-host="a658bb27d1b88b897660621da5fe7a9e"; logging-data="30932"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/TZ2Q7mLaOyAZDg82eOJOAl0nenVF3qBM="
User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux)
Cancel-Lock: sha1:kfRYZ7fSA2kDNPBbgXF+3XxxGGM= sha1:nT/0IfY4U4/u1pZJriQlQtHxeTI=
Xref: csiph.com comp.lang.c:148939
Ben Bacarisse writes:
> "Walter H." writes:
>
>
>> then I tried the same in C with several compilers
>> 32-bit, 64-bit Intel, GNU under Linux or Visual C++ under Windows
>> even QuickC or Turbo C every one built this programme with an endless-loop;
>>
>> [code]
>> #include
>> int main( int argc, char* argv[ ] )
>> {
>> short int nIter;
>
> This may or may not by a 16-bit integer. It probably will be, but
> modern C has the type int16_t that you can use to be sure. #include
> to get these new known-width types.
>
>> long lSum = 0;
>> for ( nIter = -32768; nIter <= 32767; nIter++ )
>
> When you need numbers like this it better to #include and use
> SHRT_MIN and SHRT_MAX. If you included stdint.h to use int16_t then
> INT16_MIN and INT16_MAX are available.
>
> Also, in modern C you can declare the loop variable in the for
> statement:
>
> for (short nIter = -32768; nIter <= 32767; nIter++) ...
>
>> lSum += nIter;
>> printf( "sum: %ld\n", lSum );
>> return 0;
>> }
>> [/code]
>
> Adding a bit to the answer you already have...
>
> If nInter is indeed a 16-bit integer type, and int is wider (the most
> likely situation here) then the behaviour of the code above is
> implementation-defined.
>
> Once nIter gets to 32767, the increment in the for loop causes a problem
> when the result (which will be of type int) is converted back to short.
> The result is either an implementation-defined value or an
> implementation-defined signal will be raised.
>
> Anyway, unless a signal is raised, the result is bound to be <= 32767
> so, as has been pointed out, the test is never false.
>
>> here the same in old FORTRAN which also worked as expected;
>>
>> [code]
>> PROGRAM SumDemo
>> INTEGER*2 nIter
>> INTEGER*4 lSum
>> lSum = 0
>> DO nIter = -32768,32767,1
>> lSum = lSum + nIter
>> END DO
>> WRITE ( *, * ) 'sum: ', lSum
>> END
>> [/code]
>
> Curiously, the GNU Fortran compiler gets this wrong (at least as far as
> my understanding of F77 goes) and generates an endless loop.
>
>
>
>> what would be the correct way of a for-loop
>> with the complete range of an integral type?
>
> One solution is to use a wider type:
>
> for (int_fast32_t i = INT16_MIN; i <= INT16_MAX; i++) ...
>
> but that is not always possible. The general solution is to test at the
> end, after the increment:
>
> short i = SHRT_MIN;
> do sum += i; while (i++ < SHRT_MAX);
>
> but this is not portable to implementations where (a) short is as wide
> as int (in which case the increment is undefined behaviour), or (b)
> implementations that raise a signal when converting an out-of-range
> value to short.
>
> A portable solution must increment only when i < SHRT_MAX:
>
> do sum += i; while (i < SHRT_MAX && i++ | 1);
Another way to achieve the same effect:
do sum += i; while (i < SHRT_MAX && i++ < SHRT_MAX);
(It seems likely that optimization will omit the redundant
comparison.)