having problems with math library

  • Thread starter damian birchler
  • Start date
D

damian birchler

I wrote a function that should test if a given number is a palindrom,
but when I ran it in a little testprogram it behaves strange.

#include <stdio.h>
#include <math.h>

#define MAXSIZE 100000000

int is_palindrom(double n) {
double m = 0;
double x;
double y;
double i;
for(i = 10; i <= MAXSIZE; i *= 10) {
x = modf(n/i, &y);
printf("x = modf(%lf, &y)\n\tx == %lf, y == %lf\n", n/i, x, y);
modf((x*10), &y);
printf("modf(%lf, &y)\n\ty == %lf\n", x*10, y);
m += y;
printf("is_palindrom: m currently is %lf\n", m);
m *= 10;
}
while(modf((m/10), &x) == 0)
m /= 10;
printf("is_palindrom: m finally is %lf\n", m);
return n == m;
}

void print_wether_pal(double n) {
printf("\ntesting wheter %lf is a palindrom...\n", n);
printf("\t%lf %s a palindrom\n", n, is_palindrom(n) ? "is" :
"isn't");
}

int main(int argc, char *argv[]) {
print_wether_pal(666);
print_wether_pal(404);
print_wether_pal(3);
print_wether_pal(40);
print_wether_pal(99999999);
return 0;
}

Every first time the second modf in the for-loop in is_palindrom is
called, it returns a number too small by one. This is a snippet of the
program's output:

testing wheter 666.000000 is a palindrom...
x = modf(66.600000, &y)
x == 0.600000, y == 66.000000
modf(6.000000, &y)
y == 5.000000 <<< should be 6
is_palindrom: m currently is 5.000000
x = modf(6.660000, &y)
x == 0.660000, y == 6.000000
modf(6.600000, &y)
y == 6.000000 <<< now is 6!

I know that this isn't really a bug and that the problem results lies
in the internal representation of floating point numbers in the
computer, but is there a way to overcome that problem?
 
M

Merrill & Michele

damian birchler said:
I wrote a function that should test if a given number is a palindrom,
but when I ran it in a little testprogram it behaves strange.

#include <stdio.h>
#include <math.h>

#define MAXSIZE 100000000

int is_palindrom(double n) {
double m = 0;
double x;
double y;
double i;
for(i = 10; i <= MAXSIZE; i *= 10) {
x = modf(n/i, &y);
printf("x = modf(%lf, &y)\n\tx == %lf, y == %lf\n", n/i, x, y);
modf((x*10), &y);
printf("modf(%lf, &y)\n\ty == %lf\n", x*10, y);
m += y;
printf("is_palindrom: m currently is %lf\n", m);
m *= 10;
}
while(modf((m/10), &x) == 0)
m /= 10;
printf("is_palindrom: m finally is %lf\n", m);
return n == m;
}

void print_wether_pal(double n) {
printf("\ntesting wheter %lf is a palindrom...\n", n);
printf("\t%lf %s a palindrom\n", n, is_palindrom(n) ? "is" :
"isn't");
}

int main(int argc, char *argv[]) {
print_wether_pal(666);
print_wether_pal(404);
print_wether_pal(3);
print_wether_pal(40);
print_wether_pal(99999999);
return 0;
}

Every first time the second modf in the for-loop in is_palindrom is
called, it returns a number too small by one. This is a snippet of the
program's output:

testing wheter 666.000000 is a palindrom...
x = modf(66.600000, &y)
x == 0.600000, y == 66.000000
modf(6.000000, &y)
y == 5.000000 <<< should be 6
is_palindrom: m currently is 5.000000
x = modf(6.660000, &y)
x == 0.660000, y == 6.000000
modf(6.600000, &y)
y == 6.000000 <<< now is 6!

I know that this isn't really a bug and that the problem results lies
in the internal representation of floating point numbers in the
computer, but is there a way to overcome that problem?

If I'm reading this correctly, the largest integer you're going to get
without extraordinary means is an unsigned long. Per K&R B11 , ULONG_MAX,
defined in limits.h, is in the 4 hundred millions. Usage in chp 2. MPJ
P.S. Palindrome received a terminal "e" with William the Conqueror.
 
T

Tim Rentsch

I wrote a function that should test if a given number is a palindrom,
but when I ran it in a little testprogram it behaves strange.

[program using 'double's snipped]

I know that this isn't really a bug and that the problem results lies
in the internal representation of floating point numbers in the
computer, but is there a way to overcome that problem?

How about using 'unsigned long' rather than 'double'?
 
