How to determine w/o conversions that a given floating constant can be represented?

137 Views Asked by At

How to determine w/o conversions that a given floating constant can be represented?

Sample code:

#define FLOATING_CONSTANT1  2147483647.0f
#define FLOATING_CONSTANT2  2147483520.0f

bool b1 = FLOATING_CONSTANT_CAN_BE_REPRESENTED( FLOATING_CONSTANT1 );  // false
bool b2 = FLOATING_CONSTANT_CAN_BE_REPRESENTED( FLOATING_CONSTANT2 );  // true
3

There are 3 best solutions below

2
chux - Reinstate Monica On BEST ANSWER

"w/o conversions" seems to be an unnecessary restrictive requirement, but something to get OP started and a test harness for others.


#define str(s) #s
#define xstr(s) str(s)
#define FLOATING_CONSTANT_CAN_BE_REPRESENTED(fc1) float_const_test(xstr(fc1), fc1)

bool float_const_test(const char *s, float f1) {
  printf("<%s> %g\n", s, f1);
  char *endptr;
  errno = 0;
  double d2 = strtod(s, &endptr);
  if (s == endptr) return false;
  if (strcmp(endptr, "f") && strcmp(endptr, "F")) return false;
  if (f1 != d2) return false;
  // Note a 100% here, the string may have rounded to a double.
  return true;
}

int main(void) {
  puts("Good");
  printf("%d\n", FLOATING_CONSTANT_CAN_BE_REPRESENTED(1.5f));
  printf("%d\n", FLOATING_CONSTANT_CAN_BE_REPRESENTED(0x1.5p0f));
  puts("\nBad");
  printf("%d\n", FLOATING_CONSTANT_CAN_BE_REPRESENTED(1.23f));
  printf("%d\n", FLOATING_CONSTANT_CAN_BE_REPRESENTED(1.23e40f));
}

Output

Good
<1.5f> 1.5
1
<0x1.5p0f> 1.3125
1

Bad
<1.23f> 1.23
0
<1.23e40f> inf
0

Notes:

If the fractional portion of a FP constant does not end in 5 or 0, it cannot be exactly converted to a float/double.

When exact FP constants are needed, the first step is to consider hexadecimal-floating-constant such a 0x1.23CDp12f

5
Steve Summit On

This probably isn't the answer you're looking for, and it may seem facetious, but in my own work, I pretty much imagine that I have this macro available to me. It has the handy advantage of working equally well on float, double, and long double operands:

#define fp_is_exact(f) 0

But I confess I very rarely actually write things like

if(fp_is_exact(x))
    do something the easy way;
else
    do something the hard way;

I'm going to have to write the code to do it the hard way, anyway, and the "easy" code gets used so rarely that I might as well get out of the habit of imagining that it might ever be useful for anything.

In all seriousness: assume that floating-point numbers are never exact. Exactness is not what they're for. Just about anything you might ever need to represent as a floating-point variable is not an exactly-known quantity anyway, especially if it's something you measured in the real world like a distance or weight or velocity or something. (My favorite example: How far is it from Los Angeles to New York? In inches?)


Addendum: Climbing down off of my high horse, I have to admit, sometimes it's appropriate to assume that floating-point numbers are exact. Comparisons like if(f == 0) or if(f == 1) or if(f == 3) are perfectly fine in practice. I'd have no hesitation comparing a floating-point variable for exact equality to an integer less than 10 -- or less than 100, if it came to that, and maybe also 0.5. But for just about any other number I might have in mind, or that I might be manipulating in a floating-point variable in a C program, the chance that it's exactly representable as a float or double is just about vanishingly small.

0
John Bollinger On

How to determine w/o conversions that a given floating constant can be represented?

A floating constant accepted by the compiler has type float, double, long double or some extension floating type as far as C semantics are concerned. It is represented.

But I suppose the question is meant to be whether the lexical decimal expression of the constant appearing in the source code corresponds to an exactly representable floating-point value of some chosen floating type. I feel pretty confident in saying that such a determination cannot be made by the preprocessor, because the preprocessor deals in units of (preprocessor) tokens. Although under some circumstances it operates by converting text to numbers, the preprocessor has no accessible mechanism for breaking tokens into smaller pieces.

It is possible, however, to stringify the token of interest at compile time and analyze the resulting string representation at run time. It is also possible in principle to write a standalone tool or adapt an existing standalone preprocessor to perform the kind of test you want.

It is even conceivable that a compiler might offer an option for warning about lexical floating point numbers that cannot be represented exactly, which alternative was considered in another question. (Spoiler: this is not an alternative that compiler writers have historically considered worthwhile to implement.)