This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Signed division with rounding towards -infinity (and floating point rounding)
- From: Christopher Key <cjk32 at cam dot ac dot uk>
- To: gcc at gcc dot gnu dot org
- Date: Tue, 18 Sep 2007 13:39:24 +0100
- Subject: Signed division with rounding towards -infinity (and floating point rounding)
Hello,
Apologies if this has already been covered; I've searched the archives
and not found anything.
I have some code that needs to perform signed division by a power of two
with rounding towards minus infinity, i.e. it requires an arithmetic
right shift. Now in the C specification, right shifting a signed
integer is implementation defined. gcc's behaviour is implement this as
a right shift, but it's possible that the code will be compiled on
different platforms where the implemented behaviour is different.
What I'm looking for is a way of expressing myself that will always give
the correct behaviour on all c99 implementations, but that gcc will
recognise as being equivalent to an arithmetic right shift, and hence
implement as such.
The most concise form that I've found so far is:
const int d = 8; // 16, 32 etc
x = y / d - ((y % d < 0) ? 1 : 0)
although this still produces rather longer code (see example below).
On a similar point, is there a good way get floats rounded to the
nearest integer value rather than truncated. The following give the
correct rounding behaviour (I'm only interested in +ve values),
x = (int) (f + 0.5)
although gets compiled as an addition of 0.5 followed by a truncation
(again, see example).
Please note that I'm not expecting gcc to recognise these specific
cases, I'm just wondering if logic has been built in to recognise
certain specific constructs and optimise them appropriately.
Regards,
Chris
Example follows:
# gcc -v
Using built-in specs.
Target: i586-suse-linux
Configured with: ../configure --enable-threads=posix --prefix=/usr
--with-local-prefix=/usr/local --infodir=/usr/share/info
--mandir=/usr/share/man --libdir=/usr/lib --libexecdir=/usr/lib
--enable-languages=c,c++,objc,fortran,obj-c++,java,ada
--enable-checking=release --with-gxx-include-dir=/usr/include/c++/4.1.2
--enable-ssp --disable-libssp --disable-libgcj --with-slibdir=/lib
--with-system-zlib --enable-shared --enable-__cxa_atexit
--enable-libstdcxx-allocator=new --program-suffix=-4.1
--enable-version-specific-runtime-libs --without-system-libunwind
--with-cpu=generic --host=i586-suse-linux
Thread model: posix
gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)
# cat div.c
#include <stdio.h>
int div8(int x) { return (x / 8) - ((x % 8 < 0) ? 1 : 0); }
int sft3(int x) { return x>>3; }
int trnc(double d) { return (int) d; }
int rnd(double d) { return (int) (d + 0.5); }
int main (void) {
int x = -5;
fprintf(stderr, "%d\n", div8(x));
fprintf(stderr, "%d\n", div8(x));
fprintf(stderr, "%d\n", trnc(42.6));
fprintf(stderr, "%d\n", rnd(42.6));
return 0;
}
# gcc -O3 --save-temps div.c
#cat div.s
.file "div.c"
.text
.p2align 4,,15
.globl div8
.type div8, @function
div8:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
popl %ebp
movl %edx, %ecx
sarl $31, %ecx
shrl $29, %ecx
leal (%ecx,%edx), %edx
movl %edx, %eax
andl $7, %edx
subl %ecx, %edx
sarl $3, %eax
shrl $31, %edx
subl %edx, %eax
ret
.size div8, .-div8
.p2align 4,,15
.globl sft3
.type sft3, @function
sft3:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
popl %ebp
sarl $3, %eax
ret
.size sft3, .-sft3
.p2align 4,,15
.globl trnc
.type trnc, @function
trnc:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
fnstcw -2(%ebp)
fldl 8(%ebp)
movzwl -2(%ebp), %eax
movb $12, %ah
movw %ax, -4(%ebp)
fldcw -4(%ebp)
fistpl -8(%ebp)
fldcw -2(%ebp)
movl -8(%ebp), %eax
leave
ret
.size trnc, .-trnc
.section .rodata.cst4,"aM",@progbits,4
.align 4
.LC1:
.long 1056964608
.text
.p2align 4,,15
.globl rnd
.type rnd, @function
rnd:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
fnstcw -2(%ebp)
flds .LC1
faddl 8(%ebp)
movzwl -2(%ebp), %eax
movb $12, %ah
movw %ax, -4(%ebp)
fldcw -4(%ebp)
fistpl -8(%ebp)
fldcw -2(%ebp)
movl -8(%ebp), %eax
leave
ret
.size rnd, .-rnd
.section .rodata.str1.1,"aMS",@progbits,1
.LC3:
.string "%d\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $20, %esp
movl $-1, 8(%esp)
movl $.LC3, 4(%esp)
movl stderr, %eax
movl %eax, (%esp)
call fprintf
movl $-1, 8(%esp)
movl $.LC3, 4(%esp)
movl stderr, %eax
movl %eax, (%esp)
call fprintf
movl $42, 8(%esp)
movl $.LC3, 4(%esp)
movl stderr, %eax
movl %eax, (%esp)
call fprintf
movl $43, 8(%esp)
movl $.LC3, 4(%esp)
movl stderr, %eax
movl %eax, (%esp)
call fprintf
addl $20, %esp
xorl %eax, %eax
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
.section .note.GNU-stack,"",@progbits