[comp.lang.pascal] Is this a bug in TurboPascal

husak@ux1.cso.uiuc.edu (BugsBunny) (05/01/91)

I'm running the following in TP and get the wrong answer.. Please correct
me if I'm wrong...

program Is_this_wrong;

var
    xx,yy,c1,c2,c3,c4: integer;

begin
    xx := 639; yy := 479; 
    c1 := Trunc(50*xx/639); c2 := Trunc(50*yy/479);
    c3 := Trunc(300*xx/639); c4 := Trunc(250*yy/479);
    writeln(xx,yy,c1,c2,c3,c4);
end.

OUTPUT:
639,479,50,50,-7,-23

How does it happen that the last two numbers are wrong and the rest right???

			Really confused in Illinois,

				Steve

-- 
"What am I trying to do, what am I trying to say, I'm not trying to tell you 
 anything you didn't know when you woke up today..."
				- Depeche Mode "Nothing" MUSIC FOR THE MASSES

shephard@newsserver.sfu.ca (Gordon Shephard) (05/01/91)

>var
>    xx,yy,c1,c2,c3,c4: integer;

>begin
>    xx := 639; yy := 479; 
>    c1 := Trunc(50*xx/639); c2 := Trunc(50*yy/479);
>    c3 := Trunc(300*xx/639); c4 := Trunc(250*yy/479);
>    writeln(xx,yy,c1,c2,c3,c4);
>end.

>OUTPUT:
>639,479,50,50,-7,-23

>How does it happen that the last two numbers are wrong and the rest right???

In Turbo Pascal, An Integer can take on the values from -32768 to 32767.
This is because An integer is only two bytes long, and therefore can
only take on 65536 values. 2^16.
TP also operates in the Lowest Common Denominator when Doing Arithmetic.
E.G. All of your variables are integers, so TP worked with integers.
(Note, if you'd have multiplied by 300.0 and 250.0, always a good habit
when you risk overflow, The answers would have been correct.)
When you multiplied 300*639, your answer is 191700.  So not only do you
wrap around to the negative values (32767+1=-32768 in integer
arithmetic.  Just like a speedometer), you also lose some data
(overflow).  In Hexdecimal, 191700=$2ECD4, of which only the $ECD4 is
stored.  $ECD4=-4908 as an integer. Trunc (-4908/639) = -7.
 
 The easiest way around all of this is to add .0 to at least one number
 in an equation which could cause trouble.  This forces TP to perform
 Real Arithmetic on the numbers involved


-- 
| Gordon Harry Shephard         | Distributed Computing Support Group  |
| Academic Computing Services   | Phone: (604)291-3930   (604)464-4991 | 
| Simon Fraser University       | USERGHS@SFU.BITNET                   |
| Burnaby, BC, Canada. V5A 1S6  | Shephard@Whistler.sfu.ca             | 

togood@roger.lerc.nasa.gov (Chris Miller) (05/01/91)

In article <1991May1.021059.2129@ux1.cso.uiuc.edu> husak@ux1.cso.uiuc.edu (BugsBunny) writes:
>I'm running the following in TP and get the wrong answer.. Please correct
>me if I'm wrong...
>
>program Is_this_wrong;
>var
>    xx,yy,c1,c2,c3,c4: integer;
>begin
>    xx := 639; yy := 479; 
>    c1 := Trunc(50*xx/639); c2 := Trunc(50*yy/479);
>    c3 := Trunc(300*xx/639); c4 := Trunc(250*yy/479);
>    writeln(xx,yy,c1,c2,c3,c4);
>end.
>
>OUTPUT:
>639,479,50,50,-7,-23
>
>How does it happen that the last two numbers are wrong and the rest right???
>			Really confused in Illinois,
>				Steve

No, it isn't wrong, you are creating a number that is too large for an integer 
variable (i.e. larger than MaxInt):

program RollOver;
var
  i: integer; l: longint;
begin
  writeln('MaxInt =',MaxInt);
  for i := 50 to 52 do begin
    l := longint(i)*639;
    writeln(i, ' ',l,' ',i*639,' ',Trunc(i*639/639));
  end;
end.

OUTPUT:
MaxInt =32767
50 31950 31950 50
51 32589 32589 51
52 33228 -32308 -50

dmurdoch@watstat.waterloo.edu (Duncan Murdoch) (05/01/91)

In article <1991May1.060345.26511@newsserver.sfu.ca> shephard@newsserver.sfu.ca (Gordon Shephard) writes:
> 
> The easiest way around all of this is to add .0 to at least one number
> in an equation which could cause trouble.  This forces TP to perform
> Real Arithmetic on the numbers involved
>

You have to be careful which number gets the decimal point.  TP (like most
languages) evaluates expressions a pair of operands at a time; it's the
types of the pair that determine the result, not the types of other parts
of the expression.  For example,
 
var
  int1,int2 : integer;
  real1,result : real;
begin
  int1 := 256;
  int2 := 256;
  real1 := 1.0;
  result := real1 + int1*int2;
  writeln('result = ',result);
end.

will give a result of 1.0; the multiplication is performed in the integer
types, and overflows.  Expressions like

  real1 * int1 * int2
and
  int1 * int2 * real1

