C++ 基于编译时元编程的定点算法。乘法溢出?

C++ 基于编译时元编程的定点算法。乘法溢出?,c++,c++11,template-meta-programming,fixed-point,C++,C++11,Template Meta Programming,Fixed Point,我目前正在通过模板元编程实现一个编译时3d光栅 在实现了代数基础(2d/3d/4d向量、3x3/4x4矩阵运算、用于剔除的aabb2d/3d等)之后,我注意到整数运算对于向量变换来说不够好。因此,我开始编写一个定点实现: 该库有一个基本标头,其中包含代数 类型将实现(以提供统一的接口)。以下是定点实现使用的一组定义: 下面是定点类型的实现。内部实现使用long来存储数字。所以我有64位,也就是大约19位小数: using fpbits = long long int; using fdcount

我目前正在通过模板元编程实现一个编译时3d光栅

在实现了代数基础(2d/3d/4d向量、3x3/4x4矩阵运算、用于剔除的aabb2d/3d等)之后,我注意到整数运算对于向量变换来说不够好。因此,我开始编写一个定点实现:

该库有一个基本标头,其中包含代数 类型将实现(以提供统一的接口)。以下是定点实现使用的一组定义:

下面是定点类型的实现。内部实现使用
long
来存储数字。所以我有64位,也就是大约19位小数:

using fpbits = long long int;
using fdcount = unsigned int; //Fractional decimal digits count (Precision)

const fdcount DEFAULT_FRACTIONAL_PRECISION = 8; 

template<fpbits BITS , fdcount PRECISION = DEFAULT_FRACTIONAL_PRECISION>
struct fixed_point
{
    operator float()
    {
        return (float)BITS * std::pow(10.0f,-(float)PRECISION);
    };
};

//An alias to define decimal numbers with default precision:
template<int mantissa , int exponent = 0> // MANTISSA x 10^EXPONENT
using decimal = fixed_point<decimal_shift<mantissa , DEFAULT_FRACTIONAL_PRECISION + exponent>::value>; 

/* Previously defined common metafunctions implementation */

template<fpbits BITS , fdcount PRECISION>
struct zero<fixed_point<BITS,PRECISION>> : public fixed_point<0,PRECISION> {};

template<fpbits BITS , fdcount PRECISION>
struct one<fixed_point<BITS,PRECISION>> : public fixed_point<decimal_leftshift<1,PRECISION>::value,PRECISION> {};


template<fpbits BITS1 , fdbits BITS2 , fbcount PRECISION>
struct add<fixed_point<BITS1,PRECISION> , fixed_point<BITS2,PRECISION>> : public fixed_point<BITS1+BITS2 , PRECISION> {};

template<fpbits BITS1 , fdbits BITS2 , fbcount PRECISION>
struct sub<fixed_point<BITS1,PRECISION> , fixed_point<BITS2,PRECISION>> : public fixed_point<BITS1-BITS2 , PRECISION> {};

template<fpbits BITS1 , fdbits BITS2 , fbcount PRECISION>
struct mul<fixed_point<BITS1,PRECISION> , fixed_point<BITS2,PRECISION>> : public fixed_point<decimal_rightshift<BITS1*BITS2,PRECISION>::value , PRECISION> {};
使用fpbits=long-long-int;
使用fdcount=无符号整数//小数位数计数(精度)
常量fdcount默认分数精度=8;
模板
结构不动点
{
运算符float()
{
返回(浮点)位*std::pow(10.0f,-(浮点)精度);
};
};
//定义具有默认精度的十进制数的别名:
模板//尾数x 10^指数
使用小数=定点;
/*以前定义的通用元函数实现*/
模板
结构零:公共不动点{};
模板
结构一:公共不动点{};
模板
结构添加:公共不动点{};
模板
结构子:公共不动点{};
模板
结构mul:公共不动点{};
正如我指出的,这个实现有19个十进制数字。因此,如果我们使用8位小数的精度,将pi乘以2,结果就可以表示出来,对吗? 如本例所示:

using pi = decimal<3141592 , -6>; //3141592 x 10^-6 (3,141592) This fits in our 8 precision implementation.
using pi_2 = mul<pi,decimal<2>>; //pi*2 is 314159200 * 200000000 = 62831840000000000 >> 8. 
                                 //The inmediate result of the product fits in a 
                                 //long long (Has 17 decimal digits), so no problem?

int main()
{
    std::cout << "pi: " << pi() << std::endl;
    std::cout << "2*pi: " << pi_2() << std::endl;
}
使用pi=decimal//3141592 x 10^-6(3141592)这适合我们的8精度实现。
使用pi_2=mul//pi*2是314159200*200000000=62831840000000000>>8。
//该产品的中间结果符合
//long long(有17位小数),所以没问题吧?
int main()
{

std::cout您的
decimal\u rightshift
有一个
int
参数,您将
BITS1*BIST2
传递给它。将元程序中的每个
int
替换为
long-long-int
,所有操作都应该正常。

顺便说一句,您的
one
是不正确的。正确的实现是:

template<fpbits BITS , fbcount PRECISION>
struct one<fixed_point<BITS,PRECISION>> : public fixed_point<decimal_leftshift<1, PRECISION>::value, PRECISION> {};
模板
结构一:公共不动点{};

也就是说,假设
one
实际上应该是值1。

谢谢,修正了。我最初将
decimal\u shift
实现为一个用户级函数,而不是限制定点。为了避免移位时用户溢出,我使用32位输入和64位输出。我在实现定点算术时忘记了这一点还有一个问题:其他时候,当我执行一个产生整数溢出的操作时,编译器会在模板实例化期间生成一个错误:常量表达式中的溢出。为什么这个示例不生成此错误?所有操作都是在编译时计算的。@Manu343726:问题是,常量表达式中没有溢出n在问题中;常量表达式是使用
long int
计算的,并且您有一个到
int
的有损转换。我猜您使用的编译器忘记了检查有损转换:x@MatthieuM.哦,谢谢,现在我明白了。我在C++11模式下使用ideone。我在帖子后修复了它。对不起。谢谢你的提醒。我修改了把这个扩展到真正的浮点运算,以防由于不必要的右移而丢失精度。@ AMBROZBIJAJAK起初我想实现IEEE74,但是我没有找到好的论文,定点算法比固定点要简单得多。
using pi = decimal<3141592 , -6>; //3141592 x 10^-6 (3,141592) This fits in our 8 precision implementation.
using pi_2 = mul<pi,decimal<2>>; //pi*2 is 314159200 * 200000000 = 62831840000000000 >> 8. 
                                 //The inmediate result of the product fits in a 
                                 //long long (Has 17 decimal digits), so no problem?

int main()
{
    std::cout << "pi: " << pi() << std::endl;
    std::cout << "2*pi: " << pi_2() << std::endl;
}
template<fpbits BITS , fbcount PRECISION>
struct one<fixed_point<BITS,PRECISION>> : public fixed_point<decimal_leftshift<1, PRECISION>::value, PRECISION> {};