This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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







Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]