{"id":7633,"date":"2012-05-14T07:00:00","date_gmt":"2012-05-14T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2012\/05\/14\/what-is-the-historical-reason-for-muldiv1-0x80000000-0x80000000-returning-2\/"},"modified":"2012-05-14T07:00:00","modified_gmt":"2012-05-14T07:00:00","slug":"what-is-the-historical-reason-for-muldiv1-0x80000000-0x80000000-returning-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20120514-00\/?p=7633","title":{"rendered":"What is the historical reason for MulDiv(1, -0x80000000, -0x80000000) returning 2?"},"content":{"rendered":"<p>\nCommenter rs asks,\n&#8220;<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/07\/20\/10040074.aspx#10040424\">Why does Windows (historically) return 2 for\n<code>MulDiv(1, -0x80000000, -0x80000000)<\/code> while Wine returns zero?<\/a>&#8221;\n<\/p>\n<p>\nThe <code>MulDiv<\/code> function multiplies the first two parameters\nand divides by the third. Therefore, the mathematically correct\nanswer for\n<code>MulDiv(1, -0x80000000,\n-0x80000000)<\/code> is 1,\nbecause\n<i>a<\/i>&nbsp;&times;\n<i>b<\/i>&nbsp;&divide;\n<i>b<\/i>&nbsp;=&nbsp;<i>a<\/i> for all nonzero&nbsp;<i>b<\/i>.\n<\/p>\n<p>\nSo both Windows and Wine get it wrong.\nI don&#8217;t know why Wine gets it wrong, but I dug through the\narchives to figure out what happened to Windows.\n<\/p>\n<p>\nFirst, some background.\nWhat&#8217;s the point of the <code>MulDiv<\/code> function anyway?\n<\/p>\n<p>\nBack in the days of 16-bit Windows,\nfloating point was very expensive.\nMost people did not have math coprocessors,\nso floating point was performed via software emulation.\nAnd the software emulation was slow.\nFirst, you issued a floating point operation on the assumption\nthat you had a float point coprocessor.\nIf you didn&#8217;t, then a <i>coprocessor not available<\/i>\nexception was raised.\nThis exception handler had a lot of work to do.\n<\/p>\n<p>\nIt decoded the instruction that caused\nthe exception and then emulated the operation.\nFor example, if the bytes at the point of the exception were\n<code>d9 45 08<\/code>, the exception handler would have to\nfigure out that the instruction was <code>fld dword ptr ds:[di][8]<\/code>.\nIt then had to simulate the operation of that instruction.\nIn this case, it would\nretrieve the caller&#8217;s <code>di<\/code> register,\nadd 8 to that value,\nload four bytes from that address\n(relative to the caller&#8217;s <code>ds<\/code> register),\nexpand them from 32-bit floating point to 80-bit floating point,\nand push them onto a pretend floating point stack.\nThen it advanced the instruction pointer three bytes and\nresumed execution.\n<\/p>\n<p>\nThis took an instruction that with a coprocessor would take\naround 40 cycles (already slow) and ballooned its total execution\ntime to a few hundred, probably thousand cycles.\n(I didn&#8217;t bother counting. Those who are offended by this\nhorrific laziness on my part can apply for a refund.)\n<\/p>\n<p>\nIt was in this sort of floating point-hostile environment that\nWindows was originally developed.\nAs a result,\nWindows has historically avoided using floating point and preferred\nto use integers.\nAnd one of the things you often have to do with integers is\nscale them by some ratio.\nFor example, a horizontal dialog unit is &frac14; of the average\ncharacter width, and a vertical dialog unit is 1\/8 of the average\ncharacter height.\nIf you have a value of, say, 15 horizontal dlu, the corresponding\nnumber of pixels is\n15 &times; average character width &divide; 4.\nThis multiply-then-divide operation is quite common, and that&#8217;s\nthe model that the <code>MulDiv<\/code> function is designed to\nhelp out with.\n<\/p>\n<p>\nIn particular, <code>MulDiv<\/code> took care of three\nthings that a simple\n<i>a<\/i>&nbsp;&times;\n<i>b<\/i>&nbsp;&divide;\n<i>c<\/i> didn&#8217;t.\n(And remember, we&#8217;re in 16-bit Windows, so <i>a<\/i>,\n<i>b<\/i>&nbsp;and&nbsp;<i>c<\/i> are all 16-bit signed values.)\n<\/p>\n<ul>\n<li>The intermediate product <i>a<\/i>&nbsp;&times;&nbsp;<i>b<\/i> was\n    computed as a 32-bit value, thereby avoiding overflow.<\/p>\n<li>The result was <i>rounded<\/i> to the nearest integer instead\n    of truncated toward zero<\/p>\n<li>If <i>c<\/i>&nbsp;=&nbsp;0 or if the result did not fit\n    in a signed 16-bit integer, it returned <i>INT_MAX<\/i>\n    or <i>INT_MIN<\/i> as appropriate.\n<\/ul>\n<p>\nThe <code>MulDiv<\/code> function was written in assembly language,\nas was most of GDI at the time.\nOh right, the <code>MulDiv<\/code> function was exported by GDI\nin 16-bit Windows.\nWhy?\nProbably because they were the people who needed the function first,\nso they ended up writing it.\n<\/p>\n<p>\nAnyway, after I studied the assembly language for the function,\nI found the bug.\nA <code>shr<\/code> instruction was accidentally coded as <code>sar<\/code>.\nThe problem manifests itself only for the denominator\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/ericlippert\/archive\/2011\/01\/24\/spot-the-defect-bad-comparisons-part-two.aspx\">\n<code>&minus;0x8000<\/code><\/a>, because that&#8217;s the only one whose\nabsolute value has the high bit set.\n<\/p>\n<p>\nThe purpose of the <code>sar<\/code> instruction was to divide the\ndenominator by two, so it can get the appropriate rounding behavior\nwhen there is a remainder.\nReverse-compiling back into C, the function goes like this:\n<\/p>\n<pre>\nint16 MulDiv(int16 a, int16 b, int16 c)\n{\n int16 sign = a ^ b ^ c; \/\/ sign of result\n \/\/ make everything positive; we will apply sign at the end\n if (a &lt; 0) a = -a;\n if (b &lt; 0) b = -b;\n if (c &lt; 0) c = -c;\n \/\/  add half the denominator to get rounding behavior\n uint32 prod = UInt16x16To32(a, b) + c \/ 2;\n if (HIWORD(prod) &gt;= c) goto overflow;\n int16 result = UInt32Div16To16(prod, c);\n if (result &lt; 0) goto overflow;\n if (sign &lt; 0) result = -result;\n return result;\noverflow:\n return sign &lt; 0 ? INT_MIN : INT_MAX;\n}\n<\/pre>\n<p>\nGiven that I&#8217;ve already told you where the bug is,\nit should be pretty easy to spot in the code above.\n<\/p>\n<p>\nAnyway, when this assembly language function was ported\nto Win32, it was ported as, well, an assembly language function.\nAnd the port was so successful,\nit even preserved (probably by accident) the sign extension bug.\n<\/p>\n<p>\nMind you, it&#8217;s a bug with amazing seniority.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Commenter rs asks, &#8220;Why does Windows (historically) return 2 for MulDiv(1, -0x80000000, -0x80000000) while Wine returns zero?&#8221; The MulDiv function multiplies the first two parameters and divides by the third. Therefore, the mathematically correct answer for MulDiv(1, -0x80000000, -0x80000000) is 1, because a&nbsp;&times; b&nbsp;&divide; b&nbsp;=&nbsp;a for all nonzero&nbsp;b. So both Windows and Wine get it [&hellip;]<\/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":[2],"class_list":["post-7633","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Commenter rs asks, &#8220;Why does Windows (historically) return 2 for MulDiv(1, -0x80000000, -0x80000000) while Wine returns zero?&#8221; The MulDiv function multiplies the first two parameters and divides by the third. Therefore, the mathematically correct answer for MulDiv(1, -0x80000000, -0x80000000) is 1, because a&nbsp;&times; b&nbsp;&divide; b&nbsp;=&nbsp;a for all nonzero&nbsp;b. So both Windows and Wine get it [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/7633","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=7633"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/7633\/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=7633"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=7633"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=7633"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}