Bug 32448 - abs / printf bug
Summary: abs / printf bug
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 4.3.0
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-06-21 06:00 UTC by Rob
Modified: 2007-06-28 18:51 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
Demo for abs / printf bug (1.04 KB, application/octet-stream)
2007-06-21 06:01 UTC, Rob
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Rob 2007-06-21 06:00:01 UTC
I tried this program on many versions of GCC and it seems to trigger errors.
The curious can edit the program and un-comment various combinations of lines
to chase the error. It compiles as-is and demonstrates the error.


# gcc-3.3 -v
Reading specs from /usr/lib/gcc-lib/i486-linux/3.3.5/specs
Configured with: ../src/configure -v --enable-languages=c,c++,java,f77,pascal,objc,ada,treelang --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-gxx-include-dir=/usr/include/c++/3.3 --enable-shared --enable-__cxa_atexit --with-system-zlib --enable-nls --without-included-gettext --enable-clocale=gnu --enable-debug --enable-java-gc=boehm --enable-java-awt=xlib --enable-objc-gc i486-linux
Thread model: posix
gcc version 3.3.5 (Debian 1:3.3.5-13)


# gcc-3.4 -v
Reading specs from /usr/lib/gcc/i486-linux-gnu/3.4.6/specs
Configured with: ../src/configure -v --enable-languages=c,c++,f77,pascal --prefix=/usr --libexecdir=/usr/lib --with-gxx-include-dir=/usr/include/c++/3.4 --enable-shared --with-system-zlib --enable-nls --without-included-gettext --program-suffix=-3.4 --enable-__cxa_atexit --enable-clocale=gnu --enable-libstdcxx-debug --with-tune=i686 i486-linux-gnu
Thread model: posix
gcc version 3.4.6 (Debian 3.4.6-5)


# gcc-4.1 -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu --enable-libstdcxx-debug --enable-mpfr --with-tune=i686 --enable-checking=release i486-linux-gnu
Thread model: posix
gcc version 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)


# gcc -v
Using built-in specs.
Target: i686-pc-linux-gnu
Configured with: /root/downloads/gcc-4_2-branch/configure --verbose --enable-languages=c,ada,c++,fortran,java,objc,obj-c++ --with-tune=athlon-xp --prefix=/usr --enable-objc-gc --enable-concept-checks --disable-multilib --with-gxx-include-dir=/usr/include/c++/4.2 --enable-libstdcxx-debug --enable-static --enable-shared --enable-initfini-array --enable-__cxa_atexit --enable-threads=posix --enable-version-specific-runtime-libs --enable-libssp --enable-libmudflap --enable-libgomp --disable-werror --enable-nls --with-included-gettext --enable-decimal-float --with-long-double-128 --enable-debug --enable-java-gc=boehm --with-x --x-includes=/usr/X11R6/include --x-libraries=/usr/X11R6/lib --enable-java-awt=gtk,xlib --enable-gtk-cairo --enable-qt-peer --enable-xmlj --enable-gconf-peer --enable-tool-wrappers --with-gjdoc --enable-portable-native-sync --enable-libgcj-multifile --with-stabs --enable-hash-synchronization --enable-gc-debug --enable-interpreter --with-system-zlib --enable-libada --with-tls --with-cpu=athlon-xp --with-arch=athlon-xp --enable-checking
Thread model: posix
gcc version 4.2.0 20070501 (prerelease)


