Skip to content

Commit 9ff48e0

Browse files
Add numeric_cast template for numeric conversion
This is for now only applied to mp_integer but could be extended to other numerical types.
1 parent 1aefb09 commit 9ff48e0

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

src/util/mp_arith.h

+76
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ Author: Daniel Kroening, [email protected]
1212

1313
#include <string>
1414
#include <iosfwd>
15+
#include <limits>
1516

1617
#include "big-int/bigint.hh"
18+
#include "optional.h"
1719

1820
// NOLINTNEXTLINE(readability/identifiers)
1921
typedef BigInt mp_integer;
@@ -48,9 +50,83 @@ const std::string integer2string(const mp_integer &, unsigned base=10);
4850
const mp_integer string2integer(const std::string &, unsigned base=10);
4951
const std::string integer2binary(const mp_integer &, std::size_t width);
5052
const mp_integer binary2integer(const std::string &, bool is_signed);
53+
54+
/// \deprecated use numeric_cast<unsigned long long> instead
5155
mp_integer::ullong_t integer2ulong(const mp_integer &);
56+
57+
/// \deprecated use numeric_cast<std::size_t> instead
5258
std::size_t integer2size_t(const mp_integer &);
59+
60+
/// \deprecated use numeric_cast<unsigned> instead
5361
unsigned integer2unsigned(const mp_integer &);
62+
5463
const mp_integer mp_zero=string2integer("0");
5564

65+
/// Numerical cast provides a unified way of converting from one numerical type
66+
/// to another.
67+
/// Generic case doesn't exist, this has to be specialized for different types.
68+
template <typename T, typename = void>
69+
struct numeric_castt final
70+
{
71+
};
72+
73+
/// Convert mp_integer to any signed type
74+
/// \tparam T: type to convert to
75+
/// \param mpi: mp_integer to convert
76+
/// \return optional integer of type T if conversion is possible,
77+
/// empty optional otherwise.
78+
template <typename T>
79+
struct numeric_castt<T,
80+
typename std::enable_if<std::is_integral<T>::value &&
81+
std::is_signed<T>::value>::type>
82+
{
83+
static optionalt<T> numeric_cast(const mp_integer &mpi)
84+
{
85+
static_assert(
86+
std::numeric_limits<T>::max() <=
87+
std::numeric_limits<decltype(mpi.to_long())>::max() &&
88+
std::numeric_limits<T>::min() >=
89+
std::numeric_limits<decltype(mpi.to_long())>::min(),
90+
"Numeric cast only works for types smaller than long long");
91+
if(
92+
mpi <= std::numeric_limits<T>::max() &&
93+
mpi >= std::numeric_limits<T>::min())
94+
// to_long converts to long long which is the largest signed numeric type
95+
return {static_cast<T>(mpi.to_long())};
96+
else
97+
return {};
98+
}
99+
};
100+
101+
/// Convert mp_integer to any unsigned type
102+
/// \tparam T: type to convert to
103+
/// \param mpi: mp_integer to convert
104+
/// \return optional integer of type T if conversion is possible,
105+
/// empty optional otherwise.
106+
template <typename T>
107+
struct numeric_castt<T,
108+
typename std::enable_if<std::is_integral<T>::value &&
109+
!std::is_signed<T>::value>::type>
110+
{
111+
static optionalt<T> numeric_cast(const mp_integer &mpi)
112+
{
113+
static_assert(
114+
std::numeric_limits<T>::max() <=
115+
std::numeric_limits<decltype(mpi.to_ulong())>::max() &&
116+
std::numeric_limits<T>::min() >=
117+
std::numeric_limits<decltype(mpi.to_ulong())>::min(),
118+
"Numeric cast only works for types smaller than unsigned long long");
119+
if(mpi <= std::numeric_limits<T>::max() && mpi >= 0)
120+
return {static_cast<T>(mpi.to_ulong())};
121+
else
122+
return {};
123+
}
124+
};
125+
126+
template <typename T>
127+
optionalt<T> numeric_cast(const mp_integer &mpi)
128+
{
129+
return numeric_castt<T>::numeric_cast(mpi);
130+
}
131+
56132
#endif // CPROVER_UTIL_MP_ARITH_H

0 commit comments

Comments
 (0)