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

Ча­сто в за­да­чах об­ра­бот­ки изоб­ра­же­ний тре­бу­ет­ся опре­де­лить, не вы­хо­дит ли це­лое чис­ло за пре­де­лы не­ко­то­ро­го диа­па­зо­на; при­чём ле­вой гра­ни­цей диа­па­зо­на яв­ля­ет­ся чис­ло 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 );

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

а еще по­сты на эту те­му бу­дут в бу­ду­щем?
Здрав­ствуй­те, ре­аль­но кру­тые Ча­сы Ulysse Nardin.
До­став­ка по всей Рос­сии, Укра­и­не, Бе­ло­рус­сии и Ка­зах­ста­ну.
http://buytimes.ru
drug cymbalta
Retin-A
Propranolol
generic clomid
canadian pharmacy cialis
atenolol wo prescription
deltasone pills
Generic Levaquin
wh0cd411110
wh0cd965654 buy levitra
wh0cd192934 BACLOFEN NO PRESCRIPTION
Ahaa, its nice dialogue regarding this article here at this website,
I have read all that, so at this time me also commenting at this place.
wh0cd759878 metformin prices
It’s very effortless to find out any matter on net as compared to books, as I found this post at this website.
I’m gone to tell my little brother, that he should also pay a quick visit
this webpage on regular basis to get updated from latest gossip.
Hi there it’s me, I am also visiting this website regularly, this web page is truly nice and the
viewers are in fact sharing good thoughts.

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

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

   

Можете использовать теги <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>