Correct precision with printf

B

banansol

I read the thread Floating point rounding error and
I saw Richard Heathfield's description with several
lines like:
0.1 = 1/2 = 0.5 - too large
0.01 = 1/4 = 0.25 - too large
0.001 = 1/8 = 0.125 - too large
....

And I wanted to write such a program that output
lines like that. My attempt is below, and I don't
know how I should print the third column, with
printf I either get too low precision or a lot of
zeros after the first lines. Right now I just
have %.20f in the format string, but I wonder
how I can do it like Heathfield did and remove
trailing zeroes. Otherwise I wonder if my program
is a correct C program. Thanks!

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>

/* Examines the binary representation in the array to
see if it is smaller or larger than value.
Prints a new line containing the info and returns
the result of the comparison. */
int examine(char *data, size_t length, double value)
{
size_t i;
int num, den;
double result;

/* Shorten the length */
i = length;
while (i-- && data == 0) {
length--;
}
assert(length != 0);

num = 0;
den = 1;
printf("0.");
for (i = 0; i < length; i++) {
den *= 2;
num *= 2;
if (data != 0) {
putchar('1');
num++;
}
else
putchar('0');
}

result = (double)num/den;
printf(" = %d/%d = %.20f", num, den, result);
if (result == value) {
printf(" - same\n");
return 0;
}
else if (result > value) {
printf(" - too large\n");
return 1;
}
else {
printf(" - too small\n");
return -1;
}
}

int main(int argc, char *argv[])
{
char array[17] = { 0 };
double val;
int i;
int cmp;

if (argc < 2)
return EXIT_FAILURE;

val = strtod(argv[1], NULL);
if (errno != 0)
return EXIT_FAILURE;

array[0] = 1;
for (i = 1; i < 17; i++) {
cmp = examine(array, i, val);
if (cmp == 0)
return 0;
else if (cmp < 1) {
array = 1;
}
else {
array[i - 1] = 0;
array = 1;
}
}
return EXIT_SUCCESS;
}
 
R

Richard Heathfield

(e-mail address removed) said:
I read the thread Floating point rounding error and
I saw Richard Heathfield's description with several
lines like:
0.1 = 1/2 = 0.5 - too large
0.01 = 1/4 = 0.25 - too large
0.001 = 1/8 = 0.125 - too large
...

And I wanted to write such a program that output
lines like that. My attempt is below,

Applause!

For extra credit, make it work to an arbitrary precision.
 
A

Army1987

I read the thread Floating point rounding error and
I saw Richard Heathfield's description with several
lines like:
0.1 = 1/2 = 0.5 - too large
0.01 = 1/4 = 0.25 - too large
0.001 = 1/8 = 0.125 - too large
...

And I wanted to write such a program that output
lines like that. My attempt is below, and I don't
know how I should print the third column, with
printf I either get too low precision or a lot of
zeros after the first lines. Right now I just
have %.20f in the format string, but I wonder
how I can do it like Heathfield did and remove
trailing zeroes. Otherwise I wonder if my program
is a correct C program. Thanks!
Use the %g conversion instead of %f.
BTW, have you tried it with an argument <= 0 or >= 1?
(And before returning EXIT_FAILURE I'd give the user a clue of why
the program failed.)
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>

/* Examines the binary representation in the array to
see if it is smaller or larger than value.
Prints a new line containing the info and returns
the result of the comparison. */
int examine(char *data, size_t length, double value)
{
size_t i;
int num, den;
double result;

/* Shorten the length */
i = length;
while (i-- && data == 0) {
length--;
}
assert(length != 0);

num = 0;
den = 1;
printf("0.");
for (i = 0; i < length; i++) {
den *= 2;
num *= 2;
if (data != 0) {
putchar('1');
num++;
}
else
putchar('0');
}

result = (double)num/den;
printf(" = %d/%d = %.20f", num, den, result);
if (result == value) {
printf(" - same\n");
return 0;
}
else if (result > value) {
printf(" - too large\n");
return 1;
}
else {
printf(" - too small\n");
return -1;
}
}

int main(int argc, char *argv[])
{
char array[17] = { 0 };
double val;
int i;
int cmp;

if (argc < 2)
return EXIT_FAILURE;

val = strtod(argv[1], NULL);
if (errno != 0)
return EXIT_FAILURE;

array[0] = 1;
for (i = 1; i < 17; i++) {
cmp = examine(array, i, val);
if (cmp == 0)
return 0;
else if (cmp < 1) {
array = 1;
}
else {
array[i - 1] = 0;
array = 1;
}
}
return EXIT_SUCCESS;
}
 
B

banansol

Use the %g conversion instead of %f.
Thanks! I missed that when I read about the specifiers,
didn't know it was that easy.
BTW, have you tried it with an argument <= 0 or >= 1?
(And before returning EXIT_FAILURE I'd give the user a clue of why
the program failed.)
Yeah, I knew of those two points, I should have said that.
But thanks for pointing that out.
 
B

Ben Bacarisse

I read the thread Floating point rounding error and
I saw Richard Heathfield's description with several
lines like:
0.1 = 1/2 = 0.5 - too large
0.01 = 1/4 = 0.25 - too large
0.001 = 1/8 = 0.125 - too large
...

And I wanted to write such a program that output
lines like that. My attempt is below, and I don't
know how I should print the third column, with
printf I either get too low precision or a lot of
zeros after the first lines. Right now I just
have %.20f in the format string, but I wonder
how I can do it like Heathfield did and remove
trailing zeroes.

%g has already been suggested, but %.*g (or %.*f) has not. This lets
you pass the desired precision as a parameter to *printf function.
The variable 'length' is a good first stab at the right value.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,740
Latest member
AdolphBig6

Latest Threads

Top