# /usr/test/bin/gcc -v
Using built-in specs.
Target: i686-pc-linux-gnu
Configured with: /root/downloads/gcc-4_3-trunk/configure --verbose --enable-languages=c,ada,c++,fortran,java,objc,obj-c++ --prefix=/usr/test --enable-objc-gc --enable-concept-checks --disable-multilib --with-gxx-include-dir=/usr/test/include/c++/4.3 --enable-libstdcxx-debug --enable-static --enable-shared --enable-initfini-array --enable-__cxa_atexit --enable-threads=posix --enable-version-specific-runtime-libs --enable-libssp --enable-libmudflap --enable-libgomp --disable-werror --enable-nls --with-included-gettext --enable-decimal-float --enable-debug --enable-java-gc=boehm --with-x --x-includes=/usr/X11R6/include --x-libraries=/usr/X11R6/lib --enable-java-awt=gtk,xlib --enable-gtk-cairo --enable-qt-peer --enable-xmlj --enable-gconf-peer --enable-tool-wrappers --enable-portable-native-sync --enable-examples --with-stabs --enable-hash-synchronization --enable-gc-debug --enable-interpreter --with-system-zlib --enable-libada --with-tls --with-tune=athlon-xp --with-cpu=athlon-xp --with-arch=athlon-xp --enable-stage1-checking=assert,gc,misc,rtl,rtlflag,runtime,tree
Thread model: posix
gcc version 4.3.0 20070609 (experimental)



Here is what this program prints using gcc 3.3, 3.4, 4.1, 4.2, or 4.3 on i686-pc-linux-gnu:


Try uncommenting various combinations of the printf() statements used in
this program to hide or trigger the abs/printf error. Printing QUESTION1
with abs() gives an error, printing QUESTION1 without using abs() is OK.
Uncommenting the unused sprintf() corrects printf() output, usually. The
program should print zero all the time but accuracy is another matter.
Notice in number 7 that it prints PLUS zero but otherwise NEGATIVE zero.
n= 1  QUESTION1=-0.000  QUESTION2=-0.000 
n= 1                    QUESTION2=+0.414 
n= 2  QUESTION1=-0.000  QUESTION2=-0.000 
n= 2                    QUESTION2=+0.236 
n= 3  QUESTION1=-0.000  QUESTION2=-0.000 
n= 3                    QUESTION2=+0.162 
n= 4  QUESTION1=-0.000  QUESTION2=-0.000 
n= 4                    QUESTION2=+0.123 
n= 5  QUESTION1=-16522743262502092537856.000  QUESTION2=-0.000 
n= 5                    QUESTION2=+0.099 
n= 6  QUESTION1=-3721029422797390905264552616897318787693080694838285023794783705131793115153378545691097982837335151135903481248849330176.000  QUESTION2=-0.000 
n= 6                    QUESTION2=+0.083 
n= 7  QUESTION1=+0.000  QUESTION2=-0.000 
n= 7                    QUESTION2=+0.071 
n= 8  QUESTION1=+6031613735721727548569690008146484981792835303047262360203552731912464214457090838248418515794553484912424369429741130556727473755197034894720012190408277107933184.000  QUESTION2=-0.000 
n= 8                    QUESTION2=+0.062 
n= 9  QUESTION1=-255317213055347619461852498717677545874233184953584593781149061302390209188816780225236890307385767936779025481685187363340288.000  QUESTION2=-0.000 
n= 9                    QUESTION2=+0.055 



On Cygwin 3.4.4 the output looks like this:

n= 1  QUESTION1=-0.000  QUESTION2=+10616123003695640721911045079668438242222030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000 
n= 1                    QUESTION2=+0.414 
n= 2  QUESTION1=-0.000  QUESTION2=+10616123001910498887278330503471575952153310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000 
n= 2                    QUESTION2=+0.236 
n= 3  QUESTION1=-0.000  QUESTION2=+10616123000538905870424505112676888844605500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000 
n= 3                    QUESTION2=+0.162 
...

Equally bad even though it looks more consistent.
Comment 1 Rob 2007-06-21 06:01:23 UTC
Created attachment 13753 [details]
Demo for abs / printf bug
Comment 2 Uroš Bizjak 2007-06-21 06:33:29 UTC
Use fabs/fabsf for double/float operands. Especially with %f.
Comment 3 Rob 2007-06-21 08:49:37 UTC
GCC printed no warning about disliking a conversion.


I hacked the program in accordance with your suggestion and now it prints:

n= 4  QUESTION1=+0.123  QUESTION2=+0.123  Q(n)=+0.000
n= 5  QUESTION1=+0.099  QUESTION3=+0.099  Q(n)=-0.000
n= 5  QUESTION1=+0.099  QUESTION2=+0.099  Q(n)=-0.000
n= 6  QUESTION1=+0.083  QUESTION3=+0.083  Q(n)=-0.000
n= 6  QUESTION1=+0.083  QUESTION2=+0.083  Q(n)=-0.000
n= 7  QUESTION1=+0.071  QUESTION3=+0.071  Q(n)=+0.000
n= 7  QUESTION1=+0.071  QUESTION2=+0.071  Q(n)=+0.000
n= 8  QUESTION1=+0.062  QUESTION3=+0.062  Q(n)=-0.000


Sometimes the answer is _positive_ zero and sometimes the answer is _negative_ zero.
Comment 4 Andrew Pinski 2007-06-21 09:06:03 UTC
abs converts the float/double to an integer type so this is not a bug.  If you use 4.3, you can use -Wconversion.
Comment 5 Uroš Bizjak 2007-06-21 11:27:26 UTC
(In reply to comment #3)
> GCC printed no warning about disliking a conversion.

It just happens that gcc is not like microwave oven that has to include warnings about not drying animals in it.

> Sometimes the answer is _positive_ zero and sometimes the answer is _negative_
> zero.

http://en.wikipedia.org/wiki/Floating-point
Comment 6 Rob 2007-06-21 11:30:47 UTC
Thanks for everyones input.


The only issues related to this 'bug' are:

1): printf _sometimes_ prints "-0.000" and sometimes prints "+0.000" - the reason it is even showing the "+" or "-" is because I enabled them using "%+f", this I understand. 

My 2 cents is that the correct answer is "+0.000" and never "-0.000" - UNLESS, that is related to _accuracy_ and nothing to do with "printf"; in which cause that is a different bug.


2): Using a "sprintf()" to print the answer to a char string (which itself is never even used!) "fixes" the bug. This suggests to me that something is upsetting the folding. It can't be an optimization issue since this occurs at any optimization level (even -O0, none) so unless it is the parsing it may be the folding.

Sometimes GCC goes "crazy" (see 6, 8, and 9). If indeed GCC is operating "correctly" it should do the SAME thing everytime - Correct ?

If GCC want to convert it to an INT, fine. the correct answer is ZERO and not:
a): -16522743262502092537856.000
b): +60316137357217275485696900081464849817...(many more digits)...
c): -25531721305534761946185249871767754587...(many more digits)...

Cygwin gives the same (ridiculous) answer _everytime_ which is better though it ought to be ZERO.

If GCC thinks it is doing the correct thing (there is NO BUG) then why doesn't it do the SAME thing - it is saying that the previous time it was wrong and wants to give a different answer, when it ought to give the same answer.

3): If this belongs on http://gcc.gnu.org/bugs.html#nonbugs then this is a documentation bug.

Thanks for considering this report.
Comment 7 Rob 2007-06-22 09:18:24 UTC
Uros Bizjak - has to include warnings about not drying animals in it

I have an older model with no such label therefore I am OK.   ;)

--- Here is another program that demonstrates that there is some problem ---

/*
>>------- Comment #4 From Andrew Pinski 2007-06-21 09:06 [reply] ------- 
>>abs converts the float/double to an integer type so this is not a bug.  If you
>>use 4.3, you can use -Wconversion.

Notice that "b" and "d" are different - there is no (equal or otherwise) "conversion".


The operation of abs (according to printf output) seems to be:

If it is an integer accept and use it. If it is a float then it is = 0 .

Look what printf does to "i", thats an easy conversion.
*/

