基于定点算法的编译时元编程.乘法溢出
Compile-time metaprogramming-based fixed-point arithmetic. Multiplication overflow?
我目前正在通过模板元编程实现编译时三维光栅。
在实现代数基础(2d/3d/4d矢量、3x3/4x4矩阵算术、用于剔除目的的aabb2d/3d等)后,我注意到整数算术对于矢量变换来说不够好。所以我开始写一个定点实现:
库有一个基头,其中包含代数类型将实现(以提供统一的接口)。以下是定点实现所使用的一组定义:
template<typename T>
struct zero; //Gets the zero value of a type of data. For example, zero<std::integral_constant<int>> returns std::integral_constant<int,0>
template<typename T>
struct one;
/* Algebraic operations: */
template<typename LHS , typename RHS>
struct add;
template<typename LHS , typename RHS>
struct sub;
template<typename LHS , typename RHS>
struct mul;
我决定使用基于十进制的定点,而不是基于二进制的,因为它可以让我轻松地编写十进制数字接口(下面提供的decimal
别名)。以下是幂元函数和小数位数移位的实现:
template<int base , int exponent>
struct positive_pow : public std::integral_constant<long long int , base * positive_pow<base,exponent-1>::value> {};
template<int base>
struct positive_pow<base,0> : public std::integral_constant<long long int,1> {};
template<int number , int shift>
struct decimal_leftshift : public std::integral_constant<long long int,number * positive_pow<10, shift>::value> {};
template<int number , int shift>
struct decimal_rightshift : public std::integral_constant<long long int,number / positive_pow<10, shift>::value> {};
template<bool CONDITION , int NUMBER , int SHIFT>
struct decimal_shift_chooser
{
using shifter = decimal_leftshift<NUMBER,SHIFT>;
};
template<int NUMBER , int SHIFT>
struct decimal_shift_chooser<false,NUMBER,SHIFT>
{
using shifter = decimal_rightshift<NUMBER,-SHIFT>;
};
//This metafunction shifts to one direction or other depending on the sign of the shift count passed:
template<int number , int shift>
struct decimal_shift
{
using shifter = typename decimal_shift_chooser<( shift >= 0 ) , number , shift>::shifter;
static const long long int value = shifter::value;
};
这里是定点类型的实现。内部实现使用long 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> {};
正如我所指出的,该实现有19个十进制数字。因此,如果我们使用8位小数精度,并将圆周率乘以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;
}
但是这个打印:
圆周率:314159
pi*2:-1e-07
正如你所看到的,结果是一个非常小的负数,所以我认为在计算中的任何地方都存在有符号整数溢出。
问题出在哪里?这是一个整数溢出吗?如果是真的,在哪里以及如何修复
下面是一个完整的运行示例。
您的decimal_rightshift
有一个int
参数,您将BITS1*BIST2
传递给它。用long long int
替换元程序中的每个int
,一切都应该正常。
顺便说一句,您的one
不正确。正确的实现是:
template<fpbits BITS , fbcount PRECISION>
struct one<fixed_point<BITS,PRECISION>> : public fixed_point<decimal_leftshift<1, PRECISION>::value, PRECISION> {};
也就是说,假设one
实际上被假定为值1。
相关文章:
- 有一个打印语句的函数是一种糟糕的编程实践吗
- 'short int'持有的值溢出,但"自动"不会溢出?
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 大于65535的C++数组[size]引发不一致的溢出
- 为什么我在leetcode上收到AddressSanitizer:地址0x602000000058上的堆缓冲区溢出错误
- C++中无符号字符溢出
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 模板元编程:如何将参数包组合成新的参数包
- Qt Q串行端口未编程设备未关闭
- 模板元编程 - 尝试实现维度分析
- 在 leetcode 上提交解决方案时出现堆栈缓冲区溢出错误
- 我是编程新手
- 我的 int main() 中出现堆栈溢出错误
- 整数溢出,最大值为 pow(10,19)
- 获取隐式转换溢出从无符号到已签名的警告
- 使用 strcat 获取缓冲区溢出错误
- LeetCode 1:两和 - 地址清理器:堆缓冲区溢出地址
- 给定一个类型,如何派生一个泛型更广泛的类型(例如,用于溢出安全求和)?
- CUDA 编程未处理的异常和堆栈溢出
- 基于定点算法的编译时元编程.乘法溢出