[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Printing -0 as 0?

David Mathog

6/9/2011 5:21:00 PM

IEEE 754 math rounds -.1, or -0 itself, to -0. Not 0. Fine, that has
some specific uses numerically, as in 1/-inf. However, a displaying
value of -0 is a mistake in most real world applications, were no one
expects "your balance is -0.00 dollars"!

http://www.gnu.org/s/hello/manual/libc/Rou...

says "Negative zero behaves IDENTICALLY to zero except when the
copysign or signbit functions are used to check the sign bit
directly." (Emphasis added). Sadly nobody told printf about this:

double dv=0;
dv=-dv;
printf("Prints -0, not 0: %f\n",dv)

I could not find any IEEE bits to set to disable math using -0. Is
there a flag somewhere in C99
for printf that causes -0 to print as 0 (without also causing -1 to
print as 1)? One solution is to add 0.0 to all printf arguments, but
that is really ugly. For instance this:

#include <stdlib.h>
#include <stdio.h>
int main(void){
double dv=0.0;
fprintf(stdout,"%f\n",dv);
fprintf(stdout,"%f\n",-dv);
fprintf(stdout,"%f\n",-dv+0.0);
}

prints

0.000000
-0.000000
0.000000

Thanks,

David Mathog
8 Answers

David Mathog

6/9/2011 5:30:00 PM

0

On Jun 9, 10:21 am, David Mathog <dmat...@gmail.com> wrote:

>   fprintf(stdout,"%f\n",-dv+0.0);

Turns out that isn't a general solution.

dv=-0.000000000000001;
fprintf(stdout,"%f\n",dv+0.0);

prints as -0.

Regards,

David Mathog

David Mathog

6/9/2011 5:38:00 PM

0

(Sorry if this double posts, an earlier reply never showed up)

On Jun 9, 10:21 am, David Mathog <dmat...@gmail.com> wrote:

>   fprintf(stdout,"%f\n",-dv+0.0);

Not a general solution unfortunately:

dv=-0.000000000000001;
fprintf(stdout,"%f\n",dv+0.0);

prints as -0.

Regards,

David Mathog

blp

6/9/2011 5:45:00 PM

0

David Mathog <dmathog@gmail.com> writes:

> On Jun 9, 10:21 am, David Mathog <dmat...@gmail.com> wrote:
>
>>   fprintf(stdout,"%f\n",-dv+0.0);
>
> Turns out that isn't a general solution.

This ought to work, since -0 == 0, barring compiler "optimization":
printf("%f\n", dv == 0.0 ? 0.0 : dv);
--
"When in doubt, treat ``feature'' as a pejorative.
(Think of a hundred-bladed Swiss army knife.)"
--Kernighan and Plauger, _Software Tools_

David Mathog

6/9/2011 6:28:00 PM

0

On Jun 9, 10:45 am, b...@cs.stanford.edu (Ben Pfaff) wrote:

> This ought to work, since -0 == 0, barring compiler "optimization":
>         printf("%f\n", dv == 0.0 ? 0.0 : dv);

Doesn't handle dv=-0.000000001 correctly, that prints as -0.
The printf man page says that it rounds and then prints, so the
value doesn't become precisely -0.0 until after it enters printf.
(And maybe not even then, depending on how printf goes about its
business.)

Regards,

David Mathog

phrogg

6/9/2011 7:54:00 PM

0

On Thu, 9 Jun 2011 10:29:49 -0700 (PDT), David Mathog
<dmathog@gmail.com> wrote:

>On Jun 9, 10:21 am, David Mathog <dmat...@gmail.com> wrote:
>
>>   fprintf(stdout,"%f\n",-dv+0.0);
>
>Turns out that isn't a general solution.
>
> dv=-0.000000000000001;
> fprintf(stdout,"%f\n",dv+0.0);
>
>prints as -0.
>
>Regards,
>
>David Mathog

I ran into the same problem calclating the x y coordinates of evenly
spaced points around an inscribed circle. (for drilling holers in a
circular pattern an a pipe flange).

My solution is not elegant but it works well enough for the
requirements.



double side_a;
double side_b;

/* the sides of the tringle that is calculated where side_a is the x
coordinate abd side_b the y coordinate */

long a;
long b;

double side_a_fixed;
double side_b_fixed;

int precision; /*decimal precission usually 3 for inches and 2 for
metric */


a = side_a * 10 * precision;
b = side_b * 10 * precision;


side_a_fixed = a / 10 / precision;
side_b_fixed = b / 10 / precision;

printf("%11.*f , %11.*f", precision, side_a_fixed, precision,
side_b_fixed);

David Mathog

6/9/2011 8:34:00 PM

0

On Jun 9, 12:53 pm, Mickey Mouse <f...@email.com> wrote:

> a = side_a * 10 * precision;
> b = side_b * 10 * precision;
>
> side_a_fixed = a / 10 / precision;
> side_b_fixed = b / 10 /  precision;

Ah, I see. Here's another way with more tests but not requiring
multiply
and divide at each fprintf:

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#define fltprec(A,B) ((A) >= 0.0 ? 0.0 + (A) :\
((A) < (B) ? (A) : 0.0 ))
int main(void){
double dv=0.0;
double dlim;
int precision=6;
fprintf(stdout,"%f\n",0.0000005);
fprintf(stdout,"%f\n",0.00000051);
fprintf(stdout,"%f\n",0.00000049);

dlim=-5*pow(10.0,-(precision+1));
fprintf(stdout,"dlim %.9f\n",dlim);

dv=-0.0; fprintf(stdout,"%f\n",fltprec(dv,dlim));
dv= 0.0; fprintf(stdout,"%f\n",fltprec(dv,dlim));
dv=-0.00000005; fprintf(stdout,"%f\n",fltprec(dv,dlim));
dv= 0.00000005; fprintf(stdout,"%f\n",fltprec(dv,dlim));
dv=-0.0000005; fprintf(stdout,"%f\n",fltprec(dv,dlim));
dv= 0.0000005; fprintf(stdout,"%f\n",fltprec(dv,dlim));
dv=-0.000005; fprintf(stdout,"%f\n",fltprec(dv,dlim));
dv= 0.000005; fprintf(stdout,"%f\n",fltprec(dv,dlim));
}

