On Wed, May 24, 2017 at 2:33 PM, Eric Blake wrote: > On 05/24/2017 07:00 AM, Carl Fredrik Forsberg wrote: >> I am experiencing problems printing long int values under cygwin64 installed >> on a Windows 10 machine. >> >> Below is a test program followed by its output to demonstrate the problem. >> The program was initially written to demonstrate the output from lrint(), >> and developed further to demonstrate to myself how negative integers are >> tackled by printf type specifiers (e.g. %li, %ld etc). > > Are you compiling with -Wall, or even -Wformat? > >> >> My understanding is that lrint() should return a long int. However I am >> unable to get printf to print the correct number. Instead its output is >> treated as an unsigned integer. >> Any help or hints would be much appreciated. >> >> Regards >> Carl Fredrik >> >> #include <stdio.h> /* printf */ >> #include <math.h> /* lrint */ >> >> int main () >> { >> char text[64]; >> printf ( "int -2 = %i\n", -2 ); >> printf ( "int -1 = %i\n", -1 ); >> printf ( "int 0 = %i\n", 0 ); >> printf ( "int 1 = %i\n", 1 ); > > Okay so far. > >> printf ( "long int -2 = %li\n", -2 ); >> printf ( "long int -1 = %li\n", -1 ); > > Both buggy. You are passing an int through varargs, but then telling > printf to grab a long int. It may or may not work depending on ABI and > stack sizes and what not, but gcc will warn you that it is bogus. > >> printf ( "type cast -1 = %li\n", (long int)-1 ); >> printf ( "type cast lrint(-1.0) = %li\n", (long int)lrint(-1.0) ); >> printf ( "lrint(-1.0) = %li\n", lrint(-1.0) ); >> printf ( "lrint(1.0) = %li\n", lrint(1.0) ); > > Okay. > >> printf ( "long int 0 = %li\n", 0 ); >> printf ( "long int 1 = %li\n", 1 ); >> sprintf( text,"long int -1 = %li", -1 ); > > Buggy. > >> printf ( "Via sprintf: %s\n", text); > > Okay (well, if you overlook the fact that text was populated in a buggy > manner) > >> printf ( "size of long int: %i\n", sizeof(long int)); >> printf ( "size of int: %i\n", sizeof(int)); > > Buggy. size_t should be printed with %zi, not %i (since size_t and int > are not necessarily the same type). > >> return 0; >> } >> >> >> compiled by: >> gcc lrint_test.c -o lrint_test.exe > > Missing -Wall. Also, some platforms require the use of -lm to actually > link with lrint() (cygwin does not, though). > >> >> Output: >> >> int -2 = -2 >> int -1 = -1 >> int 0 = 0 >> int 1 = 1 >> long int -2 = 4294967294 >> long int -1 = 4294967295 > > Evidence of your bugs. > >> type cast -1 = -1 >> type cast lrint(-1.0) = 4294967295 > > Now that's an interesting one - it may be that cygwin1.dll actually has > buggy behavior in lrint(). In the source code, cygwin/math/lrint.c is > dropping down to assembly; it could very well be that the assembly code > is incorrectly truncating things at 32 bits (where it is just fine for > 32-bit Cygwin, but wrong for 64-bit): > > long lrint (double x) > { > long retval = 0L; > #if defined(_AMD64_) || defined(__x86_64__) || defined(_X86_) || > defined(__i386__) > __asm__ __volatile__ ("fistpl %0" : "=m" (retval) : "t" (x) : "st"); > #elif defined(__arm__) || defined(_ARM_) > retval = __lrint_internal(x); > #endif > return retval; > } > > But I'm not an assembly coding expert, so perhaps someone else will spot > the fix faster.
I took a look at this. The code in Cygwin looks fine, I think. But the assembly is wrong: 000000018015cd40 <lrint>: 18015cd40: 48 83 ec 28 sub $0x28,%rsp 18015cd44: f2 0f 11 44 24 08 movsd %xmm0,0x8(%rsp) 18015cd4a: dd 44 24 08 fldl 0x8(%rsp) 18015cd4e: db 5c 24 18 fistpl 0x18(%rsp) 18015cd52: 48 8b 44 24 18 mov 0x18(%rsp),%rax 18015cd57: 48 83 c4 28 add $0x28,%rsp 18015cd5b: c3 retq That last `mov` should be a `movq`. The result in $rsp+0x18 is correct (I checked in gdb), but the `mov` truncates it. Not sure if that's a gcc bug or what. Best, Erik -- Problem reports: http://cygwin.com/problems.html FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple