Кон­т­роль диа­па­зо­на це­ло­го чис­ла (С++)

Ча­сто в за­да­чах об­ра­бот­ки изоб­ра­же­ний тре­бу­ет­ся опре­де­лить, не вы­хо­дит ли це­лое чис­ло за пре­де­лы не­ко­то­ро­го диа­па­зо­на; при­чём ле­вой гра­ни­цей диа­па­зо­на яв­ля­ет­ся чис­ло 0. На­при­мер, вы хо­ти­те про­ве­рить, по­па­да­ет ли пик­сель с це­ло­чис­лен­ны­ми ко­ор­ди­на­та­ми int x, int y в изоб­ра­же­ние раз­ме­ра­ми sX × sY.

Обыч­но для это­го ис­поль­зу­ет­ся усло­вие ви­да:

if(x>=0 && x<sX && y>=0 && y<sY) какое_либо_действие();

Вез­де да­лее для со­кра­ще­ния за­пи­си бу­дем про­ве­рять лишь од­ну ко­ор­ди­на­ту:

if(x>=0 && x<sX) какое_либо_действие();

В при­ве­дён­ном ко­де для пик­се­лей, по­па­даю­щих в изоб­ра­же­ние (та­ких у вас на­вер­ня­ка бу­дет боль­шин­ство) вы­пол­ня­ют­ся обе про­вер­ки, реа­ли­зуе­мые при по­мо­щи услов­ных пе­ре­хо­дов. Услов­ный пе­ре­ход яв­ля­ет­ся тя­жё­лой опе­ра­ци­ей для со­вре­мен­но­го про­цес­со­ра, так как он (в слу­чае не­удач­но­го пред­ска­за­ния пе­ре­хо­да) мо­жет при­ве­сти к сбро­су кон­вейе­ра, и по­те­ре де­сят­ков так­тов.

Од­на­ко, изу­чая ис­ход­ный код биб­лио­те­ки OpenCV, я об­на­ру­жил бо­лее эф­фек­тив­ное ре­ше­ние дан­ной за­да­чи, ос­но­ван­ное на том, что от­ри­ца­тель­ное чис­ло ти­па int, бу­дучи при­ве­де­но к ти­пу unsigned int, при­об­ре­та­ет зна­че­ние, пре­вы­шаю­щее 2147483647 (это в 32-бит­ных си­сте­мах; в 64-бит­ных ещё боль­ше). По­лу­чен­ное чис­ло боль­ше, чем ши­ри­на или вы­со­та ва­ше­го изоб­ра­же­ния, раз уж вы ад­ре­су­е­те его пик­се­ли ко­ор­ди­на­та­ми ти­па int.

В ито­ге ко­ор­ди­на­ту мож­но про­ве­рить лишь од­ним срав­не­ни­ем:

if( static_cast<unsigned int>(x) < static_cast<unsigned int>(sX) )
    какое_либо_действие();

Для со­мне­ваю­щих­ся: дан­ное при­ве­де­ние ти­па не от­ни­ма­ет ка­ко­го-ли­бо вы­чис­ли­тель­но­го вре­ме­ни у про­цес­со­ра; оно лишь ин­фор­ми­ру­ет ком­пи­ля­тор о том, ка­кие опе­ра­ции срав­не­ния сле­ду­ет ис­поль­зо­вать: зна­ко­вые или без­зна­ко­вые.

Ес­ли вы про­грам­ми­ру­е­те не на C++, а на C, то ваш код бу­дет ко­ро­че:

if( (unsigned)x < (unsigned)sX ) какое_либо_действие();

Дан­ный под­ход мо­жет быть обоб­щён на слу­чай, ко­гда ле­вая гра­ни­ца диа­па­зо­на от­лич­на от ну­ля. Для это­го нуж­но вы­честь ле­вую гра­ни­цу из зна­че­ния пе­ре­мен­ной и из пра­вой гра­ни­цы. Для про­вер­ки x_1\leqslant x<x_2 мож­но ис­поль­зо­вать та­кой код:

if( static_cast<unsigned int>(x-x1) < static_cast<unsigned int>(x2-x1) )
    какое_либо_действие();

Вы мо­же­те воз­му­тить­ся, что вме­сто од­но­го до­пол­ни­тель­но­го срав­не­ния у нас те­перь два вы­чи­та­ния. Но сле­ду­ет знать, что про­цес­сор не вы­пол­ня­ет срав­не­ние двух це­лых чи­сел на­пря­мую: вме­сто это­го вна­ча­ле вы­пол­ня­ет­ся вы­чи­та­ние, а за­тем срав­не­ние с ну­лём. Так что два вы­чи­та­ния у нас и так бы­ли бы (не­яв­но). Мы фак­ти­че­ски за­ме­ни­ли од­ну опе­ра­цию срав­не­ния с ну­лём на од­но до­пол­ни­тель­ное вы­чи­та­ние, вы­пол­ня­е­мое опе­ра­ци­ей <.

