Of course I didn't start re-reading K&R right then as I should have. But I did get around to it recently. The very first problem in chapter two asked the reader to calculate the minimum signed chars, shorts, ints, and longs, and the maximum of those plus their unsigned cousins. These are all very straightforward things to calculate for anyone with an understanding of modular arithmetic. The book then mentions a more challenging problem: calculating minimum and maximum floats. No problemo I thought:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <float.h> | |
float calcMinFloat(); | |
int main() | |
{ | |
float f; | |
f = FLT_MIN; | |
printf("Min float %e\n", f); | |
f = calcMinFloat(); | |
printf("Min float calculated %e\n", f); | |
return 0; | |
} | |
float calcMinFloat() | |
{ | |
float a,b,c; | |
a = 1.0; | |
b = 0.0; | |
while (a - b != 0.0){ | |
c = 0.5*(a - b); | |
if (c < 0.0){ | |
b = b - c; | |
} else if (c == 0.0) { | |
b = a; | |
} else { | |
a = a -c; | |
} | |
} | |
return a; | |
} |
Experienced C developers will surely recognize what is going on here but I was flummoxed. It was only upon looking at some of the other constants defined in float.h that I started to figure out what was going on. In particular I noticed that on my MBA, FLT_MIN * FLT_EPS = 1.401298e-45. FLT_EPS is "float epsilon" and is defined as the smallest x for which 1+x != x. This is sort of the standard error on the system, i.e. any calculation could be off by a factor of FLT_EPS. I decided to calculate this in my program and then to also use it to calculate FLT_MAX:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <float.h> | |
#define INF 1.0/0.0 | |
float calcMinFloat(); | |
float calcFloatErr(); | |
float calcMaxFloat(); | |
int main() | |
{ | |
float f; | |
f = FLT_MIN; | |
printf("Min float %e\n", f); | |
f = calcMinFloat(); | |
printf("Min float calculated %e\n", f); | |
f = calcFloatErr(); | |
printf("Float epsilon = %e\n",f); | |
f = FLT_MAX; | |
printf("Max float %e\n", f); | |
f = calcMaxFloat(); | |
printf("Max float calculated %e\n", f); | |
return 0; | |
} | |
float calcMinFloat() | |
{ | |
float a,b,c; | |
a = 1.0; | |
b = 0.0; | |
while (a - b != 0.0){ | |
c = 0.5*(a - b); | |
if (c < 0.0){ | |
b = b - c; | |
} else if (c == 0.0) { | |
b = a; | |
} else { | |
a = a -c; | |
} | |
} | |
b = calcFloatErr(); | |
return a/b; | |
} | |
float calcFloatErr() | |
{ | |
float err; | |
err = 1.0/2.0; | |
while ((float) (1.0 + err/2.0) != 1.0){ | |
err = err / 2.0; | |
} | |
return err; | |
} | |
float calcMaxFloat() | |
{ | |
float a, b, err; | |
a = b = err = 1.0 + calcFloatErr(); | |
b = a * err; | |
while (b != a && b != INF){ | |
a = a * err; | |
b = a * err; | |
} | |
return a; | |
} |