Ardour  9.0-pre0-582-g084a23a80d
integer_division.h
Go to the documentation of this file.
1 /*
2  Copyright (C) 2020 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 
19 #pragma once
20 
21 #include <cstdint>
22 
23 #ifndef COMPILER_INT128_SUPPORT
24 #include <boost/multiprecision/cpp_int.hpp>
25 #include "pbd/error.h"
26 #endif
27 
28 #define PBD_IDIV_ASR(x) ((x) < 0 ? -1 : 0) // Compiles into a (N-1)-bit arithmetic shift right
29 
30 /* The value of PBD_IDIV_ROUNDING will have the same sign as the dividend (x) and half
31  * the magnitude of the divisor (y). Adding ROUNDING to the dividend thus
32  * increases its magnitude before the integer division truncates the resulting
33  * quotient.
34  */
35 
36 #define PBD_IDIV_ROUNDING(x,y) ( (y)/2 - (PBD_IDIV_ASR((x)^(y)) & (y)))
37 
38 template<typename T>
39 T int_div_round (T x, T y)
40 {
41  /* essentially ((x + (y/2)) / y) but handles signed/negative values correcvtly */
42  return (x + PBD_IDIV_ROUNDING(x,y)) / y ;
43 }
44 
45 namespace PBD {
46 
47 /* this computes v * (n/d) where v, n and d are all 64 bit integers, without
48  * overflow, and with appropriate rounding given that this is integer division.
49  */
50 
51 inline
52 int64_t muldiv_round (int64_t v, int64_t n, int64_t d)
53 {
54 #ifndef COMPILER_INT128_SUPPORT
55  boost::multiprecision::int512_t bignum = v;
56 
57  bignum *= n;
58  bignum += PBD_IDIV_ROUNDING (bignum, d);
59  bignum /= d;
60 
61  try {
62 
63  return bignum.convert_to<int64_t> ();
64 
65  } catch (...) {
66  fatal << "arithmetic overflow in timeline math\n" << endmsg;
67  /* NOTREACHED */
68  return 0;
69  }
70 
71 #else
72  __int128 _n (n);
73  __int128 _d (d);
74  __int128 _v (v);
75  __int128 vn (_v * _n);
76 
77  const int64_t hd = PBD_IDIV_ROUNDING (vn, d);
78 
79  /* this could overflow, but will not do so merely because we are
80  * multiplying two int64_t together and storing the result in an
81  * int64_t. Overflow will occur where (v*n)+hd > INT128_MAX (hard
82  * limit) or where v * n / d > INT64_T (i.e. n > d)
83  */
84 
85  return(int64_t) ((vn + hd) / _d);
86 #endif
87 }
88 
89 inline
90 int64_t muldiv_floor (int64_t v, int64_t n, int64_t d)
91 {
92 #ifndef COMPILER_INT128_SUPPORT
93  boost::multiprecision::int512_t bignum = v;
94 
95  bignum *= n;
96  bignum /= d;
97 
98  try {
99 
100  return bignum.convert_to<int64_t> ();
101 
102  } catch (...) {
103  fatal << "arithmetic overflow in timeline math\n" << endmsg;
104  /* NOTREACHED */
105  return 0;
106  }
107 
108 #else
109  __int128 _n (n);
110  __int128 _d (d);
111  __int128 _v (v);
112 
113  /* this could overflow, but will not do so merely because we are
114  * multiplying two int64_t together and storing the result in an
115  * int64_t. Overflow will occur where (v*n)+hd > INT128_MAX (hard
116  * limit) or where v * n / d > INT64_T (i.e. n > d)
117  */
118 
119  return(int64_t) ((_v * _n) / _d);
120 #endif
121 }
122 } /* namespace */
123 
T int_div_round(T x, T y)
#define PBD_IDIV_ROUNDING(x, y)
Definition: axis_view.h:42
int64_t muldiv_floor(int64_t v, int64_t n, int64_t d)
int64_t muldiv_round(int64_t v, int64_t n, int64_t d)
Transmitter fatal
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71