Thread overview | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
December 14, 2009 Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
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." (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"). In the very rare cases where the result of an integer division was actually intended to be stored in a float, an explicit cast would be required. So you'd write: double y = cast(int)(1/x); Like the implicit uint->int casts which have recently been disallowed, I think this would prevent a lot of bugs, without causing much pain. |
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to Don | 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; > In the very rare cases where the result of an integer division was actually intended to be stored in a float, an explicit cast would be required. So you'd write: > double y = cast(int)(1/x); > > Like the implicit uint->int casts which have recently been disallowed, I think this would prevent a lot of bugs, without causing much pain. Maybe it's easier to simply disallow 1/x, where x is an int, and the numerator is '1'. But then maybe that will cause problems with generic code. I don't think there's an easy answer here. |
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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).
|
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to Don | Don:
> 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.
In Pascal too (and OCaML, but the situation is different) they are separated. I think here having two operators is better, Niklaus Wirth was right again (but I don't know if D can grow another division operator, it's unlikely).
Bye,
bearophile
|
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | bearophile wrote: > Don: >> 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. > > In Pascal too (and OCaML, but the situation is different) they are separated. I think here having two operators is better, Why? Niklaus Wirth was right again (but I don't know if D can grow another division operator, it's unlikely). > > Bye, > bearophile |
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to Don | Don:
> > In Pascal too (and OCaML, but the situation is different) they are separated. I think here having two operators is better,
>
> Why?
You are intelligent and expert so you must know my answer, so I fear yours is a trick question :-)
Two operators allow to reduce the need for casts (and rounding/truncation), and are more explicit, allowing the code to express its meaning better to people that come after the original programmer.
You can put them in the middle of a long expression, so you know what it's happening in the middle of it. This is useful for programming newbies too. I know there's a C translation of every usage of those two operators, but this is beside the point: even if the Laws of C language are sometimes explicit, sometimes they are not so natural and easy to remember, because normal people are not computers.
Not every part of the C language is designed perfectly, there's space for improvements.
Bye,
bearophile
|
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile |
"bearophile" <bearophileHUGS@lycos.com> wrote:
> Don:
>> > In Pascal too (and OCaML, but the situation is different) they are separated. I think here having two operators is better,
>>
>> Why?
>
> You are intelligent and expert so you must know my answer, so I fear yours is a trick question :-)
>
> Two operators allow to reduce the need for casts (and rounding/truncation), and are more explicit, allowing the code to express its meaning better to people that come after the original programmer.
>
> You can put them in the middle of a long expression, so you know what it's happening in the middle of it. This is useful for programming newbies too. I know there's a C translation of every usage of those two operators, but this is beside the point: even if the Laws of C language are sometimes explicit, sometimes they are not so natural and easy to remember, because normal people are not computers.
>
> Not every part of the C language is designed perfectly, there's space for improvements.
>
> Bye,
> bearophile
>
I like Pascal having / for floating-point and div for integers. It's rather
intuiteve that 1/2 = 0.5 and 1 div 2 = 0.
Or worse - what newbies use to input things like :
const int b=1, a=0, n = 10 ;
double h = (b-a)/n ; // haha who expect it be 0.0
As fact integer division is very different operation then floating point one. IMHO giving them one is a little like giving < and > operators second usage for templates.
But besides that all it's only a convenience issue. Having enough expierience and making explicit casting is rather quick thing.
|
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to Don | Don Wrote:
> Walter Bright wrote:
> > Don wrote:
> >>
> >> (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).
Assuming it really is this easy, I'd love to see the change. I've run into this bug countless times reviewing code, and it can be a tricky one to find. I tend to be pretty particular about always appending the ".0" for literals involved in floating-point assignments for this reason.
|
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to Don | 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
Agreed! :(
|
December 14, 2009 Re: Detecting inadvertent use of integer division | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | bearophile wrote: > Don: >>> In Pascal too (and OCaML, but the situation is different) they are separated. I think here having two operators is better, >> Why? > > You are intelligent and expert so you must know my answer, so I fear yours is a trick question :-) No, it's not a trick question. You've used Python extensively, I haven't. > Two operators allow to reduce the need for casts (and rounding/truncation), and are more explicit, allowing the code to express its meaning better to people that come after the original programmer. OK. I'm trying to get most of the benefits without needing an extra operator. What I wonder is, in what contexts is / ambiguous or difficult to read? My feeling is that intentionally mixing integer division and floating-point in the same expression is extremely rare. The effect of my rule would be, that if you see an int anywhere, it's an integer division; and if you see a floating point number anywhere, it's a floating point division. BTW, does Python allow integer division of floating point numbers? eg, int_a = float_b // float_c; ? (meaning cast(int)float_b / (cast(int)float_c); ). My idea fails if: * I'm completely wrong about the frequency of mixing integer division with floating point. So the cost is high. OR * Inadvertant integer division commonly occurs in things like int a = b/c; (which would mean that I'm wrong in assuming it's primarily an implicit casting problem). If the benefit is low, it's not much use. OR * There's something I didn't think of. Which is quite probable <g>. > You can put them in the middle of a long expression, so you know what it's happening in the middle of it. This is useful for programming newbies too. >I know there's a C translation of every usage of those two operators, but this is beside the point: even if the Laws of C language are sometimes explicit, sometimes they are not so natural and easy to remember, because normal people are not computers. Yeah, I agree. C never errs on the side of caution <g>. Much of D involves tightening things up considerably. |
Copyright © 1999-2021 by the D Language Foundation