Detecting inadvertent use of integer division

Don nospam at nospam.com
Tue Dec 15 11:31:01 PST 2009


Andrei Alexandrescu wrote:
> Don wrote:
>> Lars T. Kyllingstad wrote:
>>> Don wrote:
>>>> Jason House wrote:
>>>>> Don Wrote:
>>>>>
>>>>>> Walter Bright wrote:
>>>>>>> Don wrote:
>>>>>>>> Consider this notorious piece of code:
>>>>>>>>
>>>>>>>> assert(x>1);
>>>>>>>> double y = 1 / x;
>>>>>>>>
>>>>>>>> This calculates y as the reciprocal of x, if x is a 
>>>>>>>> floating-point number. But if x is an integer, an integer 
>>>>>>>> division is performed instead of a floating-point one, and y 
>>>>>>>> will be 0.
>>>>>>>>
>>>>>>>> It's a very common newbie trap, but I find it still catches me 
>>>>>>>> occasionally, especially when dividing two variables or 
>>>>>>>> compile-time constants.
>>>>>>>>
>>>>>>>> In the opPow thread there were a couple of mentions of 
>>>>>>>> inadvertent integer division, and how Python is removing this 
>>>>>>>> error by making / always mean floating-point division, and 
>>>>>>>> introducing a new operator for integer division.
>>>>>>>>
>>>>>>>> We could largely eliminate this type of bug without doing 
>>>>>>>> anything so drastic. Most of the problem just comes from C's 
>>>>>>>> cavalier attitude to implicit casting. All we'd need to do is 
>>>>>>>> tighten the implicit conversion rules for int->float, in the 
>>>>>>>> same way that the int->uint rules have been tightened:
>>>>>>>>
>>>>>>>> "If an integer expression has an inexact result (ie, involves an 
>>>>>>>> inexact integer divison), that expression cannot be implicitly 
>>>>>>>> cast to a floating-point type."
>>>>>>> But the compiler cannot reliably tell if it will produce an 
>>>>>>> inexact result.
>>>>>>>
>>>>>>>
>>>>>>>> (This means that double y = int_val / 1;  is OK, and also:
>>>>>>>>  double z = 90/3; would be OK. An alternative rule would be:
>>>>>>>> "If an integer expression involves integer divison, that 
>>>>>>>> expression cannot be implicitly cast to a floating-point type").
>>>>>>> This is kinda complicated if one has, say:
>>>>>>>
>>>>>>>     double z = x/y + 3;
>>>>>> Integer expressions remain inexact until there's a cast.
>>>>>>
>>>>>> (It's very simple to implement, you just use the integer range 
>>>>>> code, adding an 'inexact' flag. Division sets the flag, casts 
>>>>>> clear the flag, everything else just propagates it if a unary 
>>>>>> operation, or ORs the two flags if a binary operation).
>>>>>
>>>>> What about function calls?
>>>>> double z = abs(x/y);
>>>>
>>>> Yeah, it won't catch cases where there are both integer and 
>>>> floating-point overloads of the same function.  abs() and pow() are 
>>>> the only two I can think of -- and pow() will be covered by ^^.
>>>>
>>>> There's probably a few others.
>>>
>>> I think the most subtle cases will be calls to max() and min(). If 
>>> you do
>>>
>>>    x = max(1.2, 3/2);
>>>
>>> and the 'inexact' flag doesn't survive beyond the function call, 
>>> there will be a silent conversion to double inside max() and the 
>>> function will return 1.2.
>>
>> Note that that wouldn't happen if max had a signature like:
>> max(double a, double b)
>> or
>> max(T)(T a, T b)
> 
> max takes heterogeneous parameters to catch situations like max(a, 0).
> 
> Andrei

Yeah. It's a shame we can't use the ?: type rule for common parameters, 
since max(T, U) only makes sense when T and U have a common type. So we 
get code bloat, with unnecessary template instantiations. But it'd be 
too complicated otherwise, I think. C'est la vie.



More information about the Digitalmars-d mailing list