From: tr.17687@z991.linuxsc.com   
      
   Richard Harnden writes:   
      
   [edited for brevity]   
      
   > On 29/11/2022 12:03, Tim Rentsch wrote:   
      
   >..> On 21/11/2022 20:45, Ben Bacarisse wrote:   
   >..>   
   >..>> Consider any ordered measure that "wraps round" -- bearings in   
   >..>> degrees, minutes in the hour, indeed hours in either the 12 or   
   >..>> 24 hour clock. The problem is to determine if a given value is   
   >..>> in the sub-range specified by a start and an en value.   
   >..>>   
   >..>> I was specifically concerned with integer values where the   
   >..>> sub-range includes the start value but excludes the end value.   
      
   >> [responding to an earlier proposed solution] This assumption   
   >> doesn't match the problem statement (emphasis added):   
   >>   
   >> Consider >> any << ordered measure that "wraps round"   
   >>   
   >> What's being asked for is a function that will work with any   
   >> ordered measure (that wraps), not just some such measures. An   
   >> example of such a measure is longitude, which goes from -180   
   >> to +180 (with one of the two extreme values omitted). Similarly >   
   >> there is no reason not to allow a measure that is only positive   
   >> integers but does not include zero. An important part of the   
   >> challenge is to come up with a solution that handles these cases   
   >> as well as the more obvious ones.   
   >   
   > Okay, so how about this ... ?   
   >   
   > int in_subrange(int_fast64_t range_min, int_fast64_t range_max,   
   > int_fast64_t start, int_fast64_t end, int_fast64_t check)   
   > {   
   > while ( check < range_min )   
   > check += range_max - range_min;   
   >   
   > while ( check > range_max )   
   > check -= range_max - range_min;   
   >   
   > if ( ( end < start && (   
   > ( check > range_min && check <= end )   
   > || (check >= start && check < range_max)   
   > )   
   > )   
   > || ( check >= start && check <= end )   
   > )   
   > return 1;   
   >   
   > return 0   
   > }   
      
   Several things stand out to me.   
      
   The code seems confused about whether range_min and range_max are   
   legal values or just outside of legal values. Normally I would   
   expect them to be legal values, since the "just outside of legal   
   values" choice might fall outside the range of the type being used.   
      
   Assuming range_min and range_max are legal values, the code has an   
   off-by-one error, namely, it should be range_max - range_min + 1   
   that is added or subtracted to bring 'check' into the legal range.   
      
   There is still the problem of potential overflow, because the value   
   of the expression 'range_max - range_min' might exceed the bounds of   
   the type being used.   
      
   Minor comment: the choice of 'int_fast64_t' for the parameter type   
   is kind of surprising. Why not 'int_least64_t' or maybe even just   
   'long long'?   
      
   The expressions 'check <= end' should be 'check < end' to conform to   
   the problem statement that the interval being considered excludes   
   the end value.   
      
   If range_min and range_max are legal values, the tests for 'check'   
   being inside the range become superfluous.   
      
   Incidentally, since the problem statement doesn't say, to my way of   
   thinking it would be fine simply to exclude values of 'check' that   
   fall outside the legal range   
      
    if( check < range_min || check > range_max ) return 0;   
      
   which has the nice by-product of avoiding the overflow problem.   
      
   Let's look again at the key test, simplified so as not to do the   
   range tests (some redundant parentheses retained):   
      
    if(   
    (end < start && (check <= end || check >= start))   
    || (check >= start && check <= end)   
    ){   
    return 1;   
    }   
      
   I think the logic of this test is right, but the form of it makes me   
   nervous. The reason for that is the second line depends on the   
   condition 'start <= end' being true, but that condition is not   
   explicitly tested. The condition is /implied/ by the tests that are   
   done against 'check', but that result is not immediately obvious. I   
   think a better writing would be to use a ?: operator, as for example   
      
    if(   
    end < start   
    ? start <= check || check < end   
    : start <= check && check < end   
    ){   
    return 1;   
    }   
      
   Now it is immediately obvious that the two cases are mutually   
   exclusive.   
      
   Finally, consider the last two statements. Since the penultimate   
   statement conditionally returns 1 (true) and the last statement   
   simply returns 0 (false), these two statements can be combined into   
   a single 'return' statement:   
      
    return   
    end < start   
    ? start <= check || check < end   
    : start <= check && check < end   
    ;   
      
   (I hope you will excuse my tendency to try to simplify almost any   
   code that I see. I appreciate what you've done here.)   
      
   > It's okay for check to have 'clocked', range_min, range_max, start   
   > and end have sensible values.   
      
   What I think you mean by this is that 'start' and 'end' can be   
   assumed to have values within the legal range, and that range_min   
   and range_max have sensible values (so range_min <= range_max),   
   and that 'check' might fall outside the legal range, and that   
   possibility must be accounted for. That makes sense.   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|