{"id":108355,"date":"2023-06-19T07:00:00","date_gmt":"2023-06-19T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108355"},"modified":"2023-06-19T05:43:43","modified_gmt":"2023-06-19T12:43:43","slug":"20230619-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230619-00\/?p=108355","title":{"rendered":"Why am I being told about a signed\/unsigned comparison, and why only sometimes, and how can I fix it?"},"content":{"rendered":"<p>A customer was using the Windows <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows-hardware\/drivers\/taef\/\"> Test Authoring and Execution Framework (TAEF)<\/a>, and they found that this line of code compiled successfully most of the time:<\/p>\n<pre>std::vector&lt;int&gt; values = CalculateValues();\r\nVERIFY_ARE_EQUAL(values.size(), 0);\r\n\/\/ warning C4389: '==': signed\/unsigned mismatch\r\n<\/pre>\n<p>The <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows-hardware\/drivers\/taef\/verify\"> <code>VERIFY_<wbr \/>ARE_<wbr \/>EQUAL<\/code> macro<\/a> compares its two parameters and reports a test failure if they are not equal. The customer found that the above code compiled okay for x86-64, but produced the indicated error when compiled for x86-32. What&#8217;s going on?<\/p>\n<p>The <code>VERIFY_<wbr \/>ARE_<wbr \/>EQUAL<\/code> macro passes its parameters onward to a helper function, which in turn passes the parameters to another helper function, which in turn passes the parameters to yet another helper function, which looks like this:<\/p>\n<pre>template&lt;typename T1, typename T2&gt;\r\nstatic bool AreEqual(const T1&amp; expected, const T2&amp; actual)\r\n{\r\n    \/\/ != 0 to handle overloads of operator==\r\n    \/\/ that return BOOL instead of bool\r\n    return (expected == actual) != 0;\r\n}\r\n<\/pre>\n<p>The types of the parameters are deduced as <code>std::<wbr \/>vector&lt;int&gt;::<wbr \/>size_type<\/code> and <code>int<\/code>. The first parameter is a <code>std::<wbr \/>vector&lt;int&gt;::<wbr \/>size_type<\/code>, because that&#8217;s what the <code>vector::<wbr \/>size()<\/code> method returns. The second parameter is a <code>int<\/code> because that&#8217;s what <code>0<\/code> is.<\/p>\n<p>Therefore, the compiler warning of an unsigned\/signed comparison is valid. You are comparing an unsigned value (the size of the vector) against a signed value (the integer zero). The warning is present because the rules for an unsigned\/signed comparison is to convert the signed value to unsigned, and then compare the two unsigned values. This is different from the mathematical result.<\/p>\n<pre>unsigned int v1 = 4294967295;\r\nint v2 = -1;\r\nif (v1 == v2) { \/* true *\/ }\r\nif (v1 &gt; v2) { \/* false *\/ }\r\n<\/pre>\n<p>Both results disagree with the mathematical expectations. 4294967295 \u2260 \u22121, so mathematically, the first test should fail. And 4294967295 &gt; \u22121, so mathematically, the second test should succeed. What&#8217;s happening is that the value \u22121 is converted from a signed integer to unsigned, and that means that it becomes 4294967295. (The rule for signed-to-unsigned conversion is that negative numbers become the positive equivalent modulo <code>1 &lt;&lt; bit_size<\/code>.)<\/p>\n<p>You and I can see that this discrepancy doesn&#8217;t apply here because the signed integer being compared against is the literal value zero, which is not negative.<\/p>\n<p>What&#8217;s happening is that the back-end applies different degrees of inlining based on optimization levels and the target architecture. Higher optimization levels will consider higher degrees of inlining, and some architectures may be more conducive to inlining than others, depending on things like register pressure, the complexity of the calling convention, or other factors.<\/p>\n<p>If the back-end ends up doing enough inlining that the constant <code>0<\/code> gets inlined into the <code>==<\/code> operator, then the compiler realizes, &#8220;Oh, I would normally complain about a signed\/unsigned mismatch, but I can see that the value <code>0<\/code> is not negative, so I will suppress the warning because it doesn&#8217;t apply to this case.&#8221; On the other hand, if the back-end doesn&#8217;t inline aggressively enough, it won&#8217;t propagate the constant deep enough to realize that it&#8217;s never negative, and you get the warning.<\/p>\n<p>The customer offered this solution but complained that it was quite unwieldy:<\/p>\n<pre>VERIFY_ARE_EQUAL(values.size(), (std::vector&lt;int&gt;::size_type)0);\r\n<\/pre>\n<p>That will work, but so too will casting to any other unsigned type, because the compiler is not insisting that the second parameter&#8217;s type match the first parameter&#8217;s type exactly. It just wants them to agree on signedness. You could pass a <code>size_t<\/code> or even a <code>uint8_t<\/code>; as long as it&#8217;s unsigned. And probably the most convenient way to indicate an unsigned zero is to append a <code>U<\/code> to the literal.<\/p>\n<pre>VERIFY_ARE_EQUAL(values.size(), 0U);\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>How the compiler back-end can influence warnings.<\/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-108355","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>How the compiler back-end can influence warnings.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108355","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=108355"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108355\/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=108355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108355"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}