views:

1569

answers:

6

How can I write a C++ function returning true if a real number is exactly representable with a double?

bool isRepresentable( const char* realNumber )
{
   bool answer = false;
   // what goes here?
   return answer;
}

Simple tests:

assert( true==isRepresentable( "0.5" ) );
assert( false==isRepresentable( "0.1" ) );
A: 

Convert the string into a float with a larger scope than a double. Cast that to a double and see if they match.

Treb
Would be nice to know why this was voted down. If my answer is wrong, please tell me why.
Treb
long double == double on some systems
BCS
All very well, but there may not be such a float.
DJClayworth
In most compilers used for homework assignments, there will
Treb
A: 

This should do the trick:

bool isRepresentable(const char *realNumber)
{
    double value = strtod(realNumber, NULL);

    char test[20];
    sprintf(test, "%f", value);

    return strcmp(realNumber, test) == 0;
}

Probably best to use the 'safe' version of sprintf to prevent a potential buffer overrun (is it even possible in this case?)

Richard Poole
That doesn't work if the number has a precision different from 6 (the default precision of the %lf format)
Adam Rosenfield
You could dynamically set the "%f" string to have the right width. You'd need to handle whitespace, leading vs. not zeros, +, -, and other special cases to be fully robust.
Mr Fooz
snprintf would be a 'safe' version - http://publib.boulder.ibm.com/infocenter/systems/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/printf.htm
Cristian Ciupitu
This also doesn't work if there are different numbers of trailing zeros in the original string from what is output by test.
DJClayworth
All good points.
Richard Poole
A: 

I'd convert the string to its numeric bit representation, (a bit array or a long), then convert the string to a double and see if they match.

Trap
..and what if (on your machine) long double is the same size as double?
DJClayworth
Sorry, I posted that comment to the wrong answer.
DJClayworth
A: 

Here is my version. sprintf converts 0.5 to 0.50000, zeros at the end have to be removed.

EDIT: Has to be rewritten to handle numbers without decimal point that end with 0 correctly (like 12300).

bool isRepresentable( const char* realNumber )
{
   bool answer = false;

   double dVar = atof(realNumber);
   char check[20];
   sprintf(check, "%f", dVar);

   // Remove zeros at end - TODO: Only do if decimal point in string
   for (int i = strlen(check) - 1; i >= 0; i--) {
     if (check[i] != '0') break;
     check[i] = 0;
   }

   answer =  (strcmp(realNumber, check) == 0);

   return answer;
}
schnaader
Nearly - what about 1.200e10 ?
DJClayworth
+5  A: 

Holy homework, batman! :)

What makes this interesting is that you can't simply do an (atof|strtod|sscanf) -> sprintf loop and check whether you got the original string back. sprintf on many platforms detects the "as close as you can get to 0.1" double and prints it as 0.1, for example, even though 0.1 isn't precisely representable.

#include <stdio.h>

int main() {
    printf("%llx = %f\n",0.1,0.1);
}

prints: 3fb999999999999a = 0.100000

on my system.

The real answer probably would require parsing out the double to convert it to an exact fractional representation (0.1 = 1/10) and then making sure that the atof conversion times the denominator equals the numerator.

I think.

Mike G.
+4  A: 

Parse the number into the form a + N / (10^k), where a and N are integers, and k is the number of decimal places you have.

Example: 12.0345 -> 12 + 345 / 10^4, a = 12, N = 345, k = 4

Now, 10^k = (2 * 5) ^ k = 2^k * 5^k

You can represent your number as exact binary fraction if and only if you get rid of the 5^k term in the denominator.

The result would check (N mod 5^k) == 0

Alexei
what about 11111111111111111111111111111111111111111111111111111111111111111?
BCS
Or a better way to put it (100/epsilon + 1)
BCS