round.h 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. #pragma once
  2. #include <util/system/types.h>
  3. #include <cmath>
  4. #include <optional>
  5. #include <fenv.h>
  6. namespace NMathUdf {
  7. template <class T>
  8. inline T RoundToDecimal(T v, int decShift) {
  9. T div = std::pow(T(10), decShift);
  10. return std::floor(v / div + T(0.5)) * div;
  11. }
  12. inline std::optional<i64> Mod(i64 value, i64 m) {
  13. if (!m) {
  14. return {};
  15. }
  16. const i64 result = value % m;
  17. if ((result < 0 && m > 0) || (result > 0 && m < 0)) {
  18. return result + m;
  19. }
  20. return result;
  21. }
  22. inline std::optional<i64> Rem(i64 value, i64 m) {
  23. if (!m) {
  24. return {};
  25. }
  26. const i64 result = value % m;
  27. if (result < 0 && value > 0) {
  28. return result + m;
  29. }
  30. if (result > 0 && value < 0) {
  31. return result - m;
  32. }
  33. return result;
  34. }
  35. inline std::optional<i64> NearbyIntImpl(double value, decltype(FE_DOWNWARD) mode) {
  36. if (!::isfinite(value)) {
  37. return {};
  38. }
  39. auto prevMode = ::fegetround();
  40. ::fesetround(mode);
  41. auto res = ::nearbyint(value);
  42. ::fesetround(prevMode);
  43. // cast to i64 gives wrong sign above 9223372036854774784
  44. // lower bound is adjusted to -9223372036854774784 as well
  45. if (res < double(std::numeric_limits<i64>::min() + 513) || res > double(std::numeric_limits<i64>::max() - 512)) {
  46. return {};
  47. }
  48. return static_cast<i64>(res);
  49. }
  50. inline std::optional<i64> NearbyInt(double value, ui32 mode) {
  51. switch (mode) {
  52. case 0:
  53. return NearbyIntImpl(value, FE_DOWNWARD);
  54. case 1:
  55. return NearbyIntImpl(value, FE_TONEAREST);
  56. case 2:
  57. return NearbyIntImpl(value, FE_TOWARDZERO);
  58. case 3:
  59. return NearbyIntImpl(value, FE_UPWARD);
  60. default:
  61. return {};
  62. }
  63. }
  64. }