gpt4 book ai didi

c++ - 高效的 float 到 int 而不会溢出

转载 作者:行者123 更新时间:2023-11-28 02:09:01 27 4
gpt4 key购买 nike

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,同时将其限制为整数类型的最小值和最大值?例如,不将 minmax 转换为 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

How about clamping using fmin(), fmax()... [thanks to njuffa for the question]

代码确实变得更加高效,因为条件跳转被删除了。但是,它开始在钳位限制下表现不正确。

考虑:

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 的限制,因为 float 无法准确表示 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);
}

关于c++ - 高效的 float 到 int 而不会溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36432173/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com