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

时间:2015-01-16 17:59:55

标签: c++ opencv visual-c++ rounding-error

在使用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

3 个答案:

答案 0 :(得分:7)

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

真正发生的是"将一半舍入到均匀。"这是一种消除半整数总是向上舍入时出现偏差的方法。

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

答案 1 :(得分:5)

SSE指令的舍入行为可通过浮点环境(特别是MXCSR寄存器)进行配置。有several IEEE rounding modes。默认的舍入模式是round-to-nearest,tie-to-even,因此如果该值恰好位于两个可表示值的中间,则结果将舍入到最接近的偶数值。

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

#include <fenv.h>
#include <immintrin.h>
#include <stdio.h>

int main()
{
    printf("Default:        %d\n", _mm_cvtsd_si32(_mm_set_sd(20.5)));
    fesetround(FE_DOWNWARD);
    printf("FE_DOWNWARD:    %d\n", _mm_cvtsd_si32(_mm_set_sd(20.5)));
    fesetround(FE_UPWARD);
    printf("FE_UPWARD:      %d\n", _mm_cvtsd_si32(_mm_set_sd(20.5)));
    fesetround(FE_TONEAREST);
    printf("FE_TONEAREST:   %d\n", _mm_cvtsd_si32(_mm_set_sd(20.5)));
    fesetround(FE_TOWARDZERO);
    printf("FE_TOWARDZERO:  %d\n", _mm_cvtsd_si32(_mm_set_sd(20.5)));
}

输出:

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

答案 2 :(得分:1)

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

float f1 = 20.4999999f;
float f2 = 20.5f;
if(f1==f2)
    printf("equal\n");

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