{"id":101076,"date":"2019-03-01T07:00:00","date_gmt":"2019-03-01T22:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=101076"},"modified":"2019-03-12T23:58:13","modified_gmt":"2019-03-13T06:58:13","slug":"20190301-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190301-00\/?p=101076","title":{"rendered":"How to compare two packed bitfields without having to unpack each field"},"content":{"rendered":"<p>Suppose you are packing multiple bitfields into a single integer. Let&#8217;s say you have a 16-bit integer that you have packed three bitfields into: <\/p>\n<table CLASS=\"cp3\" BORDER=\"0\" CELLPADDING=\"3\" STYLE=\"border-collapse: collapse\">\n<tr>\n<td STYLE=\"width: 2ex;text-align: right\">15<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">14<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">13<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">12<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">11<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">10<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">9<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">8<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">7<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">6<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">5<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">4<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">3<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">2<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">1<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">0<\/td>\n<\/tr>\n<tr>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"5\">r<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"6\">g<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"5\">b<\/td>\n<\/tr>\n<\/table>\n<p>Suppose you have two of these packed bitfields, <var>x<\/var> and <var>y<\/var>, <\/p>\n<table CLASS=\"cp3\" BORDER=\"0\" CELLPADDING=\"3\" STYLE=\"border-collapse: collapse\">\n<tr>\n<td STYLE=\"width: 2ex;text-align: right\">15<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">14<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">13<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">12<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">11<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">10<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">9<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">8<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">7<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">6<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">5<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">4<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">3<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">2<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">1<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">0<\/td>\n<\/tr>\n<tr>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"5\">xr<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"6\">xg<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"5\">xb<\/td>\n<\/tr>\n<tr>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"5\">yr<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"6\">yg<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"5\">yb<\/td>\n<\/tr>\n<\/table>\n<p>and you want to know whether every field in <var>x<\/var> is greater than or equal the corresponding field in <var>y<\/var>. I.e., you want to determine whether <var>xr &ge; yr<\/var>, <var>xg &ge; yg<\/var>, and <var>xb &ge; yb<\/var>. <\/p>\n<p>One way would be to unpack the bitfields. <\/p>\n<pre>\nbool IsEveryComponentGreaterThanOrEqual(uint16_t x, uint16_t y)\n{\n auto xr = x &gt;&gt; 11;\n auto yr = y &gt;&gt; 11;\n if (xr &lt; yr) return false;\n\n auto xg = (x &gt;&gt; 5) &amp; 0x3F;\n auto yg = (y &gt;&gt; 5) &amp; 0x3F;\n if (xg &lt; yg) return false;\n\n auto xb = x &amp; 0x1F;\n auto yb = y &amp; 0x1F;\n if (xb &lt; yb) return false;\n\n return true;\n}\n<\/pre>\n<p>There&#8217;s an obvious optimization here, which is to avoid the extra shifting. <\/p>\n<pre>\nbool IsEveryComponentGreaterThanOrEqual(uint16_t x, uint16_t y)\n{\n auto xr = x &amp; 0xF100;\n auto yr = y &amp; 0xF100;\n if (xr &lt; yr) return false;\n\n auto xg = x &amp; 0x07E0;\n auto yg = y &amp; 0x07E0;\n if (xg &lt; yg) return false;\n\n auto xb = x &amp; 0x001F;\n auto yb = y &amp; 0x001F;\n if (xb &lt; yb) return false;\n\n return true;\n}\n<\/pre>\n<p>But suppose this comparison is part of your program&#8217;s inner loop, so you&#8217;re hoping for something better. <\/p>\n<p>Well, if you had planned ahead and inserted a zero padding bit at the front of each field: <\/p>\n<table CLASS=\"cp3\" BORDER=\"0\" CELLPADDING=\"3\" STYLE=\"border-collapse: collapse\">\n<tr>\n<td STYLE=\"width: 2ex;text-align: right\">18<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">17<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">16<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">15<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">14<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">13<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">12<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">11<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">10<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">9<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">8<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">7<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">6<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">5<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">4<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">3<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">2<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">1<\/td>\n<td STYLE=\"width: 2ex;text-align: right\">0<\/td>\n<\/tr>\n<tr>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"1\">0<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"5\">r<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"1\">0<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"6\">g<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"1\">0<\/td>\n<td STYLE=\"border: solid 1px black;text-align: center\" COLSPAN=\"5\">b<\/td>\n<\/tr>\n<\/table>\n<p>then you could subtract the two values and see if any padding bit became set, which indicates that an underflow occurred somewhere to the right. <\/p>\n<pre>\nbool IsEveryComponentGreaterThanOrEqual(uint32_t x, uint32_t y)\n{\n auto m = (x - y) &amp; ((1 &lt;&lt; 18) | (1 &lt;&lt; 12) | (1 &lt;&lt; 5));\n return m == 0;\n}\n<\/pre>\n<p>However, this forces you to reserve padding bits, and it seems silly to have padding bits all over your data just for this purpose. I mean, those are bits that could&#8217;ve been doing something useful! <\/p>\n<p>In our example, those three extra bits forced us to use a larger integral type, which means our memory usage doubled. <\/p>\n<p>Can you do it without inserting padding bits? <\/p>\n<p>Indeed you can, thanks to a trick from <a HREF=\"http:\/\/emulators.com\/\">emulator master<\/a> Darek Mihocka: The carry-out vector. <\/p>\n<p>You can read <a HREF=\"http:\/\/www.emulators.com\/docs\/LazyOverflowDetect_Final.pdf\">the paper<\/a> or take the easier route and <a HREF=\"http:\/\/www.emulators.com\/docs\/Mihocka-Troeger-CGO-WISH-2010_final.pdf\">read the presentation<\/a>. <\/p>\n<p>In this case, we want the subtraction carry-out vector (which is really the borrow vector). The formula is <a HREF=\"https:\/\/sourceforge.net\/p\/bochs\/code\/HEAD\/tree\/branches\/REL_2_6\/bochs\/cpu\/lazy_flags.h#l55\">right here in the Bochs emulator source code<\/a>. <\/p>\n<pre>\n#define SUB_COUT_VEC(op1, op2, result) \\\n  (((~(op1)) &amp; (op2)) | ((~((op1) ^ (op2))) &amp; (result)))\n<\/pre>\n<p>In the subtraction carry-out vector, a bit is set if the subtraction resulted in a borrow at that position. We then check whether there was a borrow at the corresponding high bits 4, 10, or 15. <\/p>\n<p>Here we go: <\/p>\n<pre>\nbool IsEveryComponentGreaterThanOrEqual(uint16_t x, uint16_t y)\n{\n auto c = ((~x &amp; y) | (~(x ^ y) &amp; (x - y));\n c &amp;= 0x8410;\n return c == 0;\n}\n<\/pre>\n<p>Slide 13 of the presentation linked above shows how this technique can be used to implement saturating bitfield arithmetic in general-purpose registers. Who needs SIMD registers! <\/p>\n<p>The carry-out vector is truly magical. <\/p>\n<p><b>Bonus reading<\/b>: <a HREF=\"http:\/\/bochs.sourceforge.net\/How%20the%20Bochs%20works%20under%20the%20hood%202nd%20edition.pdf\">How Bochs Works Under the Hood<\/a>. The &#8220;Lazy flags handling&#8221; section has a useful diagram. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>The magic carry-out vector.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-101076","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The magic carry-out vector.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/101076","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=101076"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/101076\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=101076"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=101076"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=101076"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}