fork download
  1. // file: f2i.cpp
  2. //
  3. // compiled with MinGW x86 (gcc version 4.6.2) as:
  4. // g++ -Wall -O2 -std=c++03 f2i.cpp -o f2i.exe
  5. #include <iostream>
  6. #include <iomanip>
  7. #include <limits>
  8.  
  9. using namespace std;
  10.  
  11. template<class I, class F> I truncAndCap(F f)
  12. {
  13. /*
  14.   This function converts (by truncating the
  15.   fractional part) the floating-point value f (of type F)
  16.   into an integer value (of type I), avoiding undefined
  17.   behavior by returning std::numeric_limits<I>::min() and
  18.   std::numeric_limits<I>::max() when f is too small or
  19.   too big to be converted to type I directly.
  20.  
  21.   2 problems:
  22.   - F may fail to convert to I,
  23.   which is undefined behavior and we want to avoid that.
  24.   - I may not convert exactly into F
  25.   - Direct I & F comparison fails because of I to F promotion,
  26.   which can be inexact.
  27.  
  28.   This solution is for the most practical case when I and F
  29.   are radix-2 (binary) integer and floating-point types.
  30. */
  31. int Idigits = numeric_limits<I>::digits;
  32. int Isigned = numeric_limits<I>::is_signed;
  33.  
  34. /*
  35.   Calculate cutOffMax = 2 ^ std::numeric_limits<I>::digits
  36.   (where ^ denotes exponentiation) as a value of type F.
  37.  
  38.   We assume that F is a radix-2 (binary) floating-point type AND
  39.   it has a big enough exponent part to hold the value of
  40.   std::numeric_limits<I>::digits.
  41.  
  42.   FLT_MAX_10_EXP/DBL_MAX_10_EXP/LDBL_MAX_10_EXP >= 37
  43.   (guaranteed per C++ standard from 2003/C standard from 1999)
  44.   corresponds to log2(1e37) ~= 122, so the type I can contain
  45.   up to 122 bits. In practice, integers longer than 64 bits
  46.   are extremely rare (if existent at all), especially on old systems
  47.   of the 2003 C++ standard's time.
  48. */
  49. const F cutOffMax = F(I(1) << Idigits / 2) * F(I(1) << (Idigits / 2 + Idigits % 2));
  50.  
  51. if (f >= cutOffMax)
  52. return numeric_limits<I>::max();
  53.  
  54. /*
  55.   Calculate cutOffMin = - 2 ^ std::numeric_limits<I>::digits
  56.   (where ^ denotes exponentiation) as a value of type F for
  57.   signed I's OR cutOffMin = 0 for unsigned I's in a similar fashion.
  58. */
  59. const F cutOffMin = Isigned ? -F(I(1) << Idigits / 2) * F(I(1) << (Idigits / 2 + Idigits % 2)) : 0;
  60.  
  61. if (f <= cutOffMin)
  62. return numeric_limits<I>::min();
  63.  
  64. /*
  65.   Mathematically, we may still have a little problem (2 cases):
  66.   cutOffMin < f < std::numeric_limits<I>::min()
  67.   srd::numeric_limits<I>::max() < f < cutOffMax
  68.  
  69.   These cases are only possible when f isn't a whole number, when
  70.   it's either std::numeric_limits<I>::min() - value in the range (0,1)
  71.   or std::numeric_limits<I>::max() + value in the range (0,1).
  72.  
  73.   We can ignore this altogether because converting f to type I is
  74.   guaranteed to truncate the fractional part off, and therefore
  75.   I(f) will always be in the range
  76.   [std::numeric_limits<I>::min(), std::numeric_limits<I>::max()].
  77. */
  78.  
  79. return I(f);
  80. }
  81.  
  82. template<class I, class F> void test(const char* msg, F f)
  83. {
  84. I i = truncAndCap<I,F>(f);
  85. cout <<
  86. msg <<
  87. setiosflags(ios_base::showpos) <<
  88. setw(14) << setprecision(12) <<
  89. f << " -> " <<
  90. i <<
  91. resetiosflags(ios_base::showpos) <<
  92. endl;
  93. }
  94.  
  95. #define TEST(I,F,VAL) \
  96.   test<I,F>(#F " -> " #I ": ", VAL);
  97.  
  98. int main()
  99. {
  100. TEST(short, float, -1.75f);
  101. TEST(short, float, -1.25f);
  102. TEST(short, float, +0.00f);
  103. TEST(short, float, +1.25f);
  104. TEST(short, float, +1.75f);
  105.  
  106. TEST(short, float, -32769.00f);
  107. TEST(short, float, -32768.50f);
  108. TEST(short, float, -32768.00f);
  109. TEST(short, float, -32767.75f);
  110. TEST(short, float, -32767.25f);
  111. TEST(short, float, -32767.00f);
  112. TEST(short, float, -32766.00f);
  113. TEST(short, float, +32766.00f);
  114. TEST(short, float, +32767.00f);
  115. TEST(short, float, +32767.25f);
  116. TEST(short, float, +32767.75f);
  117. TEST(short, float, +32768.00f);
  118. TEST(short, float, +32768.50f);
  119. TEST(short, float, +32769.00f);
  120.  
  121. TEST(int, float, -2147483904.00f);
  122. TEST(int, float, -2147483648.00f);
  123. TEST(int, float, -16777218.00f);
  124. TEST(int, float, -16777216.00f);
  125. TEST(int, float, -16777215.00f);
  126. TEST(int, float, +16777215.00f);
  127. TEST(int, float, +16777216.00f);
  128. TEST(int, float, +16777218.00f);
  129. TEST(int, float, +2147483648.00f);
  130. TEST(int, float, +2147483904.00f);
  131.  
  132. TEST(int, double, -2147483649.00);
  133. TEST(int, double, -2147483648.00);
  134. TEST(int, double, -2147483647.75);
  135. TEST(int, double, -2147483647.25);
  136. TEST(int, double, -2147483647.00);
  137. TEST(int, double, +2147483647.00);
  138. TEST(int, double, +2147483647.25);
  139. TEST(int, double, +2147483647.75);
  140. TEST(int, double, +2147483648.00);
  141. TEST(int, double, +2147483649.00);
  142.  
  143. TEST(unsigned, double, -1.00);
  144. TEST(unsigned, double, +1.00);
  145. TEST(unsigned, double, +4294967295.00);
  146. TEST(unsigned, double, +4294967295.25);
  147. TEST(unsigned, double, +4294967295.75);
  148. TEST(unsigned, double, +4294967296.00);
  149. TEST(unsigned, double, +4294967297.00);
  150.  
  151. return 0;
  152. }
  153.  
Success #stdin #stdout 0.01s 2684KB
stdin
Standard input is empty
stdout
float -> short:          -1.75 -> -1
float -> short:          -1.25 -> -1
float -> short:             +0 -> +0
float -> short:          +1.25 -> +1
float -> short:          +1.75 -> +1
float -> short:         -32769 -> -32768
float -> short:       -32768.5 -> -32768
float -> short:         -32768 -> -32768
float -> short:      -32767.75 -> -32767
float -> short:      -32767.25 -> -32767
float -> short:         -32767 -> -32767
float -> short:         -32766 -> -32766
float -> short:         +32766 -> +32766
float -> short:         +32767 -> +32767
float -> short:      +32767.25 -> +32767
float -> short:      +32767.75 -> +32767
float -> short:         +32768 -> +32767
float -> short:       +32768.5 -> +32767
float -> short:         +32769 -> +32767
float -> int:    -2147483904 -> -2147483648
float -> int:    -2147483648 -> -2147483648
float -> int:      -16777218 -> -16777218
float -> int:      -16777216 -> -16777216
float -> int:      -16777215 -> -16777215
float -> int:      +16777215 -> +16777215
float -> int:      +16777216 -> +16777216
float -> int:      +16777218 -> +16777218
float -> int:    +2147483648 -> +2147483647
float -> int:    +2147483904 -> +2147483647
double -> int:    -2147483649 -> -2147483648
double -> int:    -2147483648 -> -2147483648
double -> int: -2147483647.75 -> -2147483647
double -> int: -2147483647.25 -> -2147483647
double -> int:    -2147483647 -> -2147483647
double -> int:    +2147483647 -> +2147483647
double -> int: +2147483647.25 -> +2147483647
double -> int: +2147483647.75 -> +2147483647
double -> int:    +2147483648 -> +2147483647
double -> int:    +2147483649 -> +2147483647
double -> unsigned:             -1 -> 0
double -> unsigned:             +1 -> 1
double -> unsigned:    +4294967295 -> 4294967295
double -> unsigned: +4294967295.25 -> 4294967295
double -> unsigned: +4294967295.75 -> 4294967295
double -> unsigned:    +4294967296 -> 4294967295
double -> unsigned:    +4294967297 -> 4294967295