[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#799953: gcc-4.9: incorrect double to integer conversion on i386



Control: forwarded -1 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323

On Thu, 24 Sep 2015 18:42:14 +0200 Miroslav Urbanek <mu@miroslavurbanek.com> wrote:
> I believe I've found a bug in GCC that affects plymouth and maybe
> other packages on i386. The following minimal code produces an
> incorrect result.

At least it's an unexpected result ... seems like you are running in some excess precision issue, probably PR323


I can also reproduce it on amd64 when explicitly using the 387 FPU:

$ gcc-5 -O1 -mfpmath=387 -o test test.c && ./test 100
f = 9999999.000000
d = 999999872.000000
g = 999999872.000000
fractional part of d is 28.000000

and I cannot reproduce it on armhf (in qemu).

gcc-5 -O2 -o test test.c && ./test 100
f = 9999999.000000
d = 999999872.000000
g = 999999872.000000
fractional part of d is 0.000000


amd64 disassembly with -mfpmath=387:
-O0:

0000000000400546 <main>:
#  400546:       55                      push   %rbp
#  400547:       48 89 e5                mov    %rsp,%rbp
#  40054a:       48 83 ec 40             sub    $0x40,%rsp
#  40054e:       89 7d dc                mov    %edi,-0x24(%rbp)
#  400551:       48 89 75 d0             mov    %rsi,-0x30(%rbp)
  400555:       d9 05 6d 01 00 00       flds   0x16d(%rip)        # 4006c8 <_IO_stdin_used+0x38>
  40055b:       d9 5d fc                fstps  -0x4(%rbp)		= f (float)
#  40055e:       48 8b 45 d0             mov    -0x30(%rbp),%rax
#  400562:       48 83 c0 08             add    $0x8,%rax
#  400566:       48 8b 00                mov    (%rax),%rax
#  400569:       48 89 c7                mov    %rax,%rdi
#  40056c:       e8 cf fe ff ff          callq  400440 <atoi@plt>
  400571:       89 45 c8                mov    %eax,-0x38(%rbp)         = argv[1] (int)
  400574:       db 45 c8                fildl  -0x38(%rbp)              int_to_extended(argv(1))
  400577:       d8 4d fc                fmuls  -0x4(%rbp)
  40057a:       dd 5d f0                fstpl  -0x10(%rbp)  		= d (double)
  40057d:       dd 45 f0                fldl   -0x10(%rbp)
  400580:       d9 5d ec                fstps  -0x14(%rbp)  		= g (float)
  400583:       f2 0f 10 45 f0          movsd  -0x10(%rbp),%xmm0
  400588:       f2 0f 2c c0             cvttsd2si %xmm0,%eax            (int)d (double2int with truncation)
  40058c:       89 45 e8                mov    %eax,-0x18(%rbp)		= i (int)
#  40058f:       d9 45 fc                flds   -0x4(%rbp)		print f
#  400592:       dd 5d c8                fstpl  -0x38(%rbp)
#  400595:       f2 0f 10 45 c8          movsd  -0x38(%rbp),%xmm0
#  40059a:       bf 94 06 40 00          mov    $0x400694,%edi
#  40059f:       b8 01 00 00 00          mov    $0x1,%eax
#  4005a4:       e8 67 fe ff ff          callq  400410 <printf@plt>
#  4005a9:       48 8b 45 f0             mov    -0x10(%rbp),%rax	print d
#  4005ad:       48 89 45 c8             mov    %rax,-0x38(%rbp)
#  4005b1:       f2 0f 10 45 c8          movsd  -0x38(%rbp),%xmm0
#  4005b6:       bf 9c 06 40 00          mov    $0x40069c,%edi
#  4005bb:       b8 01 00 00 00          mov    $0x1,%eax
#  4005c0:       e8 4b fe ff ff          callq  400410 <printf@plt>
#  4005c5:       d9 45 ec                flds   -0x14(%rbp)		print g
#  4005c8:       dd 5d c8                fstpl  -0x38(%rbp)
#  4005cb:       f2 0f 10 45 c8          movsd  -0x38(%rbp),%xmm0
#  4005d0:       bf a4 06 40 00          mov    $0x4006a4,%edi
#  4005d5:       b8 01 00 00 00          mov    $0x1,%eax
#  4005da:       e8 31 fe ff ff          callq  400410 <printf@plt>
  4005df:       db 45 e8                fildl  -0x18(%rbp)		int_to_extended(i)
  4005e2:       dd 45 f0                fldl   -0x10(%rbp)		double_ro_extended(d)
  4005e5:       de e1                   fsubp  %st,%st(1)
  4005e7:       dd 5d c8                fstpl  -0x38(%rbp)		= d-i
#  4005ea:       f2 0f 10 45 c8          movsd  -0x38(%rbp),%xmm0	print d-i
#  4005ef:       bf ac 06 40 00          mov    $0x4006ac,%edi
#  4005f4:       b8 01 00 00 00          mov    $0x1,%eax
#  4005f9:       e8 12 fe ff ff          callq  400410 <printf@plt>
#  4005fe:       90                      nop
#  4005ff:       c9                      leaveq 
#  400600:       c3                      retq   

-O1:

0000000000400546 <main>:
#  400546:       48 83 ec 28             sub    $0x28,%rsp
#  40054a:       48 8b 7e 08             mov    0x8(%rsi),%rdi
#  40054e:       ba 0a 00 00 00          mov    $0xa,%edx
#  400553:       be 00 00 00 00          mov    $0x0,%esi
#  400558:       e8 e3 fe ff ff          callq  400440 <strtol@plt>
  40055d:       48 89 44 24 08          mov    %rax,0x8(%rsp)		= argv[1] (long)
  400562:       db 44 24 08             fildl  0x8(%rsp)		int_to_extended(argv(1))	fpu stack: argv(1)
  400566:       d9 05 4c 01 00 00       flds   0x14c(%rip)        # 4006b8 <_IO_stdin_used+0x38>	fpu stack: f, argv(1)
  40056c:       dc c9                   fmul   %st,%st(1)		fpu stack: f, d
  40056e:       d9 c9                   fxch   %st(1)			fpu stack: d, f
  400570:       d9 54 24 08             fsts   0x8(%rsp)		= g (float)
  400574:       dd 5c 24 10             fstpl  0x10(%rsp)		= d (double)
#  400578:       dd 5c 24 18             fstpl  0x18(%rsp)		print f
#  40057c:       f2 0f 10 44 24 18       movsd  0x18(%rsp),%xmm0
#  400582:       bf 84 06 40 00          mov    $0x400684,%edi
#  400587:       b8 01 00 00 00          mov    $0x1,%eax
#  40058c:       e8 7f fe ff ff          callq  400410 <printf@plt>
#  400591:       d9 44 24 08             flds   0x8(%rsp)		print g !!!
#  400595:       dd 5c 24 18             fstpl  0x18(%rsp)
#  400599:       f2 0f 10 44 24 18       movsd  0x18(%rsp),%xmm0
#  40059f:       bf 8c 06 40 00          mov    $0x40068c,%edi
#  4005a4:       b8 01 00 00 00          mov    $0x1,%eax
#  4005a9:       e8 62 fe ff ff          callq  400410 <printf@plt>
#  4005ae:       d9 44 24 08             flds   0x8(%rsp)		print g
#  4005b2:       dd 5c 24 18             fstpl  0x18(%rsp)
#  4005b6:       f2 0f 10 44 24 18       movsd  0x18(%rsp),%xmm0
#  4005bc:       bf 94 06 40 00          mov    $0x400694,%edi
#  4005c1:       b8 01 00 00 00          mov    $0x1,%eax
#  4005c6:       e8 45 fe ff ff          callq  400410 <printf@plt>
  4005cb:       f3 0f 2c 44 24 08       cvttss2si 0x8(%rsp),%eax	(int)g (float2int with truncation)
  4005d1:       89 44 24 08             mov    %eax,0x8(%rsp)		= i (int)
  4005d5:       db 44 24 08             fildl  0x8(%rsp)		int_to_extended(i)
  4005d9:       dc 6c 24 10             fsubrl 0x10(%rsp)
  4005dd:       dd 5c 24 08             fstpl  0x8(%rsp)		= d-i
#  4005e1:       f2 0f 10 44 24 08       movsd  0x8(%rsp),%xmm0		print d-i
#  4005e7:       bf 9c 06 40 00          mov    $0x40069c,%edi
#  4005ec:       b8 01 00 00 00          mov    $0x1,%eax
#  4005f1:       e8 1a fe ff ff          callq  400410 <printf@plt>
#  4005f6:       48 83 c4 28             add    $0x28,%rsp
#  4005fa:       c3                      retq   

What's the result type of 
  float * int
? Definitively not double, but float. (Just the intermediate result in the x87 fpu has extended precision.)

So I don't think the compiler is wrong considering d and g to contain the same information, since the same *float* value was assigned to both variables. Therefore it considers (int)d and int(g) to be the same, ignoring that there are some excess precision bits in the double representation which cause your trouble.

So it's actually using i = (int)g and therefore computing d-(int)g;

So what you are actually computing is the difference of the float and double representation of 999999900 (which cannot be represented as float exactly.)

printf("%f\n", 999999900.0d - 999999900.0f);
==> 28.000000


Just for completeness, gcc-4.6 -O1:

Same code, except for one instruction cvttss2si vs. cvttsd2si.

So it's really using i = (int)d and computing d-(int)d.

0000000000400522 <main>:
  400522:       48 83 ec 28             sub    $0x28,%rsp
  400526:       48 8b 7e 08             mov    0x8(%rsi),%rdi
  40052a:       ba 0a 00 00 00          mov    $0xa,%edx
  40052f:       be 00 00 00 00          mov    $0x0,%esi
  400534:       e8 17 ff ff ff          callq  400450 <strtol@plt>
  400539:       89 44 24 1c             mov    %eax,0x1c(%rsp)
  40053d:       db 44 24 1c             fildl  0x1c(%rsp)
  400541:       d9 05 89 01 00 00       flds   0x189(%rip)        # 4006d0 <_IO_stdin_used+0x38>
  400547:       dc c9                   fmul   %st,%st(1)
  400549:       d9 c9                   fxch   %st(1)
  40054b:       d9 54 24 08             fsts   0x8(%rsp)
  40054f:       dd 5c 24 10             fstpl  0x10(%rsp)
  400553:       dd 1c 24                fstpl  (%rsp)
  400556:       f2 0f 10 04 24          movsd  (%rsp),%xmm0
  40055b:       bf 9c 06 40 00          mov    $0x40069c,%edi
  400560:       b8 01 00 00 00          mov    $0x1,%eax
  400565:       e8 b6 fe ff ff          callq  400420 <printf@plt>
  40056a:       d9 44 24 08             flds   0x8(%rsp)
  40056e:       dd 1c 24                fstpl  (%rsp)
  400571:       f2 0f 10 04 24          movsd  (%rsp),%xmm0
  400576:       bf a4 06 40 00          mov    $0x4006a4,%edi
  40057b:       b8 01 00 00 00          mov    $0x1,%eax
  400580:       e8 9b fe ff ff          callq  400420 <printf@plt>
  400585:       d9 44 24 08             flds   0x8(%rsp)
  400589:       dd 1c 24                fstpl  (%rsp)
  40058c:       f2 0f 10 04 24          movsd  (%rsp),%xmm0
  400591:       bf ac 06 40 00          mov    $0x4006ac,%edi
  400596:       b8 01 00 00 00          mov    $0x1,%eax
  40059b:       e8 80 fe ff ff          callq  400420 <printf@plt>
* 4005a0:       f2 0f 2c 44 24 10       cvttsd2si 0x10(%rsp),%eax	***
  4005a6:       89 44 24 1c             mov    %eax,0x1c(%rsp)
  4005aa:       db 44 24 1c             fildl  0x1c(%rsp)
  4005ae:       dc 6c 24 10             fsubrl 0x10(%rsp)
  4005b2:       dd 5c 24 08             fstpl  0x8(%rsp)
  4005b6:       f2 0f 10 44 24 08       movsd  0x8(%rsp),%xmm0
  4005bc:       bf b4 06 40 00          mov    $0x4006b4,%edi
  4005c1:       b8 01 00 00 00          mov    $0x1,%eax
  4005c6:       e8 55 fe ff ff          callq  400420 <printf@plt>
  4005cb:       48 83 c4 28             add    $0x28,%rsp
  4005cf:       c3                      retq   


And btw, it should be easy to fix:

$ gcc-5 -O1 -mfpmath=387 -std=c99 -o test test.c && ./test 100
f = 9999999.000000
d = 999999900.000000
g = 999999872.000000
fractional part of d is 0.000000


Andreas


Reply to: