高效浮动到整数,无溢出
Efficient float to int without overflow
using int_type = int;
int_type min = std::numeric_limits<Depth>::min();
int_type max = std::numeric_limits<Depth>::max();
int_type convert(float f) {
if(f < static_cast<float>(min)) return min; // overflow
else if(f > static_cast<float>(max)) return max; // overflow
else return static_cast<int_type>(f);
}
有没有更有效的方法将float f
转换为int_type
,同时将其固定为整数类型的最小值和最大值?例如,不强制转换min
和max
以float
进行比较。
有时几乎总是,信任编译器是最好的选择。
此代码:
template<class Integral>
__attribute__((noinline))
int convert(float f)
{
using int_type = Integral;
constexpr int_type min = std::numeric_limits<int_type>::min();
constexpr int_type max = std::numeric_limits<int_type>::max();
constexpr float fmin = static_cast<float>(min);
constexpr float fmax = static_cast<float>(max);
if(f < fmin) return min; // overflow
if(f > fmax) return max; // overflow
return static_cast<int_type>(f);
}
使用 -O2 和 -fomit-frame-pointer 编译,得到:
__Z7convertIiEif: ## @_Z7convertIiEif
.cfi_startproc
movl $-2147483648, %eax ## imm = 0xFFFFFFFF80000000
movss LCPI1_0(%rip), %xmm1 ## xmm1 = mem[0],zero,zero,zero
ucomiss %xmm0, %xmm1
ja LBB1_3
movl $2147483647, %eax ## imm = 0x7FFFFFFF
ucomiss LCPI1_1(%rip), %xmm0
ja LBB1_3
cvttss2si %xmm0, %eax
LBB1_3:
retq
我不确定它会更有效率。
注释LCPI_x定义如下:
.section __TEXT,__literal4,4byte_literals
.align 2
LCPI1_0:
.long 3472883712 ## float -2.14748365E+9
LCPI1_1:
.long 1325400064 ## float 2.14748365E+9
如何使用 fmin((, fmax((...[感谢恩朱法的提问]
代码确实变得更加高效,因为删除了条件跳转。但是,它在夹紧极限处开始表现不正确。
考虑:
template<class Integral>
__attribute__((noinline))
int convert2(float f)
{
using int_type = Integral;
constexpr int_type min = std::numeric_limits<int_type>::min();
constexpr int_type max = std::numeric_limits<int_type>::max();
constexpr float fmin = static_cast<float>(min);
constexpr float fmax = static_cast<float>(max);
f = std::min(f, fmax);
f = std::max(f, fmin);
return static_cast<int_type>(f);
}
通话方式
auto i = convert2<int>(float(std::numeric_limits<int>::max()));
结果在:
-2147483648
显然,我们需要减少 epsilon 的限制,因为浮点数无法准确表示 int 的整个范围,所以......
template<class Integral>
__attribute__((noinline))
int convert2(float f)
{
using int_type = Integral;
constexpr int_type min = std::numeric_limits<int_type>::min();
constexpr int_type max = std::numeric_limits<int_type>::max();
constexpr float fmin = static_cast<float>(min) - (std::numeric_limits<float>::epsilon() * static_cast<float>(min));
constexpr float fmax = static_cast<float>(max) - (std::numeric_limits<float>::epsilon() * static_cast<float>(max));
f = std::min(f, fmax);
f = std::max(f, fmin);
return static_cast<int_type>(f);
}
应该更好...
除了现在相同的函数调用产生:
2147483392
顺便说一下,解决这个问题实际上导致我在原始代码中发现了一个错误。由于舍入误差问题相同,>
和<
运算符需要替换为>=
和<=
。
这样:
template<class Integral>
__attribute__((noinline))
int convert(float f)
{
using int_type = Integral;
constexpr int_type min = std::numeric_limits<int_type>::min();
constexpr int_type max = std::numeric_limits<int_type>::max();
constexpr float fmin = static_cast<float>(min);
constexpr float fmax = static_cast<float>(max);
if(f <= fmin) return min; // overflow
if(f >= fmax) return max; // overflow
return static_cast<int_type>(f);
}
对于 32 位整数,您可以让 CPU 为您完成一些钳位工作。
cvtss2si
指令实际上将在浮点数超出范围的情况下返回0x80000000。这使您可以在大多数情况下消除一个测试:
int convert(float value)
{
int result = _mm_cvtss_si32(_mm_load_ss(&value));
if (result == 0x80000000 && value > 0.0f)
result = 0x7fffffff;
return result;
}
如果你有很多要转换,那么_mm_cvtps_epi32让你一次处理四个(在溢出时具有相同的行为(。这应该比一次处理一个要快得多,但您需要以不同的方式构建代码才能使用它。
如果要
截断,可以利用avx2和avx指令512:
#include <float.h>
int main() {
__m256 a = {5.423423, -4.243423, 423.4234234, FLT_MAX, 79.4234876, 19.7, 8.5454, 7675675.6};
__m256i b = _mm256_cvttps_epi32(a);
void p256_hex_u32(__m256i in) {
alignas(32) uint32_t v[8];
_mm256_store_si256((__m256i*)v, in);
printf("v4_u32: %d %d %d %d %d %d %d %dn", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
}
编译方式:
g++ -std=c++17 -mavx2 a.cpp && ./a.out
对于mavx512(我的CPU不支持,所以我不会提供工作测试,请随意编辑(:
_mm512_maskz_cvtt_roundpd_epi64(k, value, _MM_FROUND_NO_EXC);
相关文章:
- 整数溢出,最大值为 pow(10,19)
- 模函数,避免C++整数溢出
- 优化正在杀死我在 clang 6 中的整数溢出检查
- 从双精度转换为整数的显式类型是否始终检查整数溢出?
- 我在c++中遇到了一个奇怪的错误,其中一个计算2个小整数加法的语句溢出到一个长值中
- 将字符串转换为整数类型T,检查是否存在溢出
- 运行时错误:有符号整数溢出:964632435 * 10 无法在类型 'int' 中表示
- 结构上溢出的整数加法
- 我很难理解这些关于检测整数溢出的评论
- 检测 32 位整数溢出
- 32 位整数缩放,无溢出
- 如何在没有整数溢出的情况下找到n%(k*k)
- C 中俄罗斯农民算法中的整数溢出
- 快速方法可以将整数乘以适当的分数,而无需浮点或溢出
- C 和 C++ 中的整数计算溢出
- 检查Android NDK COD中的整数溢出
- 是否有一些有意义的统计数据来证明保持有符号整数算术溢出未定义是合理的
- 对于特定情况的整数溢出似乎是由整数溢出引起的错误
- 如何修复整数溢出警告
- 我可以在C/ c++中指定是否封顶或溢出整数吗?