Implicit assumptions (was: Re: Some fun with -O2)

Walter von Entferndt walter.von.entferndt at posteo.net
Mon Jan 18 11:47:15 UTC 2021


Now to test the above solution (and out of curiosity), I tried it with 
*time_t* changed to *signed char* in /guess_time_t_max()/ and it gave:

DEBUG: prec(signed_char)        =  8
schar_t_max     = 0xFFFFFFFFFFFFFFFF
sizeof(schar_t) =   1 byte
precision       =  63 bit
padding         = -56 bit
bits per byte   =   8 bit
Exitcode 1

which is from the check I added at the start of /main()/ (0xFF = -1, anyway it 
would fall into an endless loop because the delta gets =0 then), while with 
*signed int* instead of *time_t* it gives:

DEBUG: prec(signed int) = 32
xxx_t_max       = 0x7FFFFFFF
sizeof(xxx_t)   =   4 byte
precision       =  63 bit
padding         = -32 bit
bits per byte   =   8 bit

and the program runs ok (I have set /printexitvalue/ in my tcsh).  It does not 
concern me that the program fails if *sizeof(time_t)* = 1, but I didn't expect 
these other curiosities.  Now I went back to a simple switch-on-storage-width,
that I believe will cover most (all?) cases except when *time_t* is a floating 
point.  Rationale: the standard says *time_t* is either a floating point or 
integer type, and from that I conclude it's equivalent to one of the standard 
types.  I tested with time_t faked beeing 1 (char), 2 (short), 4 (int) and 8 
(standard on my 64-bit Intel).  Of course with a 1-byte *time_t* it falls into 
the endless loop as expected.

At Montag, 18. Januar 2021, 00:31:46 CET, Mark Millard wrote:
> On 2021-Jan-17, at 13:25, Walter von Entferndt 
<walter.von.entferndt at posteo.net> wrote:
> > . . .
> > -      for (j = 1; 0 < j; j *= 2)
> > +      /* NOTE This does not reach very large values > time_t_max/2
> > +         neither the (susceptible for overflow) version
> > +         for (j = 1; 0 < j; j *= 2) does  */
> > +      for (i = j = 1; i < PRECISION(INT_MAX); i++, j *= 2)
> 
> I'm unclear on why INT_MAX here.
>
Because i and j are *int* s.  My intent was to avoid sign overflow, which can 
happen silently (j *= 2), but the standard says it's undefined, and that was 
the starting point of this thread.  The genuine version relies on silent 
overflow, which is sub-optimal.  The above version is (intentionally) not 
equivalent to the genuine one.  Instead this would do what the author intended 
(from my understanding), but without any chance for overflow:

      for (i = 0; i < PRECISION (INT_MAX); i++)
        {
          j = (int) 1 << i;
	  bigtime_test (j);
	}
      bigtime_test (INT_MAX);
    }

The point is that on sign overflow j switches to INT_MIN, and the author 
expects the difference INT_MIN - 1 = INT_MAX, which is reasonable, but 
mathematically bogus, as well as j *= 2 will mathematically always be >0 as 
long you start with a positive value.  And today's clever optimizing compilers 
do what you write, not what you intend; e.g. produce an endless loop when you 
write an expression that it can prove to be always true as the loop condition.

> It leaves me to wonder about using something that
> invovled PRECISION(time_t_max).
> 
You can't do that as long you don't know if it's really the maximum value.
In the code above (i < PRECISION (INT_MAX)): to account for any padding bits.  
To be portable.

> > +/*! vi: set ai tabstop=8 shiftwidth=2: */
When fixing code, I think it's polite to adopt the genuine formating.  Since 
in this case it's rather unusual, I'm telling my editor to use sw=2 (I use 4).
-- 
=|o)	"Stell' Dir vor es geht und keiner kriegt's hin." (Wolfgang Neuss)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: check_mktime.c.patch
Type: text/x-patch
Size: 5369 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-hackers/attachments/20210118/4decb044/attachment.bin>


More information about the freebsd-hackers mailing list