несоответствие округления x64 в cvRound () (_mm_cvtsd_si32)

На 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,

3

Решение

Поведение округления инструкций 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
5

Другие решения

Маленькие полуцелые числа могут быть точно представлены двоичной плавающей точкой — 0,5 — степень 2.

Что на самом деле происходит, так это «округление половины до четности». Это способ убрать смещение, которое возникает, когда полуцелые всегда округляются.

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

7

Округление работает так же по той же причине, по которой этот код печатает, что значения равны (протестировано с 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

1
По вопросам рекламы [email protected]