C9203002: Unreliable Code Generated When Shifting Left in C 6

ID Number: Q81540

6.00 6.00a 6.00ax | 6.00 6.00a

MS-DOS | OS/2

buglist6.00 buglist6.00a buglist6.00ax fixlist7.00

Summary

PROBLEM ID: C9203002

SYMPTOMS

The Microsoft C Compiler versions 6.0, 6.0a, and 6.0ax may produce

incorrect code when casting the result of a shift on an unsigned

integer constant into an unsigned long int. This problem occurs

only if the unsigned integer constant is less than 128, and becomes

apparent only when the result of the shift causes bit 15 to be set.

CAUSE

This problem occurs because the compiler treats the value as a

signed integer if it is less than 128. The code generated contains

the assembly instruction cwd (convert signed word to double) for

the cast from an unsigned int to an unsigned long int. When cwd is

executed on a value that has bit 15 set, bits 16 through 31 are set

causing an incorrect result.

RESOLUTION

To work around this problem, use a variable for the left operand of

the left shift instead of a literal value.

STATUS

Microsoft has confirmed this to be a problem in the Microsoft C

versions 6.0, 6.0a, 6.0ax. This problem was corrected in C/C++

version 7.0.

More Information:

The following line from the /Fc listing of the sample code below shows

the incorrectly generated code:

;|*** k=(unsigned long int)(1U<<i);

; Line 15

*** 00003d b8 01 00 mov ax,1

*** 000040 8a 0e 3a 00 mov cl,BYTE PTR _i

*** 000044 d3 e0 shl ax,cl

*** 000046 99 cwd

*** 000047 a3 00 00 mov WORD PTR _k,ax

*** 00004a 89 16 02 00 mov WORD PTR _k+2,dx

The register values after cwd is executed are as follows:

dx:ax = FFFF:8000

The following line, from the /Fc listing of the sample code below,

shows correctly generated code when using a variable instead of a

literal unsigned int:

;|*** k=(unsigned long int)(j<<i);

; Line 11

*** 000019 8a 0e 3a 00 mov cl,BYTE PTR _i

*** 00001d a1 3c 00 mov ax,WORD PTR _j

*** 000020 d3 e0 shl ax,cl

*** 000022 a3 00 00 mov WORD PTR _k,ax

*** 000025 c7 06 02 00 00 00 mov WORD PTR _k+2,0

Note, for unsigned integers that are 128 or greater, the compiler will

generate the correct code. The following sample code demonstrates this

problem.

Sample Code

-----------

/* Compile options needed: none

*/

#include<stdio.h>

int i=15;

unsigned int j=1;

unsigned long int k;

void main(void)

{

k=(unsigned long int)(j<<i); // k=32768

printf("\ttest one : k=%lu\n",k);

k=(unsigned long int)(1U<<i); // k=4294934528

printf("\t two : k=%lu\n",k);

}

Additional reference words: 6.00 6.00a 6.00ax