#include <math.h>
#include <stdio.h>
int main ( ) { 
/* float a, b, c, d, e, f, g, h, i, j; */       /* gives warning "argument x has type 'double'", it */
  double a, b, c, d, e, f, g, h, i, j;  /* might be better to say type "float" instead */
  a = 16.000;
  b =  abs(a);
  c = fabs(a);
  d =  abs((int)(a));
  e = fabs((int)(a));
  f =  abs((float)(a));
  g = fabs((float)(a));
  h = (int)abs(a);
  i = (float)abs(a);
  j = 0.0;

  printf("a = %.2f  b = %.2f  c = %.2f  d = %.2f  e = %.2f  ", a, b, c, d, e);
  printf("f = %.2f  g = %.2f  h = %.2f  i = %.2f  j = %.2f\n", f, g, h, i, j);
  printf("a = %d  b = %d  c = %d  d = %d  e = %d  ", a, b, c, d, e);
  printf("f = %d  g = %d  h = %d  i = %d  j = %d\n", f, g, h, i, j);
  return 0;
}

/* /usr/test/bin/gcc -Wall -Wconversion -o math_test_7 math_test_7.c */

/* Output:
a = 16.00  b = 0.00  c = 16.00  d = 16.00  e = 16.00  f = 0.00  g = 16.00  h = 0.00  i = 0.00  j = 0.00
a = 0  b = 1076887552  c = 0  d = 0  e = 0  f = 0  g = 0  h = 0  i = 1076887552  j = 0
*/

How can that be correct conversion ?
Comment 8 Rob 2007-06-22 09:42:21 UTC
A earlier version of this program had these lines in it:

...
    f =  abs((float)(a));
    g = fabs((float)(a));
    h = (int)abs(a);
    i = 0.0;

    printf("a = %.2f  b = %.2f  c = %.2f  d = %.2f  e = %.2f  ", a, b, c, d, e);
    printf("f = %.2f  g = %.2f  h = %.2f  i = %.2f\n", f, g, h, i);
    printf("a = %d  b = %d  c = %d  d = %d  e = %d  ", a, b, c, d, e);
    printf("f = %d  g = %d  h = %d  i = %d\n", f, g, h, i);
printf("i2 = %d\n", i);
    return 0;
...

Here is the output:

/* Output:
a = 16.00  b = 0.00  c = 16.00  d = 16.00  e = 16.00  f = 0.00  g = 16.00  h = 0.00  i = 0.00
a = 0  b = 1076887552  c = 0  d = 0  e = 0  f = 0  g = 0  h = 0  i = 1076887552
i2 = 0
*/


Notice that the prior time that the 9th variable was "1076887552" (as it is in comment 7). Using gdb I see that print /x 1076887552 = 0x40300000 . It might be using the stack pointer for the value. 

Notice that "i" and "i2" both print the value of "i" and that it is different - it is as if gcc is using a pointer into memory instead of the actual value.

I'll work on some debugging and see if I can find out how it derives the value of "i" that it prints differently each time.
 
