This is a bit tricky, because as you should already know, floating-point numbers are often not exact, but rather some approximation of a number. For example, 10.1
ends up as 10.09999...
. A double has about 15 digits of precision, so 15 is the largest value your sigfigs()
function could reasonably return. And it will need to be overloaded for double and float, because of course float has only half as many digits of precision:
int sigfigs(double x); // returns 1 to 15
int sigfigs(float x); // returns 1 to 7
Now there may be more clever mathematical ways to do this, but one idea is:
int sigfigs(double x) {
int mag = log10(fabs(x));
double div = pow(10, mag);
char str[20];
int wrote = snprintf(str, sizeof(str), "%.15g", x/div);
return std::count_if(str, str + wrote, isdigit);
}
This is definitely missing some cases, but I think captures the idea: we first normalize large/small numbers so that we end up with something close to 1, then we print it with a suitable format string to allow the maximum usable precision to be displayed, then we count how many digits there are.
There are notable boundary-condition bugs at 0
, 10
, etc., which are left as an exercise to correct. Serendipitously, NAN produces 0 which is good; +/- infinity also produce 0.
One final note; this does not strictly conform to the usual definition of significant figures. In particular, trailing zeros after a decimal place are not accounted for, because there is no way to do so given only a double
or float
. For example, textual inputs of 10
and 10.00000
produce bitwise-identical results from atof()
. Therefore, if you need a complete solution conforming to the academic definition of sigfigs, you will need to implement a user-defined type containing e.g. a double and an int for the sigfigs. You can then implement all the arithmetic operators to carry the sigfigs throughout your calculations, following the usual rules.