c++gccprecisionpowerpc

long double cast to int data type effects double division precision (powerpc, ppc32)


Consider following code:

#include <iostream>

int main() {
    double a=54;
    double b=10;
    long double g=3;
    std::cout << a/b << std::endl;
    int i=g;
    std::cout << a/b << std::endl;
    return 0;
}

Compiled on x86 platform gives result as expected:

$ ./a.out
5.4
5.4

Compiled on PowerPc (ppc32) platform result is a bit different:

# ./a.out.ppc
5.4
5.39999

It seems that if 'long double' is casted to 'int' data type it effects on 'double' division precision.

I don't know why it is happening and how to fix it. Any help please?

Platform: Linux
Compiler:
$ /usr/bin/powerpc-e500v2-linux-gnu-g++ -v
Using built-in specs.
COLLECT_GCC=/usr/bin/powerpc-e500v2-linux-gnu-g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/powerpc-e500v2-linux-gnu/9.4.0/lto-wrapper
Target: powerpc-e500v2-linux-gnu
Configured with: /var/tmp/portage/cross-powerpc-e500v2-linux-gnu/gcc-9.4.0/work/gcc-9.4.0/configure --host=x86_64-pc-linux-gnu --target=powerpc-e500v2-linux-gnu --build=x86_64-pc-linux-gnu --prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/powerpc-e500v2-linux-gnu/gcc-bin/9.4.0 --includedir=/usr/lib/gcc/powerpc-e500v2-linux-gnu/9.4.0/include --datadir=/usr/share/gcc-data/powerpc-e500v2-linux-gnu/9.4.0 --mandir=/usr/share/gcc-data/powerpc-e500v2-linux-gnu/9.4.0/man --infodir=/usr/share/gcc-data/powerpc-e500v2-linux-gnu/9.4.0/info --with-gxx-include-dir=/usr/lib/gcc/powerpc-e500v2-linux-gnu/9.4.0/include/g++-v9 --with-python-dir=/share/gcc-data/powerpc-e500v2-linux-gnu/9.4.0/python --enable-languages=c,c++,fortran --enable-obsolete --enable-secureplt --disable-werror --with-system-zlib --enable-nls --without-included-gettext --disable-libunwind-exceptions --enable-checking=release --with-bugurl=https://bugs.gentoo.org/ --with-pkgversion='Gentoo 9.4.0 p1' --disable-esp --enable-libstdcxx-time --enable-poison-system-directories --with-sysroot=/usr/powerpc-e500v2-linux-gnu --disable-bootstrap --enable-__cxa_atexit --enable-clocale=gnu --disable-multilib --disable-fixed-point --enable-e500-double --enable-targets=all --enable-libgomp --disable-libssp --disable-libada --disable-systemtap --disable-vtable-verify --disable-libvtv --enable-lto --without-isl --enable-default-pie --enable-default-ssp
Thread model: posix
gcc version 9.4.0 (Gentoo 9.4.0 p1)

Pure C version & assembly:

$ cat main2.cpp
#include <stdio.h>

int main() {
    double a=54;
    double b=10;
    long double g=3;
    printf("%f\n", a/b);
    int i=g;
    printf("%f\n", a/b);
    return 0;
}

$ /usr/bin/powerpc-e500v2-linux-gnu-gcc -S main2.cpp -o a.out.asm

$ cat a.out.asm
        .file   "main2.cpp"
        .machine ppc
        .section        ".text"
        .section        .rodata
        .align 2
.LC6:
        .string "%f\n"
        .section        ".got2","aw"
        .align 2
.LCTOC1 = .+32768
.LC1:
        .long .LC0
.LC3:
        .long .LC2
.LC5:
        .long .LC4
.LC7:
        .long .LC6
        .section        ".text"
        .align 2
        .globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        stwu 1,-96(1)
        .cfi_def_cfa_offset 96
        mflr 0
        stw 0,100(1)
        stw 30,88(1)
        stw 31,92(1)
        .cfi_offset 65, 4
        .cfi_offset 30, -8
        .cfi_offset 31, -4
        mr 31,1
        .cfi_def_cfa_register 31
        bcl 20,31,.L3
.L3:
        mflr 30
        addis 30,30,.LCTOC1-.L3@ha
        addi 30,30,.LCTOC1-.L3@l
        lwz 9,.LC1-.LCTOC1(30)
        lfd 0,0(9)
        stfd 0,48(31)
        lwz 9,.LC3-.LCTOC1(30)
        lfd 0,0(9)
        stfd 0,56(31)
        lwz 9,.LC5-.LCTOC1(30)
        lfd 0,0(9)
        lfd 1,8(9)
        addi 9,31,64
        stfd 0,0(9)
        stfd 1,8(9)
        lfd 12,48(31)
        lfd 0,56(31)
        fdiv 0,12,0
        fmr 1,0
        lwz 3,.LC7-.LCTOC1(30)
        creqv 6,6,6
        bl printf+32768@plt
        addi 9,31,64
        lfd 0,0(9)
        lfd 1,8(9)
        mffs 12
        mtfsb1 31
        mtfsb0 30
        fadd 0,0,1
        mtfsf 1,12
        fctiwz 0,0
        stfd 0,24(31)
        lwz 9,28(31)
        stw 9,44(31)
        lfd 12,48(31)
        lfd 0,56(31)
        fdiv 0,12,0
        fmr 1,0
        lwz 3,.LC7-.LCTOC1(30)
        creqv 6,6,6
        bl printf+32768@plt
        li 9,0
        mr 3,9
        addi 11,31,96
        lwz 0,4(11)
        mtlr 0
        lwz 30,-8(11)
        lwz 31,-4(11)
        .cfi_def_cfa 11, 0
        mr 1,11
        .cfi_restore 31
        .cfi_restore 30
        .cfi_def_cfa_register 1
        blr
        .cfi_endproc
.LFE0:
        .size   main,.-main
        .section        .rodata
        .align 3
.LC0:
        .long   1078657024
        .long   0
        .align 3
.LC2:
        .long   1076101120
        .long   0
        .align 4
.LC4:
        .long   1074266112
        .long   0
        .long   0
        .long   0
        .ident  "GCC: (Gentoo 9.4.0 p1) 9.4.0"
        .gnu_attribute 4, 5
        .section        .note.GNU-stack,"",@progbits

Solution

  • Ok. I found the bug I think:

    GCC assembly of 'long double' to 'int' casting:

      7c:   39 3f 00 40     addi    r9,r31,64
      80:   c8 09 00 00     lfd     f0,0(r9)
      84:   c8 29 00 08     lfd     f1,8(r9)
      88:   fd 80 04 8e     mffs    f12
      8c:   ff e0 00 4c     mtfsb1  31
      90:   ff c0 00 8c     mtfsb0  30
      94:   fc 00 08 2a     fadd    f0,f0,f1
      98:   fc 02 65 8e     mtfsf   1,f12
      9c:   fc 00 00 1e     fctiwz  f0,f0
      a0:   d8 1f 00 18     stfd    f0,24(r31)
      a4:   81 3f 00 1c     lwz     r9,28(r31)
      a8:   91 3f 00 2c     stw     r9,44(r31)
    

    This is crucial:

    mtfsb1  31
    mtfsb0  30
    

    It is manipulating on Rounding bits. These bits are not restored after 'cast' operation. IMO it is GCC bug but I cannot find any reference.