В за­вер­ше­ние дан­ной ча­сти ска­жу, что, на­при­мер, ком­пи­ля­тор Microsoft Visual Studio при вклю­чен­ной оп­ти­ми­за­ции зна­ет этот при­ём, и код if(x1<=x && x<x2) ком­пи­ли­ру­ет­ся в те же ин­ст­рук­ции про­цес­со­ра, что и if( static_cast<unsigned int>(x-x1) < static_cast<unsigned int>(x2-x1) ). В свя­зи с этим опи­сан­ный при­ём сле­ду­ет при­ме­нять, толь­ко ес­ли вы ра­бо­та­е­те с ту­пым ком­пи­ля­то­ром. Го­раз­до ин­те­рес­нее сле­дую­щая часть, про ко­то­рую ком­пи­ля­то­ры не зна­ют.

При­ве­де­ние чис­ла к до­пу­сти­мо­му диа­па­зо­ну

Пред­по­ло­жим те­перь, что нам на­до при­ну­ди­тель­но уме­стить зна­че­ние int x в не­ко­то­рый диа­па­зон, ле­вая гра­ни­ца ко­то­ро­го, опять же, рав­на ну­лю (ес­ли она не рав­на ну­лю, то это мож­но ис­пра­вить вы­чи­та­ни­ем). На­при­мер, мы вы­чис­ли­ли цвет, ко­то­рый не дол­жен вы­хо­дить за пре­де­лы диа­па­зо­на [0, ... , 255].

Обыч­но в этом слу­чае пи­шет­ся код при­мер­но та­ко­го ви­да:

unsigned char c = min(max(x, 0), 255);

то есть вна­ча­ле бе­рёт­ся мак­си­мум из чи­сел x и 0 (ес­ли x от­ри­ца­те­лен, то он пре­вра­ща­ет­ся в 0), а за­тем по­лу­чен­ное чис­ло про­ве­ря­ет­ся на пред­мет пре­вы­ше­ния 255, и за­ме­ня­ет­ся на 255 в слу­чае это­го са­мо­го пре­вы­ше­ния.

При­ве­дён­ный при­мер — са­мый худ­ший ва­ри­ант ре­ше­ния про­бле­мы. Де­ло в том, что функ­ции min() и max() со­дер­жат в се­бе по од­но­му срав­не­нию. Та­ким об­ра­зом, у нас все­гда вы­пол­ня­ют­ся 2 срав­не­ния не­за­ви­си­мо от зна­че­ния x.

Мож­но не­мно­го улуч­шить си­ту­а­цию. Для это­го за­ме­тим, что ес­ли «сра­бо­та­ла» функ­ция max(), то нет не­об­хо­ди­мо­сти в вы­пол­не­нии функ­ции min(). На­пи­шем что-ни­будь ти­па это­го:

unsigned char c = x < 0 ? 0 : (x > 255 ? 255 : x);

В при­ве­дён­ном ко­де, ес­ли x < 0, сра­зу воз­вра­ща­ет­ся 0, и не вы­пол­ня­ет­ся срав­не­ние с чис­лом 255. Дан­ный код для от­ри­ца­тель­ных чи­сел вы­пол­ня­ет од­ну про­вер­ку, а для всех осталь­ных — две.

Ока­зы­ва­ет­ся, мож­но улуч­шить код та­ким об­ра­зом, что­бы для всех чи­сел, по­па­даю­щих в диа­па­зон (та­ких на­вер­ня­ка бу­дет боль­шин­ство), вы­пол­ня­лась все­го од­на про­вер­ка. Для это­го при­ме­ним при­ём с кон­т­ро­лем диа­па­зо­на:

unsigned char c =
    (unsigned)x <= (unsigned)255 ? x : (x > 0 ? 255 : 0);

То есть вна­ча­ле мы про­ве­ря­ем, по­па­да­ет ли зна­че­ние x в до­пу­сти­мый диа­па­зон [0, ... , 255] (и воз­вра­ща­ем x сра­зу, ес­ли по­па­да­ет), а ина­че про­ве­ря­ем, за ка­кой имен­но край диа­па­зо­на оно вы­шло, и воз­вра­ща­ем со­от­вет­ствую­щий край.

В OpenCV есть боль­шое ко­ли­че­ство эф­фек­тив­но реа­ли­зо­ван­ных функ­ций при­ве­де­ния ти­па с учё­том «вме­ще­ния» в до­пу­сти­мый диа­па­зон це­ле­во­го ти­па. Это шаб­лон­ные функ­ции saturate_cast<це­ле­вой_тип>(ис­ход­ное_зна­че­ние). По­это­му ес­ли вы ис­поль­зу­е­те OpenCV, мо­же­те за­пи­сать по­след­ний код сле­дую­щим об­ра­зом:

unsigned char c = cv::saturate_cast<unsigned char>( x );

Семь отзывов на запись «Кон­т­роль диа­па­зо­на це­ло­го чис­ла (С++)»

а еще по­сты на эту те­му бу­дут в бу­ду­щем?
Здрав­ствуй­те, ре­аль­но кру­тые Ча­сы Ulysse Nardin.
До­став­ка по всей Рос­сии, Укра­и­не, Бе­ло­рус­сии и Ка­зах­ста­ну.
http://buytimes.ru
drug cymbalta
Retin-A
Propranolol
generic clomid
canadian pharmacy cialis

Оставить отзыв

Жёлтые поля обязательны к заполнению

   

Можете использовать теги <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang=""> <div class=""> <span class=""> <br>