8#include <boost/fusion/algorithm.hpp>
9#include <boost/fusion/container.hpp>
10#include <boost/fusion/sequence.hpp>
11#include <boost/numeric/conversion/cast.hpp>
30 template<
typename RawType>
47 std::string variableName,
unsigned int nBits = 32,
int fractionalBits = 0,
bool isSignedFlag =
true);
56 template<
typename UserType>
57 RawType
toRaw(UserType cookedValue)
const;
63 template<
typename UserType,
typename RAW_ITERATOR,
typename COOKED_ITERATOR>
65 const RAW_ITERATOR& raw_begin,
const RAW_ITERATOR& raw_end,
const COOKED_ITERATOR& cooked_begin)
const {
66 static_assert(std::is_same<typename std::iterator_traits<RAW_ITERATOR>::iterator_category,
67 std::random_access_iterator_tag>::value,
68 "RAW_ITERATOR template argument must be a random access iterator.");
69 static_assert(std::is_same<typename std::iterator_traits<COOKED_ITERATOR>::iterator_category,
70 std::random_access_iterator_tag>::value,
71 "COOKED_ITERATOR template argument must be a random access iterator.");
72 static_assert(std::is_same<typename std::iterator_traits<RAW_ITERATOR>::value_type, int8_t>::value ||
73 std::is_same<typename std::iterator_traits<RAW_ITERATOR>::value_type, int16_t>::value ||
74 std::is_same<typename std::iterator_traits<RAW_ITERATOR>::value_type, int32_t>::value ||
75 std::is_same<typename std::iterator_traits<RAW_ITERATOR>::value_type, int64_t>::value,
76 "RAW_ITERATOR template argument must be an iterator with value type equal to int8_t, int16_t, int32_t or "
78 static_assert(std::is_same<typename std::iterator_traits<COOKED_ITERATOR>::value_type, UserType>::value,
79 "COOKED_ITERATOR template argument must be an iterator with value type equal to the UserType template "
83 template<
typename UserType,
typename RAW_ITERATOR,
typename COOKED_ITERATOR>
86 COOKED_ITERATOR cooked_begin);
90 template<
typename UserType>
93 vectorToCooked<UserType>(&
raw, (&
raw) + 1, &cooked);
98 [[nodiscard]]
unsigned int getNBits()
const {
return _nBits; }
104 [[nodiscard]]
bool isSigned()
const {
return _isSigned; }
109 return _nBits == other._nBits && _fractionalBits == other._fractionalBits && _isSigned == other._isSigned;
114 std::string _variableName;
121 double _fractionalBitsCoefficient;
126 double _inverseFractionalBitsCoefficient;
128 RawType _signBitMask{};
129 RawType _usedBitsMask{};
130 RawType _unusedBitsMask{};
132 RawType _maxRawValue{};
133 RawType _minRawValue{};
143 FixedUserTypeMap<int> conversionBranch_toCooked;
147 const static int zero;
150 class initCoefficients {
152 explicit initCoefficients(FixedPointConverter* fpc) : _fpc(fpc) {}
154 template<
typename Pair>
155 void operator()(Pair)
const {
157 using UserType =
typename Pair::first_type;
160 if(_fpc->_nBits == 16 && _fpc->_fractionalBits == 0 && !_fpc->_isSigned) {
161 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 9;
163 else if(_fpc->_nBits == 16 && _fpc->_fractionalBits == 0 && _fpc->_isSigned) {
164 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 10;
166 else if(std::numeric_limits<UserType>::is_integer && _fpc->_fractionalBits == 0 && !_fpc->_isSigned) {
167 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 1;
169 else if(std::numeric_limits<UserType>::is_integer && _fpc->_fractionalBits == 0 && _fpc->_isSigned) {
170 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 2;
172 else if(_fpc->_nBits == 16 && _fpc->_fractionalBits < 0 && _fpc->_fractionalBits > -16 && !_fpc->_isSigned) {
173 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 7;
175 else if(_fpc->_nBits == 16 && _fpc->_fractionalBits < 0 && _fpc->_fractionalBits > -16 && _fpc->_isSigned) {
176 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 8;
178 else if(_fpc->_nBits == 16 && !_fpc->_isSigned) {
179 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 5;
181 else if(_fpc->_nBits == 16 && _fpc->_isSigned) {
182 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 6;
184 else if(!_fpc->_isSigned) {
185 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 3;
187 else if(_fpc->_isSigned) {
188 boost::fusion::at_key<UserType>(_fpc->conversionBranch_toCooked.table) = 4;
193 boost::fusion::at_key<UserType>(_fpc->_minCookedValues) = _fpc->scalarToCooked<UserType>(_fpc->_minRawValue);
195 catch(boost::numeric::negative_overflow& e) {
196 boost::fusion::at_key<UserType>(_fpc->_minCookedValues) = std::numeric_limits<UserType>::min();
199 boost::fusion::at_key<UserType>(_fpc->_maxCookedValues) = _fpc->scalarToCooked<UserType>(_fpc->_maxRawValue);
201 catch(boost::numeric::positive_overflow& e) {
202 boost::fusion::at_key<UserType>(_fpc->_maxCookedValues) = std::numeric_limits<UserType>::max();
207 FixedPointConverter* _fpc;
213 static S nearbyint(S s) {
return round(s); }
215 using round_style = boost::mpl::integral_c<std::float_round_style, std::round_to_nearest>;
220 template<
typename UserType,
typename std::enable_if<std::is_
signed<UserType>{},
int>::type = 0>
221 bool isNegativeUserType(UserType value)
const;
222 template<
typename UserType,
typename std::enable_if<!std::is_
signed<UserType>{},
int>::type = 0>
223 bool isNegativeUserType(UserType value)
const;
229 void padUnusedBits(RawType& rawValue)
const {
230 if(!(rawValue & _signBitMask)) {
231 rawValue &= _usedBitsMask;
234 rawValue |= _unusedBitsMask;
242 template<
typename RawType>
243 template<
typename UserType,
typename RAW_ITERATOR,
typename COOKED_ITERATOR>
245 const FixedPointConverter& fpc,
const RAW_ITERATOR& raw_begin,
const RAW_ITERATOR& raw_end,
246 COOKED_ITERATOR cooked_begin) {
248 switch(boost::fusion::at_key<UserType>(fpc.conversionBranch_toCooked.
table)) {
250 std::transform(raw_begin, raw_end, cooked_begin, [&fpc](RawType rawValue) {
251 fpc.padUnusedBits(rawValue);
252 if constexpr(
sizeof(RawType) == 4) {
253 return numericToUserType<UserType>(*(std::bit_cast<uint32_t*>(&rawValue)));
255 else if constexpr(
sizeof(RawType) == 8) {
256 return numericToUserType<UserType>(*(std::bit_cast<uint64_t*>(&rawValue)));
262 std::transform(raw_begin, raw_end, cooked_begin, [&fpc](RawType rawValue) {
263 fpc.padUnusedBits(rawValue);
264 return numericToUserType<UserType>(rawValue);
269 std::transform(raw_begin, raw_end, cooked_begin, [](
const auto& rawValue) {
270 return numericToUserType<UserType>(*(std::bit_cast<const uint16_t*>(&rawValue)));
275 std::transform(raw_begin, raw_end, cooked_begin, [](
const auto& rawValue) {
276 return numericToUserType<UserType>(*(std::bit_cast<const int16_t*>(&rawValue)));
281 const auto f =
static_cast<uint32_t
>(fpc._fractionalBitsCoefficient);
282 std::transform(raw_begin, raw_end, cooked_begin, [f](
const auto& rawValue) {
283 return numericToUserType<UserType>(f * *(std::bit_cast<const uint16_t*>(&rawValue)));
288 const auto f =
static_cast<int32_t
>(fpc._fractionalBitsCoefficient);
289 std::transform(raw_begin, raw_end, cooked_begin, [f](
const auto& rawValue) {
290 return numericToUserType<UserType>(f * *(std::bit_cast<const int16_t*>(&rawValue)));
295 const auto f = fpc._fractionalBitsCoefficient;
296 std::transform(raw_begin, raw_end, cooked_begin, [f](
const auto& rawValue) {
297 return numericToUserType<UserType>(f * *(std::bit_cast<const uint16_t*>(&rawValue)));
302 const auto f = fpc._fractionalBitsCoefficient;
303 std::transform(raw_begin, raw_end, cooked_begin, [f](
const auto& rawValue) {
304 return numericToUserType<UserType>(f * *(std::bit_cast<const int16_t*>(&rawValue)));
309 const auto f = fpc._fractionalBitsCoefficient;
310 std::transform(raw_begin, raw_end, cooked_begin, [&fpc, f](RawType rawValue) {
311 fpc.padUnusedBits(rawValue);
312 if constexpr(
sizeof(RawType) == 4) {
313 return numericToUserType<UserType>(f * *(std::bit_cast<uint32_t*>(&rawValue)));
315 else if constexpr(
sizeof(RawType) == 8) {
316 return numericToUserType<UserType>(f * *(std::bit_cast<uint64_t*>(&rawValue)));
322 const auto f = fpc._fractionalBitsCoefficient;
323 std::transform(raw_begin, raw_end, cooked_begin, [&fpc, f](RawType rawValue) {
324 fpc.padUnusedBits(rawValue);
325 auto ttt = numericToUserType<UserType>(f * rawValue);
331 std::cerr <<
"Fixed point converter configuration is corrupt." << std::endl;
339 template<
typename RawType>
340 template<
typename UserType>
342 if constexpr(std::is_same<UserType, std::string>::value) {
343 if(_fractionalBits == 0) {
345 return toRaw(std::stoi(cookedValue));
347 return toRaw(std::stoul(cookedValue));
350 return toRaw(std::stod(cookedValue));
352 else if constexpr(std::is_same<UserType, Boolean>::value) {
353 if((
bool)cookedValue) {
358 else if constexpr(std::is_same<UserType, Void>::value) {
364 if(cookedValue < boost::fusion::at_key<UserType>(_minCookedValues)) {
367 if(cookedValue > boost::fusion::at_key<UserType>(_maxCookedValues)) {
372 if constexpr(std::numeric_limits<UserType>::is_integer) {
373 if(_fractionalBits == 0) {
375 bool isNegative = isNegativeUserType(cookedValue);
376 if(isNegative && !_isSigned) {
380 cookedValue = -(cookedValue + 1);
383 auto rawValue =
static_cast<RawType
>(cookedValue);
386 if(_isSigned && isNegative) {
387 rawValue = ~rawValue;
391 return rawValue & _usedBitsMask;
395 double d_cooked = _inverseFractionalBitsCoefficient *
static_cast<double>(cookedValue);
405 if constexpr(
sizeof(RawType) == 4) {
406 using converter_signed =
407 boost::numeric::converter<int32_t, double, boost::numeric::conversion_traits<int32_t, double>,
408 boost::numeric::def_overflow_handler, Round<double>>;
409 raw =
static_cast<RawType
>(converter_signed::convert(d_cooked));
411 else if constexpr(
sizeof(RawType) == 8) {
412 using converter_signed =
413 boost::numeric::converter<int64_t, double, boost::numeric::conversion_traits<int64_t, double>,
414 boost::numeric::def_overflow_handler, Round<double>>;
415 raw =
static_cast<RawType
>(converter_signed::convert(d_cooked));
419 if constexpr(
sizeof(RawType) == 4) {
420 using converter_unsigned =
421 boost::numeric::converter<uint32_t, double, boost::numeric::conversion_traits<uint32_t, double>,
422 boost::numeric::def_overflow_handler, Round<double>>;
423 raw =
static_cast<RawType
>(converter_unsigned::convert(d_cooked));
425 else if constexpr(
sizeof(RawType) == 8) {
426 using converter_unsigned =
427 boost::numeric::converter<uint64_t, double, boost::numeric::conversion_traits<uint64_t, double>,
428 boost::numeric::def_overflow_handler, Round<double>>;
429 raw =
static_cast<RawType
>(converter_unsigned::convert(d_cooked));
433 catch(boost::numeric::negative_overflow& e) {
436 catch(boost::numeric::positive_overflow& e) {
442 return raw & _usedBitsMask;
447 template<
typename RawType>
448 template<
typename UserType,
typename std::enable_if<std::is_
signed<UserType>{},
int>::type>
450 return static_cast<bool>(value < 0);
453 template<
typename RawType>
454 template<
typename UserType,
typename std::enable_if<!std::is_
signed<UserType>{},
int>::type>
455 bool FixedPointConverter<RawType>::isNegativeUserType(UserType )
const {
461 template<
typename RawType>
462 template<
typename RAW_ITERATOR,
typename COOKED_ITERATOR>
465 COOKED_ITERATOR cooked_begin) {
466 if(fpc._fractionalBits == 0) {
468 std::vector<int32_t> intValues(raw_end - raw_begin);
469 fpc.
vectorToCooked<int32_t>(raw_begin, raw_end, intValues.begin());
470 for(
auto it : intValues) {
476 std::vector<uint32_t> uintValues(raw_end - raw_begin);
477 fpc.
vectorToCooked<uint32_t>(raw_begin, raw_end, uintValues.begin());
478 for(
auto it : uintValues) {
485 std::vector<double> doubleValues(raw_end - raw_begin);
486 fpc.
vectorToCooked<
double>(raw_begin, raw_end, doubleValues.begin());
487 for(
auto it : doubleValues) {
496 template<
typename RawType>
498 std::string variableName,
unsigned int nBits,
int fractionalBits,
bool isSignedFlag)
499 : _variableName(
std::move(variableName)), _nBits(nBits), _fractionalBits(fractionalBits), _isSigned(isSignedFlag),
500 _fractionalBitsCoefficient(pow(2., -fractionalBits)), _inverseFractionalBitsCoefficient(pow(2., fractionalBits)) {
501 _fractionalBitsCoefficient = pow(2., -_fractionalBits);
502 _inverseFractionalBitsCoefficient = pow(2., _fractionalBits);
504 const auto maxBits = (
sizeof(RawType) * 8);
505 if(nBits > maxBits) {
506 std::stringstream errorMessage;
507 errorMessage <<
"The number of bits must be <= " << maxBits <<
", but is " << nBits;
514 if((fractionalBits > -std::numeric_limits<double>::min_exponent -
static_cast<int>(nBits)) ||
515 (fractionalBits < -std::numeric_limits<double>::max_exponent +
static_cast<int>(nBits))) {
516 std::stringstream errorMessage;
517 errorMessage <<
"The number of fractional bits exceeds the dynamic"
518 <<
" range of a double.";
525 if(_isSigned && nBits > 0) {
527 _signBitMask = RawType(1) << (nBits - 1);
532 _usedBitsMask = ~RawType(0);
533 if(nBits <
sizeof(RawType) * 8) {
534 _usedBitsMask =
static_cast<RawType
>((RawType(1) << nBits) - RawType(1));
537 _unusedBitsMask = ~_usedBitsMask;
541 _maxRawValue = _usedBitsMask ^ _signBitMask;
543 _minRawValue = _signBitMask;
550 boost::fusion::for_each(_minCookedValues, initCoefficients(
this));
The fixed point converter provides conversion functions between a user type and up to 32 bit fixed po...
bool operator==(const FixedPointConverter &other) const
Compare two fixed point converters.
FixedPointConverter(std::string variableName, unsigned int nBits=32, int fractionalBits=0, bool isSignedFlag=true)
The constructor defines the conversion factor.
int getFractionalBits() const
Read back the fractional bits the converter is using.
bool operator!=(const FixedPointConverter &other) const
UserType scalarToCooked(RawType const &raw) const
Inefficient convenience function for converting a single value to cooked.
bool isSigned() const
Read back wether the conversion is using signed values.
unsigned int getNBits() const
Read back the number of bits the converter is using.
void vectorToCooked(const RAW_ITERATOR &raw_begin, const RAW_ITERATOR &raw_end, const COOKED_ITERATOR &cooked_begin) const
Conversion function from fixed-point values to type T.
RawType toRaw(UserType cookedValue) const
Conversion function from type T to fixed point.
boost::fusion::map< boost::fusion::pair< int8_t, TargetType >, boost::fusion::pair< uint8_t, TargetType >, boost::fusion::pair< int16_t, TargetType >, boost::fusion::pair< uint16_t, TargetType >, boost::fusion::pair< int32_t, TargetType >, boost::fusion::pair< uint32_t, TargetType >, boost::fusion::pair< int64_t, TargetType >, boost::fusion::pair< uint64_t, TargetType >, boost::fusion::pair< float, TargetType >, boost::fusion::pair< double, TargetType >, boost::fusion::pair< std::string, TargetType >, boost::fusion::pair< Boolean, TargetType >, boost::fusion::pair< Void, TargetType > > table
Exception thrown when a logic error has occured.
boost::fusion::map< boost::fusion::pair< int8_t, int8_t >, boost::fusion::pair< uint8_t, uint8_t >, boost::fusion::pair< int16_t, int16_t >, boost::fusion::pair< uint16_t, uint16_t >, boost::fusion::pair< int32_t, int32_t >, boost::fusion::pair< uint32_t, uint32_t >, boost::fusion::pair< int64_t, int64_t >, boost::fusion::pair< uint64_t, uint64_t >, boost::fusion::pair< float, float >, boost::fusion::pair< double, double >, boost::fusion::pair< std::string, std::string >, boost::fusion::pair< Boolean, Boolean >, boost::fusion::pair< Void, Void > > userTypeMap
Map of UserType to value of the UserType.
int32_t DEPRECATED_FIXEDPOINT_DEFAULT
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
std::string to_string(const std::string &v)
static void impl(const FixedPointConverter &fpc, const RAW_ITERATOR &raw_begin, const RAW_ITERATOR &raw_end, COOKED_ITERATOR cooked_begin)
static void impl(const FixedPointConverter &fpc, const RAW_ITERATOR &raw_begin, const RAW_ITERATOR &raw_end, COOKED_ITERATOR cooked_begin)