На 64-разрядной Windows, использующей MSVC2013, я использую cvRound
функция OpenCV с целью округления от x.5
ценности. Я столкнулся с несоответствием, где cvRound(17.5f)
возвращается 18
(хорошо!), но cvRound(20.5f)
возвращается 20
и не 21
как и ожидалось
cvRound просто реализован таким образом, поэтому кажется, что Microsoft _mm_cvtsd_si32()
,
int cvRound( double value )
{
__m128d t = _mm_set_sd( value );
return _mm_cvtsd_si32(t);
}
Кто-нибудь может подсказать, как / почему это может быть?
FWIW, cvRound(20.5f + 1e-3f)
возвращается 21
,
Поведение округления инструкций SSE настраивается через среду с плавающей запятой (в частности, регистр MXCSR). Есть несколько режимов округления IEEE. Режим округления по умолчанию — округление до ближайшего, привязка к четному, поэтому, если значение находится точно в середине двух представимых значений, результат округляется до ближайшего четного значения.
Рассмотрим следующую тестовую программу, которая демонстрирует различные режимы округления в действии:
#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
Маленькие полуцелые числа могут быть точно представлены двоичной плавающей точкой — 0,5 — степень 2.
Что на самом деле происходит, так это «округление половины до четности». Это способ убрать смещение, которое возникает, когда полуцелые всегда округляются.
Округление работает так же по той же причине, по которой этот код печатает, что значения равны (протестировано с 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