www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - DMD 0.96: type not promoting..

reply Regan Heath <regan netwin.co.nz> writes:
Hi,

I just got bitten by a type promotion problem, I was wondering whether 
people here think this is a bug, or otherwise.

import std.string;

void output(long a)
{
   printf("%.*s\n",toString(a));
}

void main()
{
   long a,b;

   //inline
   output(int.max+1);

   //using long
   a = int.max+1;
   output(a);

   //using constant value
   output(2147483647+1);

   //using long in 2 steps
   b = int.max;
   b += 1;
   output(b);
}

Ouput:
-2147483648
-2147483648
-2147483648
2147483648

The last result was the desired result in my case. I believe all 4 tests 
should give that result.


The C equivalent, using short as C does not have a long (that is bigger 
than an int) and assuming that the D property is an int itself, which it 
seems to be, would be:

#include <limits.h>

void foo(int a) {
   printf("%d\n",a);
}
void main() {
   short max = SHRT_MAX;
   int a,b;

   //inline
   output(max+1);

   //using int
   a = max+1;
   output(a);

   //using constant value
   output(32767+1);

   //using int in 2 steps
   b = max;
   b += 1;
   output(b);
}

Output:
32768
32768
32768
32768

So it appears C is promoting the short 'a' to an int then adding 1 to it 
_or_ perhaps this is all due to how int and short are physically 
represented?

Regan

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
I just tried it D with int and short, eg.

import std.string;

void output(int a)
{
   printf("%.*s\n",toString(a));
}

void main()
{
   int a,b;

   //inline
   output(short.max+1);

   //using var
   a = short.max+1;
   output(a);

   //using constant value
   output(32767+1);

   //2 steps
   b = short.max;
   b += 1;
   output(b);
}

Output:
32768
32768
32768
32768

So it appears the behaviour is different when using int/short to long/int.

Regan

On Mon, 26 Jul 2004 20:06:59 +1200, Regan Heath <regan netwin.co.nz> wrote:
 Hi,

 I just got bitten by a type promotion problem, I was wondering whether 
 people here think this is a bug, or otherwise.

 import std.string;

 void output(long a)
 {
    printf("%.*s\n",toString(a));
 }

 void main()
 {
    long a,b;

    //inline
    output(int.max+1);

    //using long
    a = int.max+1;
    output(a);

    //using constant value
    output(2147483647+1);

    //using long in 2 steps
    b = int.max;
    b += 1;
    output(b);
 }

 Ouput:
 -2147483648
 -2147483648
 -2147483648
 2147483648

 The last result was the desired result in my case. I believe all 4 tests 
 should give that result.


 The C equivalent, using short as C does not have a long (that is bigger 
 than an int) and assuming that the D property is an int itself, which it 
 seems to be, would be:

 #include <limits.h>

 void foo(int a) {
    printf("%d\n",a);
 }
 void main() {
    short max = SHRT_MAX;
    int a,b;

    //inline
    output(max+1);

    //using int
    a = max+1;
    output(a);

    //using constant value
    output(32767+1);

    //using int in 2 steps
    b = max;
    b += 1;
    output(b);
 }

 Output:
 32768
 32768
 32768
 32768

 So it appears C is promoting the short 'a' to an int then adding 1 to it 
 _or_ perhaps this is all due to how int and short are physically 
 represented?

 Regan

-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
parent reply Cabal <cabalN05P4M myrealbox.com> writes:
I would regard your first example as correct and the second as a bug in D. 

   //inline
   output(int.max+1);

   //using long
   a = int.max+1;
   output(a);

   //using constant value
   output(2147483647+1);

In these lines you are adding 1 to int.max in an integer context which trips over into negative values. Only after this has happened is the int promoted to a long, and so the long contains a negative value too. Your second example looks like D is actually doing its short arithmetic with integer types. Which would be wrong imo.
Jul 26 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Mon, 26 Jul 2004 09:56:15 +0100, Cabal <cabalN05P4M myrealbox.com> 
wrote:
 I would regard your first example as correct and the second as a bug in 
 D.

    //inline
    output(int.max+1);

    //using long
    a = int.max+1;
    output(a);

    //using constant value
    output(2147483647+1);

In these lines you are adding 1 to int.max in an integer context which trips over into negative values. Only after this has happened is the int promoted to a long, and so the long contains a negative value too.

I assumed that is what was happening. Changing this:
    //using constant value
    output(2147483647+1);


    //using constant value
    output(2147483647L+1);


also gives me what I want "2147483648", instead of "-2147483648", I guess one has to remember that constants in D are integers, unless you state otherwise.
 Your second example looks like D is actually doing its short arithmetic 
 with
 integer types. Which would be wrong imo.

