cvRound() 中的 x64 舍入不一致 (_mm_cvtsd_si32)
x64 rounding inconsistency in cvRound() (_mm_cvtsd_si32)
在使用 MSVC2013 的 x64 Windows 上,我正在使用 OpenCV 的 cvRound
函数,目的是从 x.5
值向上舍入。我遇到了一个不一致的地方 cvRound(17.5f)
returns 18
(很好!),但是 cvRound(20.5f)
returns 20
而不是 21
符合预期
cvRound 就是这样实现的,所以它似乎是微软在 _mm_cvtsd_si32()
中的不一致。
int cvRound( double value )
{
__m128d t = _mm_set_sd( value );
return _mm_cvtsd_si32(t);
}
任何人都可以建议 how/why 这可能是什么?
FWIW, cvRound(20.5f + 1e-3f)
returns 21
.
四舍五入的工作原理与此代码打印出值相等的原因相同(使用 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
小的半整数可以用二进制浮点数精确表示 -- 0.5 是 2 的幂。
真正发生的是"rounding half to even."这是一种消除偏差的方法,这种偏差在半整数总是四舍五入时发生。
SSE 指令的舍入行为可通过浮点环境(特别是 MXCSR 寄存器)进行配置。有several IEEE rounding modes。默认的舍入模式是舍入到最接近的值,并排到偶数,因此如果该值恰好位于两个可表示值的中间,则结果将舍入到最接近的偶数。
考虑以下演示不同舍入模式的测试程序:
#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
在使用 MSVC2013 的 x64 Windows 上,我正在使用 OpenCV 的 cvRound
函数,目的是从 x.5
值向上舍入。我遇到了一个不一致的地方 cvRound(17.5f)
returns 18
(很好!),但是 cvRound(20.5f)
returns 20
而不是 21
符合预期
cvRound 就是这样实现的,所以它似乎是微软在 _mm_cvtsd_si32()
中的不一致。
int cvRound( double value )
{
__m128d t = _mm_set_sd( value );
return _mm_cvtsd_si32(t);
}
任何人都可以建议 how/why 这可能是什么?
FWIW, cvRound(20.5f + 1e-3f)
returns 21
.
四舍五入的工作原理与此代码打印出值相等的原因相同(使用 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
小的半整数可以用二进制浮点数精确表示 -- 0.5 是 2 的幂。
真正发生的是"rounding half to even."这是一种消除偏差的方法,这种偏差在半整数总是四舍五入时发生。
SSE 指令的舍入行为可通过浮点环境(特别是 MXCSR 寄存器)进行配置。有several IEEE rounding modes。默认的舍入模式是舍入到最接近的值,并排到偶数,因此如果该值恰好位于两个可表示值的中间,则结果将舍入到最接近的偶数。
考虑以下演示不同舍入模式的测试程序:
#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