// Suggested by Herve Bronnimann [hbr@poly.edu] // If you want to take advantage of compile-time, // template arguments of the form would work, and // specializations for various ints would give you the desired rounding. /* Doesn't work C2864 only const static integral data members can be initialized inside a class or struct static const int lower = 3; // allowed but not double template struct interval_pi { // integer version to be used as default. static const double lower = 3.; // integer default version. C2864 static const double upper = 4.; // 3 < pi < 4 }; but this compiles OK. template struct interval_pi { // integer version to be used as default. static const int lower = 3.; // integer default version. static const int upper = 4.; // 3 < pi < 4 }; // n decimal digits, l lower interval constant in decimal, u upper constant in decimal #define BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(n, l, u) \ template <> struct interval_pi\ { \ static const double lower = l; \ static const double upper = u; \ } BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(1, 3.1 , 3.2 ); BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(2, 3.14 , 3.15 ); BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(3, 3.141 , 3.142 ); BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(4, 3.1415 , 3.1416 ); BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(5, 3.14159 , 3.14160 ); BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(6, 3.1415926 , 3.1415927 ); BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(7, 3.14159265 , 3.14159266 ); BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(8, 3.141592653 , 3.141592654 ); BOOST_INTERVAL_SPECIALIZE_PI_CONSTANT(9, 3.1415926535 , 3.1415926536 ); std::cout << interval_pi<7>::lower << std::endl; static const float pi_f_l = 3.141592502593994140625F; static const float pi_f_u = 3.1415927410125732421875F; Guillaume Melquiond [gmelquio@ens-lyon.fr] I'm surprised it worked with gcc 2.95 and 3.2 since I did try such a solution and it didn't work. I needed to put them out of the classes and to use functions inside the class to return the global constants. It was ugly but sufficient. I seem to remember that it isn't allowed by the Standard to define floating-point constants inside a class. This was also said to be true. SO MSCV is correct in forbidding it. */ #include // for numeric_limits digits and radix #include // for quad_float, a user defined floating point type. using NTL::quad_float; using NTL::to_quad_float; namespace { // anonymous. // Pi Exactly representable values, calculated using 300 bit NTL. // IEEE float, radix 2, 24 significand digits. static const float pi_f_lower = 3.141592502593994140625F; static const float pi_f_upper = 3.1415927410125732421875F; static const float pi_f_nearest = 3.14159265358979323846264338327950288419716939937510F; // IEEE double, radix 2, 53 significand digits. static const double pi_d_lower = 3.141592653589793115997963468544185161590576171875; static const double pi_d_upper = 3.141592653589793560087173318606801331043243408203125; static const double pi_d_nearest = 3.14159265358979323846264338327950288419716939937510; // IEEE long double, radix 2, 64 significand digits. static const long double pi_l_nearest = 3.141592653589793238462643383279502884197169400753029295841032855793712028004489974652930461153208029583374L; static const long double pi_l_lower = 3.14159265358979323846264338327948122706369096109574134605154738068832909192451552371494472026824951171875L; static const long double pi_l_upper = 3.141592653589793238462643383279530530870267274333579579086877554827683667326709837652742862701416015625L; // Exactly representable in 128 bit SPARC & VAX H double extended format smallest interval values of pi. static const long double pi_ll_lower = 3.141592653589793238462643383279502797479068098137295573004504331874296718662975536062731407582759857177734375L; static const long double pi_ll_nearest = 3.1415926535897932384626433832795028841971694007530292958410328557937120280044899746529304611532080295833741639955L; static const long double pi_ll_upper = 3.141592653589793238462643383279503182665056975584466184200092848859760426283305179140370455570518970489501953125L; // Examples of values for integer, used to show a user specialization. // Only useful for US states with brain-dead legislators? static const int pi_i_lower = 3; static const int pi_i_nearest = 3; static const int pi_i_upper = 4; } // namespace template ::digits, int radix = std::numeric_limits::radix > class pi; // Default digits and radix are from std::numeric_limits, // but could be made different for special floating point types // that don't provide numeric_limits. // Specializations for builtin floating point types. template <> class pi { public: static const float lower() { return pi_f_lower; } static const float upper() { return pi_f_upper; } static const float nearest() { return pi_f_nearest; } }; // template <> class pi template <> class pi { // Specialization for double. public: static const double lower() { return pi_d_lower; } static const double upper() { return pi_d_upper; } static const double nearest() { return pi_d_nearest; } }; // template <> class pi template <> class pi { // Specialization for long double, X86 double precision, // used by MSVC which is 53, same as double, public: static const long double lower() { return pi_d_lower; } static const long double upper() { return pi_d_upper; } static const long double nearest() { return pi_d_nearest; } }; // template <> class pi // Other example specializations. // Note that specializing // template <> class pi means // must specify pi::upper() // and NOT specialize template <> class pi template <> class pi { // Specialization for long double, X86 extended double precision. // used by GCC, but NOT by MSVC which is 53, same as double, // so would be wrong on MSVC. public: static const long double lower() { return pi_l_lower; } static const long double upper() { return pi_l_upper; } static const long double nearest() { return pi_l_nearest; } }; // template <> class pi template <> class pi { // Specialization for integer - NOT very practical!!!! // but shows difference between unsigned int which deliberately is NOT specialized, // and fails helpfully as expected at compile time. public: static const int lower() { return pi_i_lower; } static const int upper() { return pi_i_upper; } static const int nearest() { return pi_i_nearest; } }; // template <> class pi template <> class pi { // Specialization for NTL quad_float (aka doubledouble), uses two doubles for 106 significand bits. // An example of a special user type, which could be specialized by the user, // probably in a separate header file, for example myfloat.hpp public: static const quad_float lower() { // to_quad_float converts from C string to quad_float - return to_quad_float("3.14159265358979323846264338327948122706369096109574134605154738068832909192451552371494472026824951171875"); } static const quad_float upper() { return to_quad_float("3.141592653589793238462643383279502884197169400753029295841032855793712028004489974652930461153208029583374"); } static const quad_float nearest() { return to_quad_float("3.141592653589793238462643383279505878966979117714660462569212467758006379625612680683843791484832763671875"); } }; // template <> class pi // template <> class pi would mean need to // Alternative scheme. template const FPtype pi_lower() { int digits = std::numeric_limits::digits; // significand binary digits. int radix = std::numeric_limits::radix; // only defined for radix 2, so far. FPtype lower = (radix == 2) ? ( // Expected radix. (digits == 113) ? 3.141592653589793238462643383279502797479068098137295573004504331874296718662975536062731407582759857177734375L // value for 113 bits. : (digits == 64) ? 3.14159265358979323829596852490908531763125210004425048828125L // value for significand 64 bits. : (digits == 53) ? 3.141592653589793115997963468544185161590576171875 // value for significand 53 bits. : (digits == 24) ? 3.141592502593994140625F // value for significand 24 bits. : 3 // Unknown and/or unimplemented floating point significand ) : std::numeric_limits::quiet_NaN(); // unknown and/or unimplemented floating point radix! // BUT disadvantage is that it only shows at run-time, not compile time. return lower; // Is there a problem here with the type letter? // Should have a F only for FPtype == float, // and L only for FPtype == long double. } // template const FPtype lower() template bool check_interval_values(FPtype l, FPtype n, FPtype u) { // Check that 'nearest' FPtype value is either upper or lower interval, or both, // and interval is as small as possible. bool is_OK = true; // == No warnings. bool is_lower = static_cast(l == n); bool is_upper = static_cast(u == n); if (is_lower && is_upper) { // Both. cout << "lower, nearest & upper are same." << endl; } else { // Different, but expect upper or lower to be same as nearest. if (is_lower) { cout << "nearest is lower limit." << endl; } if (is_upper) { cout << "nearest is upper limit." << endl; } if (!(is_lower) && !(is_upper)) { // Neither! std::cerr << "WARNING : nearest is neither lower nor upper limit!" << std::endl; is_OK = false; if (n > u) { std::cerr << "WARNING : nearest > upper limit!" << std::endl; is_OK = false; } if (n < l) { std::cerr << "WARNING : nearest < lower limit!" << std::endl; is_OK = false; } } FPtype lp = l + u; // lower + upper FPtype two = FPtype(2.L); if ((lp < two * u) && (lp > two * l)) { std::cerr << "WARNING : more precise interval possible!" << std::endl; is_OK = false; } } return is_OK; } // Check /* Doesn't work C2864 MSVC 7.0 template <> class pi { static const float lower = 3.1415925025939941406250F; static const float upper = 3.1415927410125732421875F; static const float nearest = 3.14159265358979323846264338327950288419716939937510F; }; template class pi { // falling back on a lower precision available static const T upper = pi::upper; static const T lower = pi::upper; static const T nerest = pi::upper; }; */ /* Sylvain Pion [pion@cs.nyu.edu] The template solution has advantages : it can simply refuse to compile if you try to ask a digits/radix pair of values which you have not planned. With the initial switch I proposed, you can only get a run-time error. The template solution is also more easily extensible as Herve said : you don't need to have only one big statement in only one file, you can spread the various versions over several files. So I guess you could write your constants like : template ::radix, int digits = std::numeric_limits::digits> class pi; template <> class pi { static const float upper = ...; static const float lower = ...; static const float nearest = ...; }; Either you refuse to compile if the value is not planned, or you can have the template falling back on a lower precision available : template class pi { static const T upper = pi::upper; ... }; gmelquio@ens-lyon.fr It is stated in the documentation on the base type requirements: "If these values are not provided by the user, the default values will be used: they are integer values (so pi is bounded by 3 and 4)." The user has to provide its own constants if it doesn't use an interval based on 'float' or 'double'. are required for accurate input, the lower value is the number guaranteed correct on output. Floating point Type(common C++ type) Commonly used for C/C++ type Totalbits Significandbits guaranteed digits10decimaldigits significant decimaldigits IEEE single float 32 23 + 1 = 24 6 9 VAX F float 32 23 + 1 = 24 6 9 IBM G/390 short float 32 24 6 9 IEEE single extended ? >=43 >=32 7 11 IEEE double double 64 52 + 1 = 53 15 17 VAX G double 64 52 + 1 = 53 15 17 IBM G/390 long double 64 56 15 17 VAX D long double 64 56 15 17 IEEE double extended long double 80 >=64 19 21 NTL quad ‘quad’ 128 106 31 33 IBM G/390 extended long double 128 112 33 35 VAX H long double 128 112 + 1 = 113 34 36 IEEE quadruple long double 128 112 + 1 = 113 34 36 signed fractional ? 127 128 38 40 unsigned fractional ? 128 128 38 40 */