This example behaves as C/C++ does. I assume because: - short.max is actually an 'int' not a 'short'. - the constant is assumed to be an int. If short.max was a 'short' then my first trials would have given negative values and the last 2 positive values. How bout this for a radical new idea, I realise it would cause behaviour changes from C to D for the same expression, why not promote all operands in an expression to the resulting type, forcing people to use a cast if they do not want that behaviour. It occurs to me that wanting this behaviour is more common than wanting the current behaviour. eg. float a; int b; a = b/100; if 'b' and '100' are promoted to float before the calculation you get a different answer, and it's the answer you actually wanted in most cases. Currently you have to write the above as: a = cast(float)b/cast(float)100; or perhaps a = cast(float)b/100.0; I would argue the more common intent should be the default behaviour. Can anyone think of a counter example that shows a commonly desired outcome that is the current default behaviour? How does Java handle this sort of thing? What about python or Ruby? Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
next sibling parent reply Daniel Horn <hellcatv hotmail.com> writes:
Python just changed their language to do automatic type promotion
I hate it! :-(
if you divide 2 integers you wish to do integer math, not float math... 
(think arrays,random numbers, etc..)

Also it's a lot slower to start using the greatest possible type your 
expression could produce.  If you wish this behavior, write a class to 
do it (see: Arcane Jill's Big Integer)
:-)
or else just do math on longs, or reals all the time, rather than using 
smaller types initially.



 How bout this for a radical new idea, I realise it would cause behaviour 
 changes from C to D for the same expression, why not promote all 
 operands in an expression to the resulting type, forcing people to use a 
 cast if they do not want that behaviour.
 
 It occurs to me that wanting this behaviour is more common than wanting 
 the current behaviour. eg.
 
 float a;
 int b;
 
 a = b/100;
 
 if 'b' and '100' are promoted to float before the calculation you get a 
 different answer, and it's the answer you actually wanted in most cases. 
 Currently you have to write the above as:
 
 a = cast(float)b/cast(float)100;
 
 or perhaps
 
 a = cast(float)b/100.0;
 
 I would argue the more common intent should be the default behaviour.
 
 Can anyone think of a counter example that shows a commonly desired 
 outcome that is the current default behaviour?
 
 How does Java handle this sort of thing? What about python or Ruby?
 
 Regan.
 

Jul 26 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Mon, 26 Jul 2004 16:55:05 -0700, Daniel Horn <hellcatv hotmail.com> 
wrote:
 Python just changed their language to do automatic type promotion
 I hate it! :-(
 if you divide 2 integers you wish to do integer math, not float math... 
 (think arrays,random numbers, etc..)

and you will do integer math with my suggestion as long as you assign the result to an int and not a float... IIRC python does not have clearly defined types, so they cannot make this decision, yes?
 Also it's a lot slower to start using the greatest possible type your 
 expression could produce.

That is not the behaviour I want. What I want is the operands to be cast to the result type _before_ the expression is evaluated, rather than the current behaviour which is to cast the result after the expression.
 If you wish this behavior, write a class to do it (see: Arcane Jill's 
 Big Integer)
 :-)
 or else just do math on longs, or reals all the time, rather than using 
 smaller types initially.



 How bout this for a radical new idea, I realise it would cause 
 behaviour changes from C to D for the same expression, why not promote 
 all operands in an expression to the resulting type, forcing people to 
 use a cast if they do not want that behaviour.

 It occurs to me that wanting this behaviour is more common than wanting 
 the current behaviour. eg.

 float a;
 int b;

 a = b/100;

 if 'b' and '100' are promoted to float before the calculation you get a 
 different answer, and it's the answer you actually wanted in most 
 cases. Currently you have to write the above as:

 a = cast(float)b/cast(float)100;

 or perhaps

 a = cast(float)b/100.0;

 I would argue the more common intent should be the default behaviour.

 Can anyone think of a counter example that shows a commonly desired 
 outcome that is the current default behaviour?

 How does Java handle this sort of thing? What about python or Ruby?

 Regan.


-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
prev sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <opsbrqxd1l5a2sq9 digitalmars.com>, Regan Heath says...

the current behaviour. eg.

float a;
int b;

a = b/100;

if 'b' and '100' are promoted to float before the calculation you get a 
different answer, and it's the answer you actually wanted in most cases. 
Currently you have to write the above as:

a = cast(float)b/cast(float)100;

or perhaps

a = cast(float)b/100.0;

I would argue the more common intent should be the default behaviour.

Are you sure about D's current behavior. The way I read it, it currently does exactly what you want. From the manual: "The usual arithmetic conversions convert operands of binary operators to a common type. The operands must already be of arithmetic types. The following rules are applied in order: 1. Typedefs are converted to their underlying type. 2. If either operand is real, the other operand is converted to real. 3. Else if either operand is double, the other operand is converted to double. 4. Else if either operand is float, the other operand is converted to float. 5. Else the integer promotions are done on each operand, followed by: 1. If both are the same type, no more conversions are done. 2. If both are signed or both are unsigned, the smaller type is converted to the larger. 3. If the signed type is larger than the unsigned type, the unsigned type is converted to the signed type. 4. The signed type is converted to the unsigned type." Obviously, if it's not doing this, it's a bug, but it's always seemed to work for me.
Can anyone think of a counter example that shows a commonly desired 
outcome that is the current default behaviour?

I'm not convinced that /is/ the current default behavior. D works the same way as C and C++.
How does Java handle this sort of thing? What about python or Ruby?

I think Java /also/ works the same way as C, C++ and D (except that Java doesn't have unsigned types apart from char). Can't speak for those languages, but I can tell you that PHP is really weird. It expands numbers as required, like Python, and doesn't have unsigned types, so the constant 0xFFFFFFFF is a /double/. Actually I don't like this. Jill
Jul 27 2004
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 27 Jul 2004 07:46:07 +0000 (UTC), Arcane Jill wrote:

 In article <opsbrqxd1l5a2sq9 digitalmars.com>, Regan Heath says...
 
the current behaviour. eg.

float a;
int b;

a = b/100;

if 'b' and '100' are promoted to float before the calculation you get a 
different answer, and it's the answer you actually wanted in most cases. 
Currently you have to write the above as:

a = cast(float)b/cast(float)100;

or perhaps

a = cast(float)b/100.0;

I would argue the more common intent should be the default behaviour.

Are you sure about D's current behavior. The way I read it, it currently does exactly what you want. From the manual: "The usual arithmetic conversions convert operands of binary operators to a common type. The operands must already be of arithmetic types. The following rules are applied in order: 1. Typedefs are converted to their underlying type. 2. If either operand is real, the other operand is converted to real. 3. Else if either operand is double, the other operand is converted to double. 4. Else if either operand is float, the other operand is converted to float. 5. Else the integer promotions are done on each operand, followed by: 1. If both are the same type, no more conversions are done. 2. If both are signed or both are unsigned, the smaller type is converted to the larger. 3. If the signed type is larger than the unsigned type, the unsigned type is converted to the signed type. 4. The signed type is converted to the unsigned type." Obviously, if it's not doing this, it's a bug, but it's always seemed to work for me.

It is doing this, but this is not what is wanted by Regan. The expression 'a = b/100' is probable assessed thus ... ** b is an int. 100 is an int. Rule 5 applies. They remain 'int' as they are the same size integer. Thus the result of the calculation is an int too. ** a is a float, result is an int. Rule 4 applies. The result is converted to a float. The answer (assignment) is a float. But, not the expected value. It gets truncated to an int prior to conversion to a float. Maybe a better way would be for the compiler to recognize that the coder is asking for an assignment to a float, so all left-hand expressions need to be at least a float, but possibly promoted to real during calculation then truncated to float on assignment. If the coder wishes to cause the resulting left hand expression to be truncated to an integer, she could use an explicit cast. float a; int b; a = cast(int)(b/100);
Can anyone think of a counter example that shows a commonly desired 
outcome that is the current default behaviour?


Calculating pixel positions need to be truncated to integer values.
 I'm not convinced that /is/ the current default behavior. D works the same way
 as C and C++.
 
How does Java handle this sort of thing? What about python or Ruby?

I think Java /also/ works the same way as C, C++ and D (except that Java doesn't have unsigned types apart from char).

The Euphoria language does it the way Regan wants... c:\temp>type test.ex atom a integer b b = 3335 a = b / 100 printf(1, "%f\n", a) c:\temp>ex test 33.350000 c:\temp> And D definitely does not ... c:\temp>type test.d import std.stdio; void main() { float a; int b; b = 3335; a = b / 100; writef("%f\n", a); } c:\temp>dmd test C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test 33.000000 c:\temp> But if you make one tiny change, one gets ... c:\temp>type test.d import std.stdio; void main() { float a; int b; b = 3335; a = b / 100.0; // <-- Explicit float literal writef("%f\n", a); } c:\temp>dmd test C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test 33.349998 c:\temp> Well almost, the precision isn't so hot. To get better, change 'a' to double or real. -- Derek Melbourne, Australia 27/Jul/04 6:10:32 PM
Jul 27 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Tue, 27 Jul 2004 18:32:28 +1000, Derek Parnell <derek psych.ward> wrote:
 On Tue, 27 Jul 2004 07:46:07 +0000 (UTC), Arcane Jill wrote:

 In article <opsbrqxd1l5a2sq9 digitalmars.com>, Regan Heath says...

 the current behaviour. eg.

 float a;
 int b;

 a = b/100;

 if 'b' and '100' are promoted to float before the calculation you get a
 different answer, and it's the answer you actually wanted in most 
 cases.
 Currently you have to write the above as:

 a = cast(float)b/cast(float)100;

 or perhaps

 a = cast(float)b/100.0;

 I would argue the more common intent should be the default behaviour.

Are you sure about D's current behavior. The way I read it, it currently does exactly what you want. From the manual: "The usual arithmetic conversions convert operands of binary operators to a common type. The operands must already be of arithmetic types. The following rules are applied in order: 1. Typedefs are converted to their underlying type. 2. If either operand is real, the other operand is converted to real. 3. Else if either operand is double, the other operand is converted to double. 4. Else if either operand is float, the other operand is converted to float. 5. Else the integer promotions are done on each operand, followed by: 1. If both are the same type, no more conversions are done. 2. If both are signed or both are unsigned, the smaller type is converted to the larger. 3. If the signed type is larger than the unsigned type, the unsigned type is converted to the signed type. 4. The signed type is converted to the unsigned type." Obviously, if it's not doing this, it's a bug, but it's always seemed to work for me.

It is doing this, but this is not what is wanted by Regan. The expression 'a = b/100' is probable assessed thus ... ** b is an int. 100 is an int. Rule 5 applies. They remain 'int' as they are the same size integer. Thus the result of the calculation is an int too. ** a is a float, result is an int. Rule 4 applies. The result is converted to a float. The answer (assignment) is a float. But, not the expected value. It gets truncated to an int prior to conversion to a float.

Exactly! I didn't think I had to spell this out, obviously this mistake/bug/problem is not as comman as I thought, I have had it numerous times.
 Maybe a better way would be for the compiler to recognize that the coder 
 is
 asking for an assignment to a float, so all left-hand expressions need to
 be at least a float, but possibly promoted to real during calculation 
 then
 truncated to float on assignment.

That is what I have been suggesting.
 If the coder wishes to cause the resulting left hand expression to be
 truncated to an integer, she could use an explicit cast.

    float a;
    int b;
    a = cast(int)(b/100);

Yep.
 Can anyone think of a counter example that shows a commonly desired
 outcome that is the current default behaviour?


Calculating pixel positions need to be truncated to integer values.

The key word in my request was "commonly", I do not believe the above to be common. So while it's a good example that it needs to be possible, it's not the commonly desired outcome, and in fact if you were doing the above you'd remember to ensure it happens, correct?
 I'm not convinced that /is/ the current default behavior. D works the 
 same way
 as C and C++.

 How does Java handle this sort of thing? What about python or Ruby?

I think Java /also/ works the same way as C, C++ and D (except that Java doesn't have unsigned types apart from char).

The Euphoria language does it the way Regan wants... c:\temp>type test.ex atom a integer b b = 3335 a = b / 100 printf(1, "%f\n", a) c:\temp>ex test 33.350000 c:\temp> And D definitely does not ... c:\temp>type test.d import std.stdio; void main() { float a; int b; b = 3335; a = b / 100; writef("%f\n", a); } c:\temp>dmd test C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test 33.000000 c:\temp> But if you make one tiny change, one gets ... c:\temp>type test.d import std.stdio; void main() { float a; int b; b = 3335; a = b / 100.0; // <-- Explicit float literal writef("%f\n", a); } c:\temp>dmd test C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test 33.349998 c:\temp> Well almost, the precision isn't so hot. To get better, change 'a' to double or real.

Good examples. Thanks. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 27 2004
prev sibling parent Regan Heath <regan netwin.co.nz> writes:
On Tue, 27 Jul 2004 07:46:07 +0000 (UTC), Arcane Jill 
<Arcane_member pathlink.com> wrote:
 In article <opsbrqxd1l5a2sq9 digitalmars.com>, Regan Heath says...

 the current behaviour. eg.

 float a;
 int b;

 a = b/100;

 if 'b' and '100' are promoted to float before the calculation you get a
 different answer, and it's the answer you actually wanted in most cases.
 Currently you have to write the above as:

 a = cast(float)b/cast(float)100;

 or perhaps

 a = cast(float)b/100.0;

 I would argue the more common intent should be the default behaviour.

Are you sure about D's current behavior.

I assumed it was the same as C/C++.
 The way I read it, it currently does
 exactly what you want. From the manual:

 "The usual arithmetic conversions convert operands of binary operators 
 to a
 common type. The operands must already be of arithmetic types. The 
 following
 rules are applied in order:

 1. Typedefs are converted to their underlying type.
 2. If either operand is real, the other operand is converted to real.
 3. Else if either operand is double, the other operand is converted to 
 double.
 4. Else if either operand is float, the other operand is converted to 
 float.
 5. Else the integer promotions are done on each operand, followed by:
 1. If both are the same type, no more conversions are done.
 2. If both are signed or both are unsigned, the smaller type is 
 converted to the
 larger.
 3. If the signed type is larger than the unsigned type, the unsigned 
 type is
 converted to the signed type.
 4. The signed type is converted to the unsigned type."


 Obviously, if it's not doing this, it's a bug, but it's always seemed to 
 work
 for me.

These rules do not help for: a = b/100 (if a is a float and b is an int) they do help for: a = b/100 (if b is a float) a = b/100.0 (as 100.0 is a float) maybe it's just me, I do not think "100.0" when I think "one hundred" I think of "100" so I'll write "100", and it will give the wrong result.
 Can anyone think of a counter example that shows a commonly desired
 outcome that is the current default behaviour?

I'm not convinced that /is/ the current default behavior. D works the same way as C and C++.

The problem I have mentioned _is_ one I have in C.
 How does Java handle this sort of thing? What about python or Ruby?

I think Java /also/ works the same way as C, C++ and D (except that Java doesn't have unsigned types apart from char). Can't speak for those languages, but I can tell you that PHP is really weird. It expands numbers as required, like Python, and doesn't have unsigned types, so the constant 0xFFFFFFFF is a /double/. Actually I don't like this.

I don't want types to be expanded, why does everyone think that is what I am after... "type expansion", to me, suggests making an 'int' into a 'long' because the value assigned to it is too big for an 'int', I don't want that at all. I simply want the type promotion it would do to the result to be done to the operands before the expression is evaluated. eg. float a; int b; a = b/100 causes: - b is promoted to float - 100 is promoted to float - expression evaluated - result assigned to a; Yes, this causes more promotions to be done. Yes, this changes the result of the expression, such that in C it would give a different result. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 27 2004
prev sibling next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <opsbqjhxmf5a2sq9 digitalmars.com>, Regan Heath says...
Hi,

I just got bitten by a type promotion problem, I was wondering whether 
people here think this is a bug, or otherwise.

I suspect it's probably not a bug, but defined behaviour. From "Lexical": The type of the integer is resolved as follows: 1. If it is decimal it is the last representable of ulong, long, or int. 2. If it is not decimal, it is the last representable of ulong, long, uint, or int. 3. If it has the 'u' suffix, it is the last representable of ulong or uint. 4. If it has the 'l' suffix, it is the last representable of ulong or long. 5. If it has the 'u' and 'l' suffixes, it is ulong. So, I guess what you need to do is one of: # cast(long) int.max; # 4294967295l; // That's a lowercase L at the end, not a digit one.
Jul 26 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Mon, 26 Jul 2004 11:50:23 +0000 (UTC), Arcane Jill 
<Arcane_member pathlink.com> wrote:

 In article <opsbqjhxmf5a2sq9 digitalmars.com>, Regan Heath says...
 Hi,

 I just got bitten by a type promotion problem, I was wondering whether
 people here think this is a bug, or otherwise.

I suspect it's probably not a bug, but defined behaviour. From "Lexical": The type of the integer is resolved as follows: 1. If it is decimal it is the last representable of ulong, long, or int. 2. If it is not decimal, it is the last representable of ulong, long, uint, or int. 3. If it has the 'u' suffix, it is the last representable of ulong or uint. 4. If it has the 'l' suffix, it is the last representable of ulong or long. 5. If it has the 'u' and 'l' suffixes, it is ulong.

I figured that might be the case. Thanks for the above, I didn't find it in my searching.
 So, I guess what you need to do is one of:
 #    cast(long) int.max;
 #    4294967295l; // That's a lowercase L at the end, not a digit one.

Uppercase L works too, and is easier to see. Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
prev sibling parent "Walter" <newshound digitalmars.com> writes:
D cares along wholesale the C/C++ rules on integral promotions in arithmetic
expressions, which is what you're seeing. Changing this would mean very
subtle problems will crop up when converting C/C++ code into D, something I
think is worse.
Jul 27 2004