Which emits:

0.000000
0.000001
0.000000
dlim -0.000000500
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
-0.000005
0.000005


The odd thing is the rounding of .0000005, which rounds down
when I expected it would round up. In the first pass the define
was (A)<=(B), but that emitted a -0.000000 when -.0000005 was printed.

I need to test this more and make sure it works at all precisions, or
if
the strange rounding is a numerical representation problem.

Thanks for the hint,

David Mathog

David Mathog

6/9/2011 8:55:00 PM

0

On Jun 9, 12:53 pm, Mickey Mouse <f...@email.com> wrote:

This seems to do the trick:

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#define fltprec(A,B) ((A) > (B) ? (A) : ((A) < -(B) ? (A) : 0.0 ))
int main(void){
double dv=0.0;
double dlim;
int precision;
for(precision=1; precision<15; precision++){
dlim=5*pow(10.0,-(precision+1));
fprintf(stdout,"dlim %.15f ",dlim);
fprintf(stdout," %.*f",precision,1.0001*dlim);
fprintf(stdout," %.*f",precision,dlim);
fprintf(stdout," %.*f\n",precision,0.9999*dlim);
}

for(precision=1; precision<15; precision++){
dlim=5*pow(10.0,-(precision+1));
fprintf(stdout,"dlim %.15f ",dlim);
fprintf(stdout," %.*f ",precision,fltprec(-dlim*10.0,dlim));
fprintf(stdout," %.*f ",precision,fltprec(dlim*10.0,dlim));
fprintf(stdout," %.*f ",precision,fltprec(-dlim,dlim));
fprintf(stdout," %.*f ",precision,fltprec(dlim,dlim));
fprintf(stdout," %.*f ",precision,fltprec(-dlim/10.0,dlim));
fprintf(stdout," %.*f\n",precision,fltprec(dlim/10.0,dlim));
}
}

The first loop shows that there are rounding issues for various
precisions
right at the cutoff (sometimes rounds up, sometimes down). So the
slightly modified #define handles those, resulting in zero in all
columns which were supposed to be (not -0).

Hopefully that is the final solution.

Regards,

David Mathog

David Mathog

6/9/2011 9:45:00 PM

0

On Jun 9, 1:55 pm, David Mathog <dmat...@gmail.com> wrote:
> On Jun 9, 12:53 pm, Mickey Mouse <f...@email.com> wrote:

> Hopefully that is the final solution.

Aargh, of course it wasn't. NAN compares false with everything, so
NAN
was printing as 0.

This change handles nan, +inf, and -inf too.

#define fltprec(A,B) ((A) > (B) ? (A) :\
((A) < -(B) ? (A) :\
(isnan(A) ? (A) : 0.0) ))

I guess it isn't that much less efficient than the preceding version,
since 1/2 - epsilon of the domain will pass the first test, and the
other 1/2 - epsilon will pass the second, leaving only 2*epsilon of
the domain to undergo the final test.

Still, it sure would be nice if printf had a "do not print negative
zero" switch!

Regards,

David Mathog