3

Suppose I is some integer type and F some (real) floating point type.

I want to write two functions. The first function shall take a value i of type I and return a boolean indicating whether i converted to F falls into the representable range, i.e. whether (F)i will have defined behavior.

The second function shall take a value f of type F and return a boolean indicating whether f converted to I falls into the representable range, i.e. whether (I)f will have defined behavior.

Is it possible to write such a function that will be, on every implementation conforming to the standard, correct and not exhibit undefined behavior for any input? In particular I do not want to assume that the floating point types are IEEE 754 types.

I am asking about both C and C++ and their respective standard versions separately, in case that changes the answer.

Basically the intention of this question is to figure out whether (sensible) floating-point / integral conversions are possible without relying on IEEE 754 or other standards or hardware details at all. I ask out of curiosity.

Comparing against e.g. INT_MAX or FLT_MAX does not seem to be possible, because it is not clear which type to do the comparison in without already knowing which of the types has wider range.

walnut
  • 21,629
  • 4
  • 23
  • 59
  • 1
    `std::numeric_limits` should give you all the information you need to know if the values are convertible (calculation will be required). – NathanOliver Nov 06 '19 at 22:03
  • @NathanOliver-ReinstateMonica I am not sure what I should use for comparison. As I mentioned I don't think I can compare against `max`, because that would already require the operand to be converted to that type, which may not be possible. I could compare the log2 of the integer against `max_exponent`, but I am not sure that I can draw any conclusion when they compare equal. – walnut Nov 06 '19 at 22:16
  • 2
    https://stackoverflow.com/questions/51104995/can-a-conversion-from-double-to-int-be-written-in-portable-c related or even a duplicate – Volodymyr Boiko Nov 06 '19 at 22:24
  • @GreenTree Thanks, I must have searched for the wrong terms. This and the other linked questions seem like duplicates. I still need to read through them in detail though. – walnut Nov 06 '19 at 22:29
  • Is there anything wrong with just doing the conversion both directions and checking for equality? I have trouble thinking of a case where that would fail. – Mark Ransom Nov 06 '19 at 23:26
  • @MarkRansom That might work, yet may need to do twice: FP-->i->FP compare as FP and a i->FP->i compare as integer – chux - Reinstate Monica Nov 06 '19 at 23:33
  • @chux-ReinstateMonica I was thinking of two functions, one for each conversion direction. Each would only need a single check. – Mark Ransom Nov 06 '19 at 23:35
  • @MarkRansom The conversion has undefined behavior if the value falls outside the representable range, so it may not be used without prior testing whether the function I am looking for returns `true`. – walnut Nov 06 '19 at 23:37

1 Answers1

1

some float to some int is fairly easy is we can assume FLT_RADIX != 10 (2N floating point) and the range of FP exceeds the integer range.

Form exact FP limits
Test if FP has a fraction part that is 0**. (also handles NaN, inf)
Test if too positive.
Test if too negative.
Test if converted to integer value rounds.

Pseudo code

// For now, assume 2's complement.
// With some extra macro magic, could handle all integer encodings. 

// Use integer limits whose magnitudes are at or 1 away from a power-of-2  
//   and form FP power-of-2 limits 
// The following will certainly not incur any rounding
#define FLT_INT_MAXP1 ((INT_MAX/2 + 1)*2.0f)
#define FLT_INT_MIN (INT_MIN*1.0f)

status float_to_int_test(float f) {
  float ipart;
  if (modff(f, &ipart) != 0.0) {
    return not_a_whole_number;
  }
  if (f >= FLT_INT_MAXP1) return too_big;
  if (f < FLT_INT_MIN) return too_negative;
  if (f != (volatile float) f)) return rounding_occurred;
  return success;
}

Armed with the above float_to_int test....

status int_to_float_test(int i) { 
  volatile float f = (float) i;
  if (float_to_int_test(f) != success) return fail
  volatile int j = (int) f;
  if (i != j) return fail;
  return success;
}

Simplifications possible, but something to get OP started.

Extreme cases which need additional code include int128_t or wider having more range than float and FLT_RADIX == 10.


** Hmmm - appears OP does not cares about fractional part. In that case conversion from double to int appears as a good duplicate for half the problem.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Yes, I am not interested in the fractional part. The case where the range of `I` is potentially larger than that of `F` is what I considered problematic. The linked question doesn't completely answer the question, but I guess could be considered a duplicate. If someone wants to close it as duplicate, that would be fine by me. – walnut Nov 06 '19 at 23:45
  • @uneven_mark Yes the "I is larger than that of F" is tricky - I'll add something later on that - real life is calling me away. [`int double_to_int(double val, int *err)`](https://stackoverflow.com/a/51310956/2410359) does handle the "not interested in the fractional part". – chux - Reinstate Monica Nov 06 '19 at 23:53