cvRound() 中的 x64 舍入不一致 (_mm_cvtsd_si32)



在使用MSVC2013的x64 Windows上,我正在使用OpenCV的cvRound函数,目的是从x.5值中舍入。我遇到了不一致的情况,cvRound(17.5f)返回18(好!),但cvRound(20.5f)返回20而不是按预期21

cvRound是这样简单地实现的,所以它似乎是_mm_cvtsd_si32() Microsoft不一致。

int  cvRound( double value )
{
    __m128d t = _mm_set_sd( value );
    return _mm_cvtsd_si32(t);
}

谁能建议如何/为什么会这样?

FWIW,cvRound(20.5f + 1e-3f)返回21 .

小的半整数可以用二进制浮点数精确表示——0.5 是 2 的幂。

真正发生的事情是"四舍五入到偶数"。 这是一种消除半整数总是向上舍入时发生的偏差的方法。

http://en.wikipedia.org/wiki/Rounding#Round_half_to_even

SSE 指令的舍入行为可通过浮点环境(特别是 MXCSR 寄存器)进行配置。 有几种 IEEE 舍入模式。 默认舍入模式是舍入到最近值、并列到偶数,因此,如果该值正好位于两个可表示值的中间,则结果将舍入到最接近的偶数值。

请考虑以下测试程序,该程序演示了不同的舍入模式:

#include <fenv.h>
#include <immintrin.h>
#include <stdio.h>
int main()
{
    printf("Default:        %dn", _mm_cvtsd_si32(_mm_set_sd(20.5)));
    fesetround(FE_DOWNWARD);
    printf("FE_DOWNWARD:    %dn", _mm_cvtsd_si32(_mm_set_sd(20.5)));
    fesetround(FE_UPWARD);
    printf("FE_UPWARD:      %dn", _mm_cvtsd_si32(_mm_set_sd(20.5)));
    fesetround(FE_TONEAREST);
    printf("FE_TONEAREST:   %dn", _mm_cvtsd_si32(_mm_set_sd(20.5)));
    fesetround(FE_TOWARDZERO);
    printf("FE_TOWARDZERO:  %dn", _mm_cvtsd_si32(_mm_set_sd(20.5)));
}

输出:

Default:        20
FE_DOWNWARD:    20
FE_UPWARD:      21
FE_TONEAREST:   20
FE_TOWARDZERO:  20

入的工作方式与此代码打印的值相等的原因相同(使用 MSVC2012 进行测试)

float f1 = 20.4999999f;
float f2 = 20.5f;
if(f1==f2)
    printf("equaln");

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

最新更新