Microsoft KB Archive/63053

{|
 * width="100%"|

BUG: Operands Reversed in Divide Operation

 * }

Q63053

-

The information in this article applies to:


 * The C/C++ Compiler (CL.EXE), included with:
 * Microsoft C for MS-DOS, versions 6.0, 6.0a, and 6.0ax
 * Microsoft C for OS/2, versions 6.0, and 6.0a
 * Microsoft C/C++ for MS-DOS, version 7.0
 * Microsoft Visual C++, versions 1.0, 1.5

-

SYMPTOMS
Under certain situations, the Microsoft C Compiler can generate code that causes the denominator in a division operation to be divided by the numerator instead of the other way around.

Compiling the sample code below with default optimizations demonstrates the problem and causes the following error:

  run-time error M6103: Math - floating-point error: divide by 0

CAUSE
The problem apparently occurs only when the denominator contains both a double value and a function parameter, and the numerator is a global float. Also, the next statement apparently must contain an expression utilizing the result of the operation. These prerequisites to the problem indicate that the problem is directly tied to optimizations removing common subexpressions.

STATUS
Microsoft has confirmed this to be a problem in C version 6.0, C/C++ version 7.0, 8.0, and 8.0c. This problem was corrected in C version 6.0a, but reappeared in C/C++ version 7.0.

This is not a problem in the 32-bit compilers included with Visual C++ 32- bit Edition.

MORE INFORMATION
In the sample code, the denominator in the first assignment statement in set_adc is divided by the numerator. Therefore, instead of receiving 26.0/2.56 cast to an int, i gets a value of 0 (zero), or 2.56/26.0 cast to an int.

The following are several possible workarounds to the problem:


 * Disable optimizations.
 * Declare the numerator locally or as a double instead of a float.
 * Use a global float variable in the denominator instead of parameter.
 * Don't use a double value in the denominator.
 * Cast the denominator as a float before division.
 * Break up assignment statements with a function call.
 * Use the /qc compiler option.

The section of mixed source and assembly below shows the assembly instructions generated by default optimizations. The troublesome instruction [FDIVP ST(1),ST] is at offset 0023.

Following the logic below, width is pushed onto the coprocessor stack, ST. Then it is multiplied by 2.56. Then _f2 is pushed onto the coprocessor stack, ST, making the above result ST(1). Finally, the FDIVP instruction takes ST(1), the denominator, and divides it by ST, the numerator, which is _f2

The rest is to be expected; __ftol is called to convert the float to an integer. The result, 0, is moved from the AX register into the local variable i. Then i is pushed onto the coprocessor stack, ST, and then the FDIVR instruction divides _f2 by this value causing the divide by 0 error.

8:         i=(int)(f2/(2.56*width)); 9:         f1=f2/i; 0047:0014 9B            WAIT 0047:0015 D94604        FLD       DWord Ptr [width] 0047:0018 9B            WAIT 0047:0019 DC0EB802      FMUL      QWord Ptr [__fpinit+e (02B8)] 0047:001D 9B            WAIT 0047:001E D9064200      FLD       DWord Ptr [_f2 (0042)] 0047:0022 9B            WAIT 0047:0023 DEF9          FDIVP     ST(1),ST                  ; wrong 0047:0025 E8001B        CALL      __ftol (1B28) 0047:0028 8946FE        MOV       Word Ptr [i],AX 0047:002B 9B            WAIT 0047:002C DF46FE        FILD      Word Ptr [i] 0047:002F 9B            WAIT 0047:0030 D83E4200      FDIVR     DWord Ptr [_f2 (0042)] 0047:0034 9B            WAIT 0047:0035 D91ED004      FSTP      DWord Ptr [_f1 (04D0)] 0047:0039 90            NOP 0047:003A 9B            WAIT The following code generated with disabled optimizations shows the correct method of doing this. Width is pushed onto the coprocessor stack, ST. Width is then multiplied by 2.56 with the result stored in ST. The FDIVR instruction then divides _f2 by the above value, and after conversion, i equals 10 as it is supposed to.

8:         i=(int)(f2/(2.56*width)); 0047:0016 9B            WAIT 0047:0017 D94604        FLD       DWord Ptr [width] 0047:001A 9B            WAIT 0047:001B DC0EB802      FMUL      QWord Ptr [__fpinit+e (02B8)] 0047:001F 9B            WAIT 0047:0020 D83E4200      FDIVR     DWord Ptr [_f2 (0042)]   ; right 0047:0024 E8091B        CALL      __ftol (1B30) 0047:0027 8946FE        MOV       Word Ptr [i],AX 9:         f1=f2/i; 0047:002A 9B            WAIT 0047:002B D9064200      FLD       DWord Ptr [_f2 (0042)] 0047:002F 9B            WAIT 0047:0030 DE76FE        FIDIV     Word Ptr [i] 0047:0033 9B            WAIT 0047:0034 D91ED004      FSTP      DWord Ptr [_f1 (04D0)] 0047:0038 90            NOP 0047:0039 9B            WAIT

Sample Code
/* Compile options needed: C6  none float f1; float f2=26.0f;  // Works if f2 is declared locally or as double. void set_adc(float width) { // Works if width declared as local variable instead of parameter. int i;  i=(int)(f2/(2.56*width)); // Works if used with float constant 2.56f. // Works if denominator cast as float. // Works if broken up with function call //     such as printf(&quot;hello&quot;);. f1=f2/i; } void main(void) {  set_adc(1.0f); printf( &quot;f1 is %f\n&quot;, f1 ); } Additional query words: 6.00 7.00 8.00 8.00c 1.00 1.50
 * C7  /Ot
 * VC++ /Ot
 * 1) include 
 * 1) include 

Keywords : kb16bitonly

Issue type :

Technology : kbVCsearch kbAudDeveloper kbCVCComp