are especially dangerous, because you don't know in advance how TP will pair
the operands, and it makes a difference when overflows can occur.  (In TP
6.0, with the values above, the results of those two expressions are
65536.0 and 0.0 respectively, because on Wednesdays it operates from left
to right.  I haven't tried the program on other days of the week, so I'm
not sure what happens then.)

Duncan Murdoch

DBH106@psuvm.psu.edu (05/01/91)

When you use integers, you must be aware that the highest value an integer can
have is 32767.  The program you posted multiplied 300 * 639 {xx} / 639.  First,
300 is multiplied by 639, resulting in 191700.  This is the error.  If you had
the Range Checking flag on you would have got a run time error.  With it off,
you get (I think) -4908.  Then the division results in -7.68... .  the Trunc
function gives -7 as the answer.  The other bad input is the same problem, I
believe.

One suggestion.  When first writing a program, set all the compiler option
flags.

Dan Harter
DBH106@psuvm.psu.edu

milne@ics.uci.edu (Alastair Milne) (05/02/91)

In <1991May1.060345.26511@newsserver.sfu.ca> shephard@newsserver.sfu.ca (Gordon Shephard) writes:

>>var
>>    xx,yy,c1,c2,c3,c4: integer;

>>begin
>>    xx := 639; yy := 479; 
>>    c1 := Trunc(50*xx/639); c2 := Trunc(50*yy/479);
>>    c3 := Trunc(300*xx/639); c4 := Trunc(250*yy/479);
>>    writeln(xx,yy,c1,c2,c3,c4);
>>end.

>>OUTPUT:
>>639,479,50,50,-7,-23

>>How does it happen that the last two numbers are wrong and the rest right???

    If you have never before seen integer overflow, you are very lucky.

    Look at the order of operations you are probably getting in the 
    3rd line of assignments, and consider what the intermediate
    results probably are: 300*639, 250*479.  Far bigger than MaxInt.

    Yes, I know -- you looked at those assignments and thought 
    "anybody in his right mind will see a fraction here with numerator
    and denominator the same, so they'll do that first."  But Pascal
    won't.

    If you want a specific order of evaluation for an expression, make *sure*
    you parenthesise the parts to be done first.

    Try TRUNC(300 * (xxx/639)).  I think you'll find a difference.

>TP also operates in the Lowest Common Denominator when Doing Arithmetic.
>E.G. All of your variables are integers, so TP worked with integers.

     No.  The '/' operator is floating-point division, no matter the 
     type of its operands.  For integer division, DIV must be used 
     (MOD is integer division too, but a different operation).

     Also, having yielded a floating-point result from the division,
     Pascal then assumes floating-point multiplication (that is, assuming
     the division is done first, as the poster intended.)  That is, 
     if either operand of "*" is floating-point, so is the result.

     This is why the TRUNC operation was needed before the assignment, 
     to obtain the integer part of a floating point result.

>(Note, if you'd have multiplied by 300.0 and 250.0, always a good habit
>when you risk overflow, The answers would have been correct.)

      This must be my day for disagreeing.  Floating-point often involves
      representational inaccuracy -- and is often slower to boot, though
      with some newer CPU's that isn't always true.  

      Within a certain range outside integer's scope, floating-point 
      is safe enough -- but it's no panacea.  There are points, I grant,
      where you wind up needing it for its range allowance, but you pay
      a price.  Certainly it's not the primary answer in the case 
      we have here.

>When you multiplied 300*639, your answer is 191700.  So not only do you
>wrap around to the negative values (32767+1=-32768 in integer
>arithmetic.  Just like a speedometer), you also lose some data
>(overflow).  ... 

       Here I agree completely.  Overflow is indeed losing data.

> The easiest way around all of this is to add .0 to at least one number
> in an equation which could cause trouble.  ...

       The best way around all this is to make sure the evaluation
       order that was intended is in fact used, with judicious use
       of parentheses.  And of course, be *aware* of the intermediate
       operations and results in an evaluation.

       As we see here, they make a difference.


       Alastair Milne

bobb@vice.ICO.TEK.COM (Bob Beauchaine) (05/04/91)

In article <1991May1.134303.7289@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:
>Expressions like
>
>  real1 * int1 * int2
>and
>  int1 * int2 * real1
>
>are especially dangerous, because you don't know in advance how TP will pair
>the operands, and it makes a difference when overflows can occur.  (In TP
>6.0, with the values above, the results of those two expressions are
>65536.0 and 0.0 respectively, because on Wednesdays it operates from left
>to right.  I haven't tried the program on other days of the week, so I'm
>not sure what happens then.)
>

  The only (?) sure way to get around this is to typecast.  Choosing a 
  temporary result variable can guarantee you a correct value, no
  matter what day of the week it is.  For instance, you can't overflow
  a long integer with the product of two integers, no matter what the
  magnitude of the two integers.  So something like this will work,
  independent of the compiler's ordering of terms and factors, because
  of the precedence of parenthesis:
    
       real1 * longint(int1 * int2)
       or
       longint(int1 * int2) * real1

  Note, however, that you can only have two *distinct* operands inside
  the typecast, or the problem can occur all over again inside the 
  parenthesis.


  I found this out, as usual, by having a program crash from overflow.

/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ 

Bob Beauchaine bobb@vice.ICO.TEK.COM 

C: The language that combines the power of assembly language with the 
   flexibility of assembly language.