D

Dag Viken

damian birchler said:
I wrote a function that should test if a given number is a palindrom,
but when I ran it in a little testprogram it behaves strange.

#include <stdio.h>
#include <math.h>

#define MAXSIZE 100000000

int is_palindrom(double n) {
double m = 0;
double x;
double y;
double i;
for(i = 10; i <= MAXSIZE; i *= 10) {
x = modf(n/i, &y);
printf("x = modf(%lf, &y)\n\tx == %lf, y == %lf\n", n/i, x, y);
modf((x*10), &y);
printf("modf(%lf, &y)\n\ty == %lf\n", x*10, y);
m += y;
printf("is_palindrom: m currently is %lf\n", m);
m *= 10;
}
while(modf((m/10), &x) == 0)
m /= 10;
printf("is_palindrom: m finally is %lf\n", m);
return n == m;
}

void print_wether_pal(double n) {
printf("\ntesting wheter %lf is a palindrom...\n", n);
printf("\t%lf %s a palindrom\n", n, is_palindrom(n) ? "is" :
"isn't");
}

int main(int argc, char *argv[]) {
print_wether_pal(666);
print_wether_pal(404);
print_wether_pal(3);
print_wether_pal(40);
print_wether_pal(99999999);
return 0;
}

Every first time the second modf in the for-loop in is_palindrom is
called, it returns a number too small by one. This is a snippet of the
program's output:

testing wheter 666.000000 is a palindrom...
x = modf(66.600000, &y)
x == 0.600000, y == 66.000000
modf(6.000000, &y)
y == 5.000000 <<< should be 6
is_palindrom: m currently is 5.000000
x = modf(6.660000, &y)
x == 0.660000, y == 6.000000
modf(6.600000, &y)
y == 6.000000 <<< now is 6!

I know that this isn't really a bug and that the problem results lies
in the internal representation of floating point numbers in the
computer, but is there a way to overcome that problem?

You have to be very careful with floating point precision. The following
code works for me (VC7):

int is_palindrom(double n)
{
double m = 0;
double x;
for (x = floor(fabs(n)); x > 0; x = floor(x/10))
m = m*10 + fmod(x, 10);
printf("is_palindrom: m finally is %lf\n", m);
return n == m;
}

Note that fmod() will retain precision better than modf() because the
division is managed inside the function instead of outside. The fmod()
function gives you the most accurate remainder of a division, whereas modf()
only separates the integer and fractional parts of a number (i.e. after a
division). If both parameters of fmod() are integers and can be fully
represented within the precision of IEEE doubles (53 bits = 15-16 digits)
then the fmod() result is exact. I think I got this right.

If unsigned long precision (32 bits (=9-10 digits) on 32-bit platforms) is
sufficient I would recommend using something like this instead:

int is_palindrom(unsigned long n)
{
unsigned long m = 0;
unsigned long x;
for (x = n; x > 0; x /= 10)
m = m*10 + x%10;
printf("is_palindrom: m finally is %lu\n", m);
return n == m;
}

Dag
 
D

damian birchler

Tim Rentsch said:
I wrote a function that should test if a given number is a palindrom,
but when I ran it in a little testprogram it behaves strange.

[program using 'double's snipped]

I know that this isn't really a bug and that the problem results lies
in the internal representation of floating point numbers in the
computer, but is there a way to overcome that problem?

How about using 'unsigned long' rather than 'double'?
Originally this was exactely what I wanted to do. But because most
arguments and results to the functions in the math library are either
of type float or double I decided to declare my varibles to double so
that I don't have to cast them everytime they are used. Actually I
tried this once (using unsigned longs), but my program wouldn't
finish, it stuck as if it was in a endless loop. And there is another
problem. How would x = modf(n/i, &y) work, given n and i are unsigned
longs? Integer divisons don't result in floating point numbers. do
they?
 
J

Joe Wright

Merrill said:
[ snip ]

If I'm reading this correctly, the largest integer you're going to get
without extraordinary means is an unsigned long. Per K&R B11 , ULONG_MAX,
defined in limits.h, is in the 4 hundred millions. Usage in chp 2. MPJ
P.S. Palindrome received a terminal "e" with William the Conqueror.

#define ULONG_MAX 4294967295UL

Surely in the 4 thousand millions.
 
M

Merrill & Michele

:
#define ULONG_MAX 4294967295UL

Surely in the 4 thousand millions.

Correct you are. I'll remember the ballpark figure by it being somewhat
smaller than Halliburton's no-bid contract. MPJ
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top