[eigen] Unexpected result type deduction |

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]

*To*: eigen@xxxxxxxxxxxxxxxxxxx*Subject*: [eigen] Unexpected result type deduction*From*: Martin Errenst <imp@xxxxxxxxxxxxxxx>*Date*: Sun, 18 Jun 2017 19:28:02 +0200

Hi, while implementing my own fixed point class, I ran into some problems with Eigen. Not sure whether my code misses to extend Eigen some more to work correctly or if it is a real bug. Here is the output from my minimal example code (see attachment), compiled with revision 10512:c06cfd545b15: ############# g++ -std=c++11 -g -lm -I/home/mj/opt/include/ -I../ test_eigen.cpp -o test_eigen ../test_eigen Input data: 0.09999999962747097 (0xccccccc) Fixed<31> 0.09999999962747097 (0xccccccc) Fixed<31> 0.99989999970421195 (0x7ffcb923) Fixed<31> 0.99989999970421195 (0x7ffcb923) Fixed<31> Lets perform a single addition, this should yield a Fixed<30>: 1.9997999994084239 (0x7ffcb923) Fixed<30> Perform the same operation, but colWise (expecting Fixed<30> as type, like above): going from: 30bit fraction length to: 31bit. This should not be called. going from: 30bit fraction length to: 31bit. This should not be called. 0.19999999925494194 (0x19999998) Fixed<31> -1 (0x80000000) Fixed<31> This is wrong :(. Expected something close to 2 and got -1. Honoring the output format is important. ############# Basically, operator+(Fixed<F> a, Fixed<F>) will return a Fixed<F-1>, this way, no overflows can happen and it only drops the LSB. Somehow, the .col(0)+.col(1) expression, forces the result type to be the same as the parameters type (that is why I cannot put 'explicit' back into the constructor which can convert between different fraction lengths, the whole code will not compile then at all :|). I would expect here, that the result type is actually deducted from the result type of the operator (with given arguments). Also we can tell from the output, that the operator+ was successfully used, but the result got casted to the wrong type. If someone can shed light on this issue regarding if it's my fault due to missing glue code, or a real bug, I would be really grateful :). Thanks, imp

#include <cstddef> #include <iostream> #include <iomanip> #include <sstream> #include <cmath> #include <eigen3/Eigen/Dense> // 32bit fixedpoint class template <size_t F> class Fixed { private: static const int64_t one = (static_cast<int64_t>(1)<<(F)); int32_t value; public: // getter&setter int32_t getRawValue() const { return value; }; void setRawValue(int64_t x) { value = x; }; public: // constructors explicit Fixed() : value(0) { }; explicit Fixed(double x) { value = one * x ; }; template<size_t R> /* explicit */ Fixed(Fixed<R> const &rhs){ // TODO make explicit again. std::cout << "going from: " << R << "bit fraction length to: " << F << "bit. This should not be called." << std::endl; value = (one * static_cast<double>(rhs)); // temp fix }; public: // type conversions explicit operator double() const { return (static_cast<double>(value)) / (static_cast<double>(one)); }; }; // output functions template <size_t F> std::ostream &operator<<(std::ostream &os, const Fixed<F> &f) { os << std::setprecision(17) << static_cast<double>(f) << " (0x" <<std::hex << f.getRawValue() << std::dec << ") Fixed<" << F << ">"; return os; }; // math functions template <size_t F_in_left, size_t F_in_right, size_t F_out = (((F_in_left >= F_in_right)?F_in_right:F_in_left)-1)> Fixed<F_out> operator+(const Fixed<F_in_left> a, const Fixed<F_in_right> b) { // An addition can yield a new MSB, thus we drop the lowest LSB. // This way, it cannot overflow, nor do we need to check for saturation. Fixed<F_out> ret; ret.setRawValue((((static_cast<int64_t>(a.getRawValue())<<((F_in_left >= F_in_right)?0:F_in_right-F_in_left)) + (static_cast<int64_t>(b.getRawValue())<<((F_in_right >= F_in_left)?0:F_in_left-F_in_right))))>>((F_in_left >= F_in_right?F_in_left-F_in_right:F_in_right-F_in_left)+1)); return ret; }; // Following https://eigen.tuxfamily.org/dox/TopicCustomizing_CustomScalar.html . namespace Eigen { template<size_t F> struct NumTraits<Fixed<F>> : GenericNumTraits<Fixed<F>> { typedef Fixed<F> Real; typedef Fixed<F> NonInteger; typedef Fixed<F> Literal; typedef Fixed<F> Nested; static inline Real epsilon() { return Fixed<F>(0); } static inline Real dummy_precision() { return Fixed<F>(0); } static inline int digits10() { return (std::log(2.0)/std::log(10.0)*((32>F)?(32-F-1):0)); } // TODO return value was Real, but the value range doesn't always work, so it's int for now. static inline Real highest() { return Fixed<F>(0xffffffff); } static inline Real lowest() { return Fixed<F>(-0xffffffff); } enum { IsInteger = 0, IsSigned = 1, IsComplex = 0, RequireInitialization = 1, ReadCost = 3, // The costs are guessed for now. AddCost = 20, MulCost = 50 }; }; }; using T = Fixed<31>; using cppfixT_t = Eigen::Array<T, 2, 2>; int main() { cppfixT_t data; data << T(0.1), T(0.1), T(0.9999), T(0.9999); // This row will cause an overflow if we cast the sum later. std::cout << "Input data:" << std::endl << data << std::endl << std::endl; std::cout << "Lets perform a single addition, this should yield a Fixed<30>:" << std::endl; std::cout << data(1, 0)+data(1, 1) << std::endl << std::endl; std::cout << "Perform the same operation, but colWise (expecting Fixed<30> as type, like above):" << std::endl; std::cout << data.col(0)+data.col(1) << std::endl; std::cout << "This is wrong :(. Expected something close to 2 and got -1. Honoring the output format is important." << std::endl; return 0; }

**Messages sorted by:**[ date | thread ]- Prev by Date:
**[eigen] Numerically stable tensor sum in eigen** - Next by Date:
**[eigen] Solving positive semidefinite systems** - Previous by thread:
**[eigen] Numerically stable tensor sum in eigen** - Next by thread:
**[eigen] Solving positive semidefinite systems**

Mail converted by MHonArc 2.6.19+ | http://listengine.tuxfamily.org/ |