I compiled the code below with: /opt/local/bin/gcc-mp-4.3 -Wall -O3 -fno-inline main.i There were no warnings or errors from the compiler. The result of executing a.out was: z.imag is 0.000000; expected -0.000000 If the -O3 option is dropped, or -fno-builtin is added, then the output is as expected: z.imag is -0.000000; expected -0.000000 It looks as though the twin calls to cos and sin are being optimized to a single call to (the gcc builtin version of) cexp, and that cexp(-0.0) incorrectly throws away the sign of the zero. Here's the output of gcc -v: Using built-in specs. Target: i386-apple-darwin9.5.0 Configured with: ../gcc-4.3.2/configure --prefix=/opt/local --enable-languages=c,c++,objc,obj-c++,java,fortran --libdir=/opt/local/lib/gcc43 --includedir=/opt/local/include/gcc43 --infodir=/opt/local/share/info --mandir=/opt/local/share/man --with-local-prefix=/opt/local --with-system-zlib --disable-nls --program-suffix=-mp-4.3 --with-gxx-include-dir=/opt/local/include/gcc43/c++/ --with-gmp=/opt/local --with-mpfr=/opt/local Thread model: posix gcc version 4.3.2 (GCC) and here are the contents of main.i: # 1 "main.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "main.c" # 1 "/usr/include/stdio.h" 1 3 4 # 64 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/_types.h" 1 3 4 # 27 "/usr/include/_types.h" 3 4 # 1 "/usr/include/sys/_types.h" 1 3 4 # 32 "/usr/include/sys/_types.h" 3 4 # 1 "/usr/include/sys/cdefs.h" 1 3 4 # 33 "/usr/include/sys/_types.h" 2 3 4 # 1 "/usr/include/machine/_types.h" 1 3 4 # 34 "/usr/include/machine/_types.h" 3 4 # 1 "/usr/include/i386/_types.h" 1 3 4 # 37 "/usr/include/i386/_types.h" 3 4 typedef signed char __int8_t; typedef unsigned char __uint8_t; typedef short __int16_t; typedef unsigned short __uint16_t; typedef int __int32_t; typedef unsigned int __uint32_t; typedef long long __int64_t; typedef unsigned long long __uint64_t; typedef long __darwin_intptr_t; typedef unsigned int __darwin_natural_t; # 70 "/usr/include/i386/_types.h" 3 4 typedef int __darwin_ct_rune_t; typedef union { char __mbstate8[128]; long long _mbstateL; } __mbstate_t; typedef __mbstate_t __darwin_mbstate_t; typedef int __darwin_ptrdiff_t; typedef long unsigned int __darwin_size_t; typedef __builtin_va_list __darwin_va_list; typedef int __darwin_wchar_t; typedef __darwin_wchar_t __darwin_rune_t; typedef int __darwin_wint_t; typedef unsigned long __darwin_clock_t; typedef __uint32_t __darwin_socklen_t; typedef long __darwin_ssize_t; typedef long __darwin_time_t; # 35 "/usr/include/machine/_types.h" 2 3 4 # 34 "/usr/include/sys/_types.h" 2 3 4 # 58 "/usr/include/sys/_types.h" 3 4 struct __darwin_pthread_handler_rec { void (*__routine)(void *); void *__arg; struct __darwin_pthread_handler_rec *__next; }; struct _opaque_pthread_attr_t { long __sig; char __opaque[36]; }; struct _opaque_pthread_cond_t { long __sig; char __opaque[24]; }; struct _opaque_pthread_condattr_t { long __sig; char __opaque[4]; }; struct _opaque_pthread_mutex_t { long __sig; char __opaque[40]; }; struct _opaque_pthread_mutexattr_t { long __sig; char __opaque[8]; }; struct _opaque_pthread_once_t { long __sig; char __opaque[4]; }; struct _opaque_pthread_rwlock_t { long __sig; char __opaque[124]; }; struct _opaque_pthread_rwlockattr_t { long __sig; char __opaque[12]; }; struct _opaque_pthread_t { long __sig; struct __darwin_pthread_handler_rec *__cleanup_stack; char __opaque[596]; }; # 94 "/usr/include/sys/_types.h" 3 4 typedef __int64_t __darwin_blkcnt_t; typedef __int32_t __darwin_blksize_t; typedef __int32_t __darwin_dev_t; typedef unsigned int __darwin_fsblkcnt_t; typedef unsigned int __darwin_fsfilcnt_t; typedef __uint32_t __darwin_gid_t; typedef __uint32_t __darwin_id_t; typedef __uint64_t __darwin_ino64_t; typedef __uint32_t __darwin_ino_t; typedef __darwin_natural_t __darwin_mach_port_name_t; typedef __darwin_mach_port_name_t __darwin_mach_port_t; typedef __uint16_t __darwin_mode_t; typedef __int64_t __darwin_off_t; typedef __int32_t __darwin_pid_t; typedef struct _opaque_pthread_attr_t __darwin_pthread_attr_t; typedef struct _opaque_pthread_cond_t __darwin_pthread_cond_t; typedef struct _opaque_pthread_condattr_t __darwin_pthread_condattr_t; typedef unsigned long __darwin_pthread_key_t; typedef struct _opaque_pthread_mutex_t __darwin_pthread_mutex_t; typedef struct _opaque_pthread_mutexattr_t __darwin_pthread_mutexattr_t; typedef struct _opaque_pthread_once_t __darwin_pthread_once_t; typedef struct _opaque_pthread_rwlock_t __darwin_pthread_rwlock_t; typedef struct _opaque_pthread_rwlockattr_t __darwin_pthread_rwlockattr_t; typedef struct _opaque_pthread_t *__darwin_pthread_t; typedef __uint32_t __darwin_sigset_t; typedef __int32_t __darwin_suseconds_t; typedef __uint32_t __darwin_uid_t; typedef __uint32_t __darwin_useconds_t; typedef unsigned char __darwin_uuid_t[16]; # 28 "/usr/include/_types.h" 2 3 4 typedef int __darwin_nl_item; typedef int __darwin_wctrans_t; typedef unsigned long __darwin_wctype_t; # 65 "/usr/include/stdio.h" 2 3 4 typedef __darwin_va_list va_list; typedef __darwin_off_t off_t; typedef __darwin_size_t size_t; typedef __darwin_off_t fpos_t; # 98 "/usr/include/stdio.h" 3 4 struct __sbuf { unsigned char *_base; int _size; }; struct __sFILEX; # 132 "/usr/include/stdio.h" 3 4 typedef struct __sFILE { unsigned char *_p; int _r; int _w; short _flags; short _file; struct __sbuf _bf; int _lbfsize; void *_cookie; int (*_close)(void *); int (*_read) (void *, char *, int); fpos_t (*_seek) (void *, fpos_t, int); int (*_write)(void *, const char *, int); struct __sbuf _ub; struct __sFILEX *_extra; int _ur; unsigned char _ubuf[3]; unsigned char _nbuf[1]; struct __sbuf _lb; int _blksize; fpos_t _offset; } FILE; extern FILE *__stdinp; extern FILE *__stdoutp; extern FILE *__stderrp; # 248 "/usr/include/stdio.h" 3 4 void clearerr(FILE *); int fclose(FILE *); int feof(FILE *); int ferror(FILE *); int fflush(FILE *); int fgetc(FILE *); int fgetpos(FILE * , fpos_t *); char *fgets(char * , int, FILE *); FILE *fopen(const char * , const char * ); int fprintf(FILE * , const char * , ...) ; int fputc(int, FILE *); int fputs(const char * , FILE * ) __asm("_" "fputs" "$UNIX2003"); size_t fread(void * , size_t, size_t, FILE * ); FILE *freopen(const char * , const char * , FILE * ) __asm("_" "freopen" "$UNIX2003"); int fscanf(FILE * , const char * , ...) ; int fseek(FILE *, long, int); int fsetpos(FILE *, const fpos_t *); long ftell(FILE *); size_t fwrite(const void * , size_t, size_t, FILE * ) __asm("_" "fwrite" "$UNIX2003"); int getc(FILE *); int getchar(void); char *gets(char *); extern const int sys_nerr; extern const char *const sys_errlist[]; void perror(const char *); int printf(const char * , ...) ; int putc(int, FILE *); int putchar(int); int puts(const char *); int remove(const char *); int rename (const char *, const char *); void rewind(FILE *); int scanf(const char * , ...) ; void setbuf(FILE * , char * ); int setvbuf(FILE * , char * , int, size_t); int sprintf(char * , const char * , ...) ; int sscanf(const char * , const char * , ...) ; FILE *tmpfile(void); char *tmpnam(char *); int ungetc(int, FILE *); int vfprintf(FILE * , const char * , va_list) ; int vprintf(const char * , va_list) ; int vsprintf(char * , const char * , va_list) ; int asprintf(char **, const char *, ...) ; int vasprintf(char **, const char *, va_list) ; char *ctermid(char *); char *ctermid_r(char *); FILE *fdopen(int, const char *); char *fgetln(FILE *, size_t *); int fileno(FILE *); void flockfile(FILE *); const char *fmtcheck(const char *, const char *); int fpurge(FILE *); int fseeko(FILE *, off_t, int); off_t ftello(FILE *); int ftrylockfile(FILE *); void funlockfile(FILE *); int getc_unlocked(FILE *); int getchar_unlocked(void); int getw(FILE *); int pclose(FILE *); FILE *popen(const char *, const char *); int putc_unlocked(int, FILE *); int putchar_unlocked(int); int putw(int, FILE *); void setbuffer(FILE *, char *, int); int setlinebuf(FILE *); int snprintf(char * , size_t, const char * , ...) ; char *tempnam(const char *, const char *) __asm("_" "tempnam" "$UNIX2003"); int vfscanf(FILE * , const char * , va_list) ; int vscanf(const char * , va_list) ; int vsnprintf(char * , size_t, const char * , va_list) ; int vsscanf(const char * , const char * , va_list) ; FILE *zopen(const char *, const char *, int); FILE *funopen(const void *, int (*)(void *, char *, int), int (*)(void *, const char *, int), fpos_t (*)(void *, fpos_t, int), int (*)(void *)); # 371 "/usr/include/stdio.h" 3 4 int __srget(FILE *); int __svfscanf(FILE *, const char *, va_list) ; int __swbuf(int, FILE *); static __inline int __sputc(int _c, FILE *_p) { if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) return (*_p->_p++ = _c); else return (__swbuf(_c, _p)); } # 2 "main.c" 2 # 1 "/usr/include/math.h" 1 3 4 # 28 "/usr/include/math.h" 3 4 # 1 "/usr/include/architecture/i386/math.h" 1 3 4 # 49 "/usr/include/architecture/i386/math.h" 3 4 typedef float float_t; typedef double double_t; # 83 "/usr/include/architecture/i386/math.h" 3 4 enum { _FP_NAN = 1, _FP_INFINITE = 2, _FP_ZERO = 3, _FP_NORMAL = 4, _FP_SUBNORMAL = 5, _FP_SUPERNORMAL = 6 }; # 117 "/usr/include/architecture/i386/math.h" 3 4 extern unsigned int __math_errhandling ( void ); # 137 "/usr/include/architecture/i386/math.h" 3 4 extern int __fpclassifyf(float ); extern int __fpclassifyd(double ); extern int __fpclassify (long double); # 172 "/usr/include/architecture/i386/math.h" 3 4 static __inline__ int __inline_isfinitef (float ) __attribute__ ((__always_inline__)); static __inline__ int __inline_isfinited (double ) __attribute__ ((__always_inline__)); static __inline__ int __inline_isfinite (long double) __attribute__ ((__always_inline__)); static __inline__ int __inline_isinff (float ) __attribute__ ((__always_inline__)); static __inline__ int __inline_isinfd (double ) __attribute__ ((__always_inline__)); static __inline__ int __inline_isinf (long double) __attribute__ ((__always_inline__)); static __inline__ int __inline_isnanf (float ) __attribute__ ((__always_inline__)); static __inline__ int __inline_isnand (double ) __attribute__ ((__always_inline__)); static __inline__ int __inline_isnan (long double) __attribute__ ((__always_inline__)); static __inline__ int __inline_isnormalf (float ) __attribute__ ((__always_inline__)); static __inline__ int __inline_isnormald (double ) __attribute__ ((__always_inline__)); static __inline__ int __inline_isnormal (long double) __attribute__ ((__always_inline__)); static __inline__ int __inline_signbitf (float ) __attribute__ ((__always_inline__)); static __inline__ int __inline_signbitd (double ) __attribute__ ((__always_inline__)); static __inline__ int __inline_signbit (long double) __attribute__ ((__always_inline__)); static __inline__ int __inline_isinff( float __x ) { return __builtin_fabsf(__x) == __builtin_inff(); } static __inline__ int __inline_isinfd( double __x ) { return __builtin_fabs(__x) == __builtin_inf(); } static __inline__ int __inline_isinf( long double __x ) { return __builtin_fabsl(__x) == __builtin_infl(); } static __inline__ int __inline_isfinitef( float __x ) { return __x == __x && __builtin_fabsf(__x) != __builtin_inff(); } static __inline__ int __inline_isfinited( double __x ) { return __x == __x && __builtin_fabs(__x) != __builtin_inf(); } static __inline__ int __inline_isfinite( long double __x ) { return __x == __x && __builtin_fabsl(__x) != __builtin_infl(); } static __inline__ int __inline_isnanf( float __x ) { return __x != __x; } static __inline__ int __inline_isnand( double __x ) { return __x != __x; } static __inline__ int __inline_isnan( long double __x ) { return __x != __x; } static __inline__ int __inline_signbitf( float __x ) { union{ float __f; unsigned int __u; }__u; __u.__f = __x; return (int)(__u.__u >> 31); } static __inline__ int __inline_signbitd( double __x ) { union{ double __f; unsigned int __u[2]; }__u; __u.__f = __x; return (int)(__u.__u[1] >> 31); } static __inline__ int __inline_signbit( long double __x ){ union{ long double __ld; struct{ unsigned int __m[2]; short __sexp; }__p; }__u; __u.__ld = __x; return (int) (((unsigned short) __u.__p.__sexp) >> 15); } static __inline__ int __inline_isnormalf( float __x ) { float fabsf = __builtin_fabsf(__x); if( __x != __x ) return 0; return fabsf < __builtin_inff() && fabsf >= 1.17549435e-38F; } static __inline__ int __inline_isnormald( double __x ) { double fabsf = __builtin_fabs(__x); if( __x != __x ) return 0; return fabsf < __builtin_inf() && fabsf >= 2.2250738585072014e-308; } static __inline__ int __inline_isnormal( long double __x ) { long double fabsf = __builtin_fabsl(__x); if( __x != __x ) return 0; return fabsf < __builtin_infl() && fabsf >= 3.36210314311209350626e-4932L; } # 262 "/usr/include/architecture/i386/math.h" 3 4 extern double acos( double ); extern float acosf( float ); extern double asin( double ); extern float asinf( float ); extern double atan( double ); extern float atanf( float ); extern double atan2( double, double ); extern float atan2f( float, float ); extern double cos( double ); extern float cosf( float ); extern double sin( double ); extern float sinf( float ); extern double tan( double ); extern float tanf( float ); extern double acosh( double ); extern float acoshf( float ); extern double asinh( double ); extern float asinhf( float ); extern double atanh( double ); extern float atanhf( float ); extern double cosh( double ); extern float coshf( float ); extern double sinh( double ); extern float sinhf( float ); extern double tanh( double ); extern float tanhf( float ); extern double exp ( double ); extern float expf ( float ); extern double exp2 ( double ); extern float exp2f ( float ); extern double expm1 ( double ); extern float expm1f ( float ); extern double log ( double ); extern float logf ( float ); extern double log10 ( double ); extern float log10f ( float ); extern double log2 ( double ); extern float log2f ( float ); extern double log1p ( double ); extern float log1pf ( float ); extern double logb ( double ); extern float logbf ( float ); extern double modf ( double, double * ); extern float modff ( float, float * ); extern double ldexp ( double, int ); extern float ldexpf ( float, int ); extern double frexp ( double, int * ); extern float frexpf ( float, int * ); extern int ilogb ( double ); extern int ilogbf ( float ); extern double scalbn ( double, int ); extern float scalbnf ( float, int ); extern double scalbln ( double, long int ); extern float scalblnf ( float, long int ); extern double fabs( double ); extern float fabsf( float ); extern double cbrt( double ); extern float cbrtf( float ); extern double hypot ( double, double ); extern float hypotf ( float, float ); extern double pow ( double, double ); extern float powf ( float, float ); extern double sqrt( double ); extern float sqrtf( float ); extern double erf( double ); extern float erff( float ); extern double erfc( double ); extern float erfcf( float ); extern double lgamma( double ); extern float lgammaf( float ); extern double tgamma( double ); extern float tgammaf( float ); extern double ceil ( double ); extern float ceilf ( float ); extern double floor ( double ); extern float floorf ( float ); extern double nearbyint ( double ); extern float nearbyintf ( float ); extern double rint ( double ); extern float rintf ( float ); extern long int lrint ( double ); extern long int lrintf ( float ); extern double round ( double ); extern float roundf ( float ); extern long int lround ( double ); extern long int lroundf ( float ); extern long long int llrint ( double ); extern long long int llrintf ( float ); extern long long int llround ( double ); extern long long int llroundf ( float ); extern double trunc ( double ); extern float truncf ( float ); extern double fmod ( double, double ); extern float fmodf ( float, float ); extern double remainder ( double, double ); extern float remainderf ( float, float ); extern double remquo ( double, double, int * ); extern float remquof ( float, float, int * ); extern double copysign ( double, double ); extern float copysignf ( float, float ); extern double nan( const char * ); extern float nanf( const char * ); extern double nextafter ( double, double ); extern float nextafterf ( float, float ); extern double fdim ( double, double ); extern float fdimf ( float, float ); extern double fmax ( double, double ); extern float fmaxf ( float, float ); extern double fmin ( double, double ); extern float fminf ( float, float ); extern double fma ( double, double, double ); extern float fmaf ( float, float, float ); extern long double acosl(long double); extern long double asinl(long double); extern long double atanl(long double); extern long double atan2l(long double, long double); extern long double cosl(long double); extern long double sinl(long double); extern long double tanl(long double); extern long double acoshl(long double); extern long double asinhl(long double); extern long double atanhl(long double); extern long double coshl(long double); extern long double sinhl(long double); extern long double tanhl(long double); extern long double expl(long double); extern long double exp2l(long double); extern long double expm1l(long double); extern long double logl(long double); extern long double log10l(long double); extern long double log2l(long double); extern long double log1pl(long double); extern long double logbl(long double); extern long double modfl(long double, long double *); extern long double ldexpl(long double, int); extern long double frexpl(long double, int *); extern int ilogbl(long double); extern long double scalbnl(long double, int); extern long double scalblnl(long double, long int); extern long double fabsl(long double); extern long double cbrtl(long double); extern long double hypotl(long double, long double); extern long double powl(long double, long double); extern long double sqrtl(long double); extern long double erfl(long double); extern long double erfcl(long double); extern long double lgammal(long double); extern long double tgammal(long double); extern long double ceill(long double); extern long double floorl(long double); extern long double nearbyintl(long double); extern long double rintl(long double); extern long int lrintl(long double); extern long double roundl(long double); extern long int lroundl(long double); extern long long int llrintl(long double); extern long long int llroundl(long double); extern long double truncl(long double); extern long double fmodl(long double, long double); extern long double remainderl(long double, long double); extern long double remquol(long double, long double, int *); extern long double copysignl(long double, long double); extern long double nanl(const char *); extern long double nextafterl(long double, long double); extern double nexttoward(double, long double); extern float nexttowardf(float, long double); extern long double nexttowardl(long double, long double); extern long double fdiml(long double, long double); extern long double fmaxl(long double, long double); extern long double fminl(long double, long double); extern long double fmal(long double, long double, long double); # 507 "/usr/include/architecture/i386/math.h" 3 4 extern double __inf( void ); extern float __inff( void ); extern long double __infl( void ); extern float __nan( void ); extern double j0 ( double ); extern double j1 ( double ); extern double jn ( int, double ); extern double y0 ( double ); extern double y1 ( double ); extern double yn ( int, double ); extern double scalb ( double, double ); # 543 "/usr/include/architecture/i386/math.h" 3 4 extern int signgam; # 557 "/usr/include/architecture/i386/math.h" 3 4 extern long int rinttol ( double ); extern long int roundtol ( double ); # 568 "/usr/include/architecture/i386/math.h" 3 4 struct exception { int type; char *name; double arg1; double arg2; double retval; }; # 597 "/usr/include/architecture/i386/math.h" 3 4 extern int finite ( double ); extern double gamma ( double ); extern int matherr ( struct exception * ); extern double significand ( double ); extern double drem ( double, double ); # 29 "/usr/include/math.h" 2 3 4 # 3 "main.c" 2 typedef struct { double real; double imag; } my_complex; static my_complex c_rect(double phi) { my_complex z; z.real = cos(phi); z.imag = sin(phi); return z; } int main(void) { my_complex z; z = c_rect(-0.0); printf("z.imag is %f; expected %f\n", z.imag, -0.0); return 0; }
Works on Linux where it replaces it with a call to sincos. Also works with the Linux glibc if explicitly calling cexp like static my_complex c_rect(double phi) { my_complex z; z.imag = __imag cexp (I * phi); return z; } which is only with -ffast-math replaced by a call to sin. So, this is a Darwin libm bug.
Thanks for the response! It does appear to be true that cexp doesn't follow Annex G of C99, on OS X 10.5.5. I agree that this is undesirable, but I can't see why it should be considered a bug. Annex G of C99 is merely informative, and as far as I can tell __STDC_IEC_559_COMPLEX__ is not defined on OS X 10.5.5. In other words, it seems to me that there's no basis for gcc's assumption that sin(-0.0) is interchangeable with the imaginary part of cexp(-0.0*I).
Well, Darwin GCC maintainers claim that darwin /* All new versions of Darwin have C99 functions. */ #define TARGET_C99_FUNCTIONS 1 so we just rely on that. If it is not true then this is either a darwin bug or they should not advertise the availability of these functions.
Makes sense. Thanks for the explanation. I'll go and file a bug with Apple.
A quick note for anyone else who comes across this: this libm bug is fixed in Snow Leopard (darwin 10).