Detecting inadvertent use of integer division

Don nospam at nospam.com
Tue Dec 15 02:52:06 PST 2009


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)

> 
> But it's probably not a very common problem.
> 
> -Lars



More information about the Digitalmars-d mailing list