[Info-vax] VMS Cobol - GnuCOBOL

Dan Cross cross at spitfire.i.gajendra.net
Mon Mar 6 17:07:28 EST 2023


In article <tu4pi3$nhe$1 at news.misty.com>,
Johnny Billquist  <bqt at softjar.se> wrote:
>On 2023-03-03 13:42, bill wrote:
>> On 3/3/2023 6:59 AM, Johnny Billquist wrote:
>>> Damn! I learned something new today...
>>>
>>> On 2023-03-01 15:54, Dan Cross wrote:
>>>> In article <ttnk0m$lg5$2 at news.misty.com>,
>>>> Johnny Billquist  <bqt at softjar.se> wrote:
>>>>> On 2023-02-28 20:11, Dan Cross wrote:
>>>
>>> [...]
>>>
>>>>> You overcomplicated the whole explanation.
>>>>
>>>> Hmm.
>>>
>>> I was wrong. :-)
>>>
>>>>> Basically, if you multiply two numbers, and the result would be bigger
>>>>> than the types involved, the result is undefined.
>>>>
>>>> Well...no.  _unsigned_ integer overflow in C is well-defined (it
>>>> has modular wrapping semantics; C11 sec 6.2.5 para 9).
>>>> Similarly, overflow of signed atomics is well-defined (C11 sec
>>>> 7.17.7.5 para 3), so this is not always true.
>>>
>>> I had actually missed that unsigned integers do have a defined 
>>> overflow behavior. Thanks.
>>>
>>>>> In this case, the type
>>>>> is unsigned short. If the multiplication cause anything that don't fit
>>>>> into an unsigned short, then the result is undefined.
>>>>>
>>>>> And it's fairly easy to find two unsigned short numbers that when
>>>>> multiplied will give a result that don't fit into an unsigned short.
>>>>
>>>> The range of unsigned short has little to do with it, and
>>>> truncation of the result is fine too (again, defined to use
>>>> modular wrapping for unsigned types; C11 sec 6.3.1.3).  The
>>>> problem is entirely due to the promotion to _signed_ int prior
>>>> to the multiplication.  The fix, incidentally, is easy:
>>>>
>>>> unsigned short
>>>> mul(unsigned short a, unsigned short b)
>>>> {
>>>>     unsigned int aa = a, bb = b;
>>>>     return aa * bb;
>>>> }
>>>>
>>>> This is well-defined, even if the range of `unsigned short` is
>>>> the same as `unsigned int`, which is permitted by the standard.
>>>
>>> Right. My second error. It is allowed to promote a unsigned short to a 
>>> signed int.
>>> I do feel that I don't entirely agree with that rule, but it's 
>>> definitely written as such.
>>>
>>> Changing from unsigned to signed feels just as a freedom too much.
>>>
>>>>> It's really not that different from any other language. Some languages
>>>>> will throw an exception, others will just give some result that 
>>>>> might or
>>>>> might not be meaningful. But there is no guarantee in almost any
>>>>> language of some kind of specific meaningful result. Try it in FORTRAN
>>>>> and see what you get there, or Cobol (or BASIC). :-)
>>>>
>>>> The issue, and the reason for the complex initial explanation,
>>>> is the subtle interaction between the implicit type promotion
>>>> rules, arithmetic, undefined behavior, and the freedom that UB
>>>> gives to compiler writers, which is pretty unique to C.  Hence
>>>> why optimizing compilers often _appear_ to introduce bugs when
>>>> in fact they're performing perfectly legal transformations, and
>>>> turning off the optimizer can appear to "fix" the problem.
>>>
>>> Yup. You were right. Sorry that I had to go and read the spec again. 
>>> Trusting my brain was obviously the wrong thing to do.
>>>
>> 
>> And it really doesn't matter whether the result is truncation,
>> conversion to signed int, wrap around, throwing an exception or
>> undefined.  The answer is still going to be wrong and the calculation
>> worthless.  So, what's the point?  Fix the damn code.
>> 
>> Go ahead, blame the language again.
>
>No. I don't fully agree. The fact that unsigned types are defined to 
>have the wrapping behavior means that you could definitely do this 
>intentionally and for rather good reasons.
>
>But the type propagation rule here breaks that promise, which is what I 
>find *very* broken. I do consider it a error in the language 
>specification when this can happen.

In fairness to C (!!), this only happens for types that are
lower "rank" than `int`, where "rank" is defined in C11 sec
6.3.1.1.  So if I start with an `unsigned int` and do my
arithmetic, the implicit conversion to a larger signed type
won't happen; the issue is when one starts with smaller types.

Hence why the `mul` example is fixed by assignment to
intermediate `unsigned int`s:

unsigned short
mul(unsigned short a, unsigned short b)
{
	unsigned int aa = a;
	unsigned int bb = b;
	return aa * bb;
}

Of course, these rules are sufficiently obtuse, and so easy to
accidentally misuse, that I have to agree with your assessment
that this feels like a bug in the language.

	- Dan C.




More information about the Info-vax mailing list