Comment 9 Uroš Bizjak 2007-06-22 12:26:12 UTC
(In reply to comment #8)

> I'll work on some debugging and see if I can find out how it derives the value
> of "i" that it prints differently each time.

Don't worry, it works correctly.

The non-problem you are going after is in printf(). It takes variable arguments from the stack and interprets them according to the format string. Argument are pushed to the stack by the caller without any other communication with callee, so it is obvious that format string _must_ reflect the type of values on stack. Also note, that %f inherently converts float type to double, so your values on the stack are FUBAR as far as printf is concerned.
Comment 10 Rob 2007-06-23 04:21:50 UTC
(In reply to comment #9)
> Don't worry, it works correctly.
> ...
> Argument are pushed to the stack by the caller without any other
> communication with callee, so it is obvious that format string _must_ 
> reflect the type of values on stack.
> Also note, that %f inherently converts float type to double, so your values on
> the stack are FUBAR as far as printf is concerned.

That is "working correctly" ?!?

Very well, but not so obvious.


_IF_ we had "argflaps" (taken from the word "mudflaps") we could get printf (or any other function) to type-check the variable on the stack (or anywhere!) at run-time and compare it to what it "thought" it should be. This could be a very powerful feature - but lack of it is not a bug :( .
Comment 11 kargls 2007-06-23 05:06:00 UTC
(In reply to comment #10)
> (In reply to comment #9)
> > Don't worry, it works correctly.
> > ...
> > Argument are pushed to the stack by the caller without any other
> > communication with callee, so it is obvious that format string _must_ 
> > reflect the type of values on stack.
> > Also note, that %f inherently converts float type to double, so your values on
> > the stack are FUBAR as far as printf is concerned.
> 
> That is "working correctly" ?!?
> 
> Very well, but not so obvious.
> 
> 
> _IF_ we had "argflaps" (taken from the word "mudflaps") we could get printf (or
> any other function) to type-check the variable on the stack (or anywhere!) at
> run-time and compare it to what it "thought" it should be. This could be a very
> powerful feature - but lack of it is not a bug :( .
> 

(1) Try -Wformat

(2) Send a patch that implements your argflaps

(3) There is an expectation someone writing C might actually
    adhere to the Standard
Comment 12 Rob 2007-06-23 07:51:28 UTC
> Send a patch that implements your argflaps

Cyclone uses what it calls "thin" and "fat" pointers. We could implement this in GCC. An extra byte per variable could store the type and a bit of code could check it (at run-time) and perform "run-time conversion", use "Syntactic Sugar" or "C overloading" - depending on what you would like to call it..


Investigating:

1): Cyclone is a dialect of C that retains its transparency and control, but adds the benefits of safety (no unchecked runtime errors, for instance). In Cyclone, buffer overflows and related bugs are prevented for all programs, whether written by security experts or by novices. The changes required to achieve safety are pervasive, but Cyclone is still recognizably C.

http://www.eecs.harvard.edu/~greg/cyclone/


2a): CIL (C Intermediate Language) is a high-level representation along with a set of tools that permit easy analysis and source-to-source transformation of C programs. http://hal.cs.berkeley.edu/cil/ . This intermediate language is used to stage both Deputy and CCured.


2b): CCured is a source-to-source translator for C. It analyzes the C program to determine the smallest number of run-time checks that must be inserted in the program to prevent all memory safety violations. The resulting program is memory safe, meaning that it will stop rather than overrun a buffer or scribble over memory that it shouldn't touch. Many programs can be made memory-safe this way while losing only 10–60% run-time performance (the performance cost is smaller for cleaner programs, and can be improved further by holding CCured's hand on the parts of the program that it does not understand by itself). Using CCured we have found bugs that Purify misses with an order of magnitude smaller run-time cost.

http://hal.cs.berkeley.edu/ccured/index.html


2c): Deputy is a C compiler that is capable of preventing common C programming errors, including out-of-bounds memory accesses as well as many other common type-safety errors. It is designed to work on real-world code, up to and including the Linux kernel itself.

Deputy allows C programmers to provide simple type annotations that describe pointer bounds and other important program invariants. Deputy verifies that your program adheres to these invariants through a combination of compile-time and run-time checking.

http://deputy.cs.berkeley.edu/


These projects have been operating for many years, come from a reputable source and integrate with gcc (albiet they do not seem to have tried 4.3.0).


A paper on Deputy - Static Analysis of C for Hybrid Type Checking
http://www.eecs.berkeley.edu/Pubs/TechRpts/2007/EECS-2007-1.pdf


The difference between Duputy and CCured (both implemented on top of the CIL framework) is:

CCured is a whole program analysis that instruments pointers with bounds information. As in Deputy the bounds information may need to be checked at runtime. The CCured system includes an optimization phase that also attempts to reason about bounds checks, null checks, and others. The Deputy type system presents its optimizer with some different challenges than those seen by the CCured optimizer. The primary challenge is the sheer volume of runtime 
checks added by the type system. On the benchmarks presented here, deputized code achieves better performance than cured code, however CCured also enforces allocation safety, which Deputy does not.

The "license" for these in one case is to simply credit the authors.
Comment 13 Rob 2007-06-23 08:32:30 UTC
(In reply to comment #11)
> (1) Try -Wformat

"-Wall" includes "-Wformat" according to gcc.info. See comment 7 for the command line I used:
/* /usr/test/bin/gcc -Wall -Wconversion -o math_test_7 math_test_7.c */


> (3) There is an expectation someone writing C might actually
>     adhere to the Standard

From time to time that does not always occur. See here:
http://gcc.gnu.org/bugzilla/buglist.cgi?emailreporter1=1&emailtype1=substring&email1=kargl@gcc.gnu.org
Comment 14 kargls 2007-06-23 17:56:52 UTC
(In reply to comment #13)
> (In reply to comment #11)
> > (1) Try -Wformat
> 
> "-Wall" includes "-Wformat" according to gcc.info. See comment 7 for the
> command line I used:
> /* /usr/test/bin/gcc -Wall -Wconversion -o math_test_7 math_test_7.c */

mobile:kargl[204] cc -o z -Wformat m.c -lm
m.c: In function 'main':
m.c:21: warning: format '%d' expects type 'int', but argument 2 has type 'double'
m.c:21: warning: format '%d' expects type 'int', but argument 3 has type 'double'
m.c:21: warning: format '%d' expects type 'int', but argument 4 has type 'double'
m.c:21: warning: format '%d' expects type 'int', but argument 5 has type 'double'
m.c:21: warning: format '%d' expects type 'int', but argument 6 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 2 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 3 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 4 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 5 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 6 has type 'double'

mobile:kargl[205] cc -o z -Wall m.c -lm
m.c: In function 'main':
m.c:9: warning: implicit declaration of function 'abs'
m.c:21: warning: format '%d' expects type 'int', but argument 2 has type 'double'
m.c:21: warning: format '%d' expects type 'int', but argument 3 has type 'double'
m.c:21: warning: format '%d' expects type 'int', but argument 4 has type 'double'
m.c:21: warning: format '%d' expects type 'int', but argument 5 has type 'double'
m.c:21: warning: format '%d' expects type 'int', but argument 6 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 2 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 3 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 4 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 5 has type 'double'
m.c:22: warning: format '%d' expects type 'int', but argument 6 has type 'double'

m.c is your code in comment #7

> 
> 
> > (3) There is an expectation someone writing C might actually
> >     adhere to the Standard
> 
> From time to time that does not always occur. See here:
>http://gcc.gnu.org/bugzilla/buglist.cgi?emailreporter1=1&emailtype1=substring&email1=kargl@gcc.gnu.org

I'm not sure what you're trying to demonstate.  Bug reports associated with me
are related to Fortran not C.

Comment 15 Rob 2007-06-23 18:49:38 UTC
>Andrew Pinski
>abs converts the float/double to an integer type so this is not a bug.


>Uros Bizjak
>The non-problem you are going after is in printf(). It takes variable arguments
>from the stack and interprets them according to the format string. Argument are
>pushed to the stack by the caller without any other communication with callee,
>so it is obvious that format string _must_ reflect the type of values on stack.
>Also note, that %f inherently converts float type to double, so your values on
>the stack are FUBAR as far as printf is concerned.


So what we end up with is something like this:

main(){
  printf("%f  %f  %f  %f\n", abs(1234.5678), fabs(1234.5678), abs((int)1234.5678), abs((int)(1234.5678)));
}

and here is the incorrect output:

639356954161190327651780598819979664641251888147947213308332781758079574424033219293612810237966089410023577192498425554519097800758249636139201808050627475876578945300496861358730667076856621948936720606078976728760320.000000  
0.000000  -0.007810  -0.000000


The first and third values are obviously wrong.

The second and fourth values agree - so (in theory) that is the "correct" answer - according to the way that GCC is intended to operate (as per what Andrew says).

If the "correct" answer is zero (without concerning ourselves if it is plus zero or minus zero) then why are the _third_ and _fourth_ answer different!

 abs((int)1234.5678)  = -0.007810
abs((int)(1234.5678)) = -0.000000

Should the cast work without the extra parentheses.
Comment 16 Uroš Bizjak 2007-06-23 19:41:32 UTC
(In reply to comment #14)

> m.c: In function 'main':
> m.c:9: warning: implicit declaration of function 'abs'

Also, add a prototype for integer abs(), like "int abs(int);". Then everything will work as "expected".
Comment 17 Uroš Bizjak 2007-06-23 19:51:49 UTC
(In reply to comment #15)

> and here is the incorrect output:

Here is the correct input:

--cut here--
#include <stdio.h>

int abs(int);
double fabs(double);

int main()
{
  printf("%i  %f  %i  %i\n", abs(1234.5678), fabs(1234.5678),
         abs((int)1234.5678), abs((int)(1234.5678)));

  return 0;
}
--cut here--

OK, have fun, I'll stop wasting electrons here...
Comment 18 Rob 2007-06-24 03:21:45 UTC
(In reply to comment #16)
> (In reply to comment #14)
> > m.c: In function 'main':
> > m.c:9: warning: implicit declaration of function 'abs'
> Also, add a prototype for integer abs(), like "int abs(int);". Then everything
> will work as "expected".


> Rob
> ...why are the _third_ and _fourth_ answers different!
>Should the cast work without the extra parentheses.

#include <stdio.h>
int abs(int);
double fabs(double);
int main()
{
  printf("%f  %f  %f  %f\n", abs(1234.5678), fabs(1234.5678), 
         abs((int)1234.5678), abs((int)(1234.5678)));
  return 0;
}

639356810344963382486834444601597699265113107384544848778287582042881171854556760809962494834238640539694738043963701847575050181760552125660054155932857305858816958002965674142423095065121202781270370782793943786455040.000000  
0.000000  -0.422029  -0.000000


If I re-write and swap the 3rd and fourth abs'es I get:

#include <stdio.h>
int abs(int);
double fabs(double);
int main()
{
  printf("%f  %f  %f  %f\n", abs(1234.5678), fabs(1234.5678), 
         abs((int)(1234.5678)), abs((int)1234.5678));
  return 0;
}

639356810344963382486834444601597699265113107384544848778287582042881171854556760809962494834238640539694738043963701847575050181760552125660054155932857305858816958002965674142423095065121202781270370782793943786455040.000000  
0.000000  -1.333046  -0.000000

The same sort of output, so the parentheses are not at fault.

Printf does not cast it's "%i" / "%f" 's even with constants know at compile time, let alone run-time. It is like you said in comment 2 "Especially with %f."

So if you wanted to call functions by way of a pointer and they might return int or float (or double) you would need code to test the type prior to attempting to send it to multiple types of printf statement.

Using one printf for each possible combination with only 2 vars we would need 4 printf's. At least all the extra trouble buys us that it doesn't matter if they are const or var :( here goes (there is no need for "_someone_" to post that I'm missing a main() statement!):


...
switch (TYPE_TEST_FOR_2_VARS(x,y)) {
  case TTF2V_I_I:
    printf("X= %i  Y=%i\n", x, y);
    goto done;
  case TTF2V_I_F:
    printf("X= %i  Y=%f\n", x, y);
    goto done;
  case TTF2V_F_I:
    printf("X= %f  Y=%i\n", x, y);
    goto done;
  case TTF2V_F_F:
    printf("X= %f  Y=%f\n", x, y);
    goto done;
}
done:;
...


Same idea for 4 variables, just a lot more code ...

Gotcha, thanks Uros.
Comment 19 Rob 2007-06-24 05:01:58 UTC
So here we have it:

#include <stdio.h>
int abs(int);
double fabs(double);
int main()
{
  printf("%f  %f  %f  %f\n", abs(1234.5678), fabs(1234.5678),
         abs((int)1234.5678), abs((int)(1234.5678)));
  printf("%f  %f  %f  %f\n", (float)abs(1234.5678), (float)fabs(1234.5678),
         (float)abs((int)1234.5678), (float)abs((int)(1234.5678)));
  return 0;
}


# /opt/gcc-4_3-build-2/gcc/xgcc -Wall -B/opt/gcc-4_3-build-2/gcc/ -o math_test_11 math_test_11.c
math_test_11.c: In function 'main':
math_test_11.c:9: warning: format '%f' expects type 'double', but argument 2 has type 'int'
math_test_11.c:9: warning: format '%f' expects type 'double', but argument 4 has type 'int'
math_test_11.c:9: warning: format '%f' expects type 'double', but argument 5 has type 'int'


# ./math_test_11
639356810344963382486834444601597699265113107384544848778287582042881171854556760809962494834238640539694738043963701847575050181760552125660054155932857305858816958002965674142423095065121202781270370782793943786455040.000000 
0.000000  -0.008386  -0.000000
1234.000000  1234.567749  1234.000000  1234.000000


Conclusion:

Printf can't figure out it's own casts (even for consts) and you _must_ use the cast (float) before any variables that are to be printed with format "%f".

Since GCC can 'see' the "%f" at compile time, _If_ gcc put in the "(float)" cast (fix all other types too) for you then the printf statement would produce the correct result and provide the missing piece to what Andrew claims in comment 4 "abs converts the float/double to an integer type".

A simple modification to GCC could read the format and cast the variable. This could be integrated with ssp to avoid reading and writing (sprintf()) to the stack. This does not seem like an expensive mod. 
Comment 20 Andrew Pinski 2007-06-24 05:08:28 UTC
> Since GCC can 'see' the "%f" at compile time
Yes it can but that does not mean the behavior is the correct to cast it to float.  In fact the behavior is undefined at runtime so you are just running into the runtime undefined behavior which is really normal with C/C++ now adays.  
Comment 21 Rob 2007-06-24 12:22:05 UTC
(In reply to comment #20)
> > Since GCC can 'see' the "%f" at compile time
> ... that does not mean the behavior is the correct to cast it to float. ...

So the correct behavior is:

a): Cast it to something other than float.

 - if it can only be float (in the example, otherwise anything else) as far as 
   printf is concerned then why do that? I know you are NOT suggesting that we
   could cast it to (int) and use "%f" to print it and you are NOT suggesting
   that we could cast it to (char *), a string, and print it with "%f".

 - If the printf contains "%f" then the var "must" be a float.


Example:

  long double A = 123.456;
  int B = A;

Is the variable B an int, or something else. Why is it incorrect behaviour to print it with %f even if that means we get (a compiler warning when compiling and) 123.000 for output - would anyone prefer gibberish for output ?


b): do as I suggested and avoid the security issue, bad output, and the need for contorted code (as shown in comment #18).


c): The Emperor's clothes look wonderful.
Comment 22 Rob 2007-06-28 18:32:06 UTC
Why is it a bad idea to leave this flaw in GCC ?

Format String Bugs and Exploits
http://www.geocities.com/ravecoolr/fmt.doc

or if you like:
http://www.enderunix.org/docs/formatstr.txt

Allowing GCC to stay as-is and permit someone to use a user supplied format string to print an integer opens a whole field of exploits that could be closed by fixing this.
Comment 23 Andrew Pinski 2007-06-28 18:51:51 UTC
There is a -Wformat for a reason, use it.