Bug Report (Float --> Integer Inplace Conversion Precision Problem)
David Welling
dwelling@dsrnet.com
Mon May 7 06:57:00 GMT 2001
Originator
Joe Murray, John Casserly, Dave Welling
Organization
DSR (Digital Systems Research)
Confidential
No
Synopsis
Float --> Integer Inplace Conversion Precision Problem
Severity
Critical
Priority
high
Category
c
Class
The machine code generated by GCC is incorrect.
ice-on-legal-code
Release
Error occurs in:
GCC-2.9.5 and egcs 1.1.2
Environment
Various Machines using the INTEL processor.
Description
A problem was discovered when performing an inplace cast from an integer to
a float.
Consider the following program compiled with no options.
--------------------------------------------
#include <stdio.h>
int main(void) {
int iaa =28;
int ibb =80;
int icc =20;
int imm;
int imm0;
float fll;
float ftmp;
fll = (float)iaa / (float)ibb;
imm = ftmp = fll *(float)icc;
printf("Direct assign float to float = %.10f\n", ftmp);
printf("Inplace assign to int = %d\n", imm);
imm0 = ftmp;
printf("Direct assign float to int = %d\n", imm0);
return (0);
}
------------------------
Using GCC (egcs1.1.2) compiler, the result is:
Direct assign float to float = 7
Inplace assign to int = 6
Direct assign float to int = 7
The inplace assignment should result in 7. It does not because the value is
copied straight from the FPU 80 bit register for the integer imm. This
truncates the value to 6. In the case of imm0 the value has been corrected
to 32 bit precision because the value of ftmp was already moved from the FPU
register to memory.
The following lists the aforementioned code in assembly.
----------------------------------------------------------
.file "simple.c"
.version "01.01"
# GNU C version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)
(i386-redhat-linux) compiled by GNU C version egcs-2.91.66 19990314/Linux
(egcs-1.1.2 release).
# options passed: -march=i686 -mcpu=i686 -fverbose-asm
# options enabled: -fpeephole -ffunction-cse -fkeep-static-consts
# -fpcc-struct-return -fcommon -fverbose-asm -fgnu-linker -fargument-alias
# -m80387 -mhard-float -mno-soft-float -mieee-fp -mfp-ret-in-387
# -mschedule-prologue -mcpu=i686 -march=i686
gcc2_compiled.:
.section .rodata
.align 32
.LC0:
.string "Direct assign float to float = %.10f\n"
.align 32
.LC1:
.string " Inplace assign float to int = %d\n"
.align 32
.LC2:
.string " Direct assign float to int = %d\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $40,%esp
movl $28,-4(%ebp)
movl $80,-8(%ebp)
movl $20,-12(%ebp)
fildl -4(%ebp)
fildl -8(%ebp)
fdivrp %st,%st(1)
fstps -24(%ebp)
fildl -12(%ebp)
fmuls -24(%ebp)
fsts -28(%ebp)
fnstcw -32(%ebp)
movl -32(%ebp),%edx
movb $12,%dh
movl %edx,-40(%ebp)
fldcw -40(%ebp)
fistpl -16(%ebp)
fldcw -32(%ebp)
flds -28(%ebp)
subl $8,%esp
fstpl (%esp)
pushl $.LC0
call printf
addl $12,%esp
movl -16(%ebp),%eax
pushl %eax
pushl $.LC1
call printf
addl $8,%esp
flds -28(%ebp)
fnstcw -32(%ebp)
movl -32(%ebp),%edx
movb $12,%dh
movl %edx,-40(%ebp)
fldcw -40(%ebp)
fistpl -20(%ebp)
fldcw -32(%ebp)
movl -20(%ebp),%eax
pushl %eax
pushl $.LC2
call printf
addl $8,%esp
xorl %eax,%eax
jmp .L1
.p2align 4,,7
.L1:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)"
-------------------------------------------------------------
The following lists the inplace cast from the above assembly code
fnstcw -32(%ebp) # Get the FPU Control Word
movl -32(%ebp),%edx
movb $12,%dh # Set the Upper byte to 12 or 0x0c
# (32 bit Resolution Truncate
Mode)
movl %edx,-40(%ebp)
fldcw -40(%ebp) # Store the New FPU Control Word
fistpl -16(%ebp) # Perform the Cast
fldcw -32(%ebp) # Restore the Original Control Word
The problem is that the fistpl instruction will not correct the 80 bit value
to the meaningful 32 bit resolution of the integer. This causes the 80 bit
value to be truncated.
How-To-Repeat
Run the above program.
Fix
One solution is to copy the floating point value onto the variable stack and
back to the Floating Point Register before the FPU Register is modified and
before the fistpl instruction is given. This causes the float to be rounded
to the nearest 32/64 bit value (depending on the size of the float i.e.
float or double).
For Example the above segment would change to the following for a 32 bit
float casted to and integer.
subl $4, %esp # Make Room on the Varible Stack
fstps (%esp) # Copy the value onto the variable stack
flds (%esp) # Copy the value back to the FPU Register
addl $4, %esp # Set the Variable Stack to its original
Position
fnstcw -32(%ebp) # Get the FPU Control Word
movl -32(%ebp),%edx
movb $12,%dh # Set the Upper byte to 12 or 0x0c
# (32 bit Resolution Truncate
Mode)
movl %edx,-40(%ebp)
fldcw -40(%ebp) # Store the New FPU Control Word
fistpl -16(%ebp) # Perform the Cast
fldcw -32(%ebp) # Restore the Original Control Word
This can be accomplished by added the following code to the gcc source file
i386.c and the routine "output_fix_trunc" for gcc(egcs1.1.2).
if (GET_MODE(operands[1]) == SFmode)
{
xops[0] = stack_pointer_rtx;
xops[1] = GEN_INT(4);
output_asm_insn ("subl %1,%0", xops);
output_asm_insn ("fstps (%0)", xops);
output_asm_insn ("flds (%0)", xops);
output_asm_insn ("addl %1,%0", xops);
}
else if (GET_MODE(operands[1]) == DFmode)
{
xops[0] = stack_pointer_rtx;
xops[1] = GEN_INT(8);
output_asm_insn ("subl %1, %0", xops);
output_asm_insn ("fstpl (%0)", xops);
output_asm_insn ("fldl (%0)", xops);
output_asm_insn ("addl %1, %0", xops);
}
or adding the following code to the gcc source file i386.c and the routine
"output_fix_trunc" for gcc-2.9.5
if (GET_MODE (operands[1]) == SFmode)
{
xops[0] = stack_pointer_rtx;
xops[1] = GEN_INT(4);
output_asm_insn (AS2(subl,%1,%0), xops);
output_asm_insn (AS1(fstps,(%0)), xops);
output_asm_insn (AS1(flds,(%0)), xops);
output_asm_insn (AS2(addl,%1,%0), xops);
}
else if (GET_MODE (operands[1]) == DFmode)
xops[0] = stack_pointer_rtx;
xops[1] = GEN_INT(8);
output_asm_insn (AS2(subl,%1,%0), xops);
output_asm_insn (AS1(fstpl,(%0)), xops);
output_asm_insn (AS1(fldl,(%0)), xops);
output_asm_insn (AS2(addl,%1,%0), xops);
}
More information about the Gcc-bugs
mailing list