msvc_rounding_control.hpp 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /* Boost interval/detail/msvc_rounding_control.hpp file
  2. *
  3. * Copyright 2000 Maarten Keijzer
  4. * Copyright 2002 Hervé Brönnimann, Guillaume Melquiond, Sylvain Pion
  5. *
  6. * Distributed under the Boost Software License, Version 1.0.
  7. * (See accompanying file LICENSE_1_0.txt or
  8. * copy at http://www.boost.org/LICENSE_1_0.txt)
  9. */
  10. #ifndef BOOST_NUMERIC_INTERVAL_DETAIL_MSVC_ROUNDING_CONTROL_HPP
  11. #define BOOST_NUMERIC_INTERVAL_DETAIL_MSVC_ROUNDING_CONTROL_HPP
  12. #ifndef _MSC_VER
  13. # error This header is only intended for MSVC, but might work for Borland as well
  14. #endif
  15. #include <float.h> // MSVC rounding control
  16. // Although the function is called _control87, it seems to work for
  17. // other FPUs too, so it does not have to be changed to _controlfp.
  18. namespace boost {
  19. namespace numeric {
  20. namespace interval_lib {
  21. namespace detail {
  22. #if BOOST_MSVC < 1400 || defined(_WIN64)
  23. extern "C" { double rint(double); }
  24. #else
  25. inline double rint(double x)
  26. {
  27. _asm FLD [x] ;
  28. _asm FRNDINT ;
  29. //_asm RET ;
  30. }
  31. #endif
  32. struct x86_rounding
  33. {
  34. static unsigned int hard2msvc(unsigned short m) {
  35. unsigned int n = 0;
  36. if (m & 0x01) n |= _EM_INVALID;
  37. if (m & 0x02) n |= _EM_DENORMAL;
  38. if (m & 0x04) n |= _EM_ZERODIVIDE;
  39. if (m & 0x08) n |= _EM_OVERFLOW;
  40. if (m & 0x10) n |= _EM_UNDERFLOW;
  41. if (m & 0x20) n |= _EM_INEXACT;
  42. switch (m & 0x300) {
  43. case 0x000: n |= _PC_24; break;
  44. case 0x200: n |= _PC_53; break;
  45. case 0x300: n |= _PC_64; break;
  46. }
  47. switch (m & 0xC00) {
  48. case 0x000: n |= _RC_NEAR; break;
  49. case 0x400: n |= _RC_DOWN; break;
  50. case 0x800: n |= _RC_UP; break;
  51. case 0xC00: n |= _RC_CHOP; break;
  52. }
  53. if (m & 0x1000) n |= _IC_AFFINE; // only useful on 287
  54. return n;
  55. }
  56. static unsigned short msvc2hard(unsigned int n) {
  57. unsigned short m = 0;
  58. if (n & _EM_INVALID) m |= 0x01;
  59. if (n & _EM_DENORMAL) m |= 0x02;
  60. if (n & _EM_ZERODIVIDE) m |= 0x04;
  61. if (n & _EM_OVERFLOW) m |= 0x08;
  62. if (n & _EM_UNDERFLOW) m |= 0x10;
  63. if (n & _EM_INEXACT) m |= 0x20;
  64. switch (n & _MCW_RC) {
  65. case _RC_NEAR: m |= 0x000; break;
  66. case _RC_DOWN: m |= 0x400; break;
  67. case _RC_UP: m |= 0x800; break;
  68. case _RC_CHOP: m |= 0xC00; break;
  69. }
  70. switch (n & _MCW_PC) {
  71. case _PC_24: m |= 0x000; break;
  72. case _PC_53: m |= 0x200; break;
  73. case _PC_64: m |= 0x300; break;
  74. }
  75. if ((n & _MCW_IC) == _IC_AFFINE) m |= 0x1000;
  76. return m;
  77. }
  78. typedef unsigned short rounding_mode;
  79. static void get_rounding_mode(rounding_mode& mode)
  80. { mode = msvc2hard(_control87(0, 0)); }
  81. static void set_rounding_mode(const rounding_mode mode)
  82. {
  83. _control87(hard2msvc(mode),
  84. _MCW_EM | _MCW_RC
  85. #if !defined(_M_AMD64) && !defined(_M_ARM) && !defined(_M_ARM64)
  86. // x64 ignores _MCW_PC and _MCW_IC, and the Debug CRT library actually
  87. // asserts when these are passed to _control87.
  88. // MSDN says on '_control87' that changing precision (_MCW_PC) or
  89. // infinity (_MCW_IC) handling is not supported on the ARM and x64
  90. // architectures and that _control87 raises an assertion
  91. // and the invalid parameter handler is invoked.
  92. | _MCW_PC | _MCW_IC
  93. #endif
  94. );
  95. }
  96. static double to_int(const double& x) { return rint(x); }
  97. };
  98. } // namespace detail
  99. } // namespace interval_lib
  100. } // namespace numeric
  101. } // namespace boost
  102. #endif /* BOOST_NUMERIC_INTERVAL_DETAIL_MSVC_ROUNDING_CONTROL_HPP */