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

cdm*_*dmh 3 c++ opencv rounding-error visual-c++

在使用MSVC2013的x64 Windows上,我使用的cvRound是OpenCV 的功能,目的是从x.5值向上舍入.我遇到了一个不一致的cvRound(17.5f)回报18(好!),但cvRound(20.5f)回报20并没有21像预期的那样

因此简单地实现了cvRound,所以它似乎是微软的不一致_mm_cvtsd_si32().

int  cvRound( double value )
{
    __m128d t = _mm_set_sd( value );
    return _mm_cvtsd_si32(t);
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以建议如何/为什么这样做?

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

Chr*_*les 7

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

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

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


Jam*_*lis 5

SSE指令的舍入行为可通过浮点环境(特别是MXCSR寄存器)进行配置.有几种IEEE舍入模式.默认的舍入模式是round-to-nearest,tie-to-even,因此如果该值恰好位于两个可表示值的中间,则结果将四舍五入为最接近的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)));
}
Run Code Online (Sandbox Code Playgroud)

输出:

Default:        20
FE_DOWNWARD:    20
FE_UPWARD:      21
FE_TONEAREST:   20
FE_TOWARDZERO:  20
Run Code Online (Sandbox Code Playgroud)