{"id":117,"date":"2013-01-14T17:23:20","date_gmt":"2013-01-14T17:23:20","guid":{"rendered":"http:\/\/blog.mozilla.org\/nfroyd\/?p=117"},"modified":"2013-01-14T17:23:20","modified_gmt":"2013-01-14T17:23:20","slug":"64-bit-multiplication-pitfalls","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/nfroyd\/2013\/01\/14\/64-bit-multiplication-pitfalls\/","title":{"rendered":"64-bit multiplication pitfalls"},"content":{"rendered":"<p>I&#8217;ve seen several instances of code recently that look something like this:<\/p>\n<pre>void madd(int64_t *sum, int32_t x, int32_t y)\r\n{\r\n  *sum += x * y;\r\n}<\/pre>\n<p>Or this:<\/p>\n<pre>void func(int64_t);\r\n...\r\n  int32_t x, y = ...;\r\n  ...\r\n  func(x * y);<\/pre>\n<p>Unfortunately, neither of these cases do what the programmer intended.\u00a0 The intended result was to compute the full 64-bit product from multiplying two 32-bit numbers.\u00a0 What gets computed instead is the lower 32-bits of the desired product, sign-extended to 64-bits, which is quite different! The assembly produced by x86-64 GCC at <tt>-O2<\/tt> for the first example looks like:<\/p>\n<pre>    imull   %edx, %esi    # int32_t multmp = x * y\r\n    movslq  %esi, %rsi    # int64_t exttmp = static_cast&lt;int64_t&gt;(multmp)\r\n    addq    %rsi, (%rdi)  # *sum += exttmp\r\n    ret<\/pre>\n<p>If the full 64-bit product is desired, one of the arguments needs to be cast to a 64-bit value first.<\/p>\n<pre>void madd(int64_t *sum, int32_t x, int32_t y)\r\n{\r\n  *sum += static_cast&lt;int64_t&gt;(x) * y;\r\n}<\/pre>\n<p>(The standard-ese for this is that operands are automatically promoted based on the types of the operands, not on the type of the result.\u00a0 Integers smaller than <tt>int<\/tt> are promoted to <tt>int<\/tt>, which is what you want most of the time. Of course, here we&#8217;re dealing with things that are already <tt>int<\/tt>-sized[*], so we have to explicitly ask for promotion.)<\/p>\n<p>which produces the desired:<\/p>\n<pre>    movslq  %esi, %rsi    # int64_t xtmp = static_cast&lt;int64_t&gt;(x)\r\n    movslq  %edx, %rdx    # int64_t ytmp = static_cast&lt;int64_t&gt;(y)\r\n    imulq   %rdx, %rsi    # int64_t multmp = xtmp * ytmp\r\n    addq    %rsi, (%rdi)  # *sum += multmp\r\n    ret<\/pre>\n<p>The above examples are semi-obvious instances, but when dealing with types whose sizes are not specified, similar problems occur. Consider replacing <tt>int64_t<\/tt> with <tt>off_t<\/tt> and <tt>int32_t<\/tt> with <tt>size_t<\/tt> in the example above. While such code will mostly work (most files are well under 2GB or 4GB in size), <tt>off_t<\/tt> and <tt>size_t<\/tt> do not need to be the same size: try compiling <tt>sizeof off_t<\/tt> with <tt>-D_FILE_OFFSET_BITS=64<\/tt> on your favorite 32-bit Linux sometime.<\/p>\n<p>[*] Assuming we&#8217;re on a fairly standard 32-bit or 64-bit machine, of course.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve seen several instances of code recently that look something like this: void madd(int64_t *sum, int32_t x, int32_t y) { *sum += x * y; } Or this: void func(int64_t); &#8230; int32_t x, y = &#8230;; &#8230; func(x * y); Unfortunately, neither of these cases do what the programmer intended.\u00a0 The intended result was to [&hellip;]<\/p>\n","protected":false},"author":320,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/posts\/117"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/users\/320"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/comments?post=117"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/posts\/117\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/media?parent=117"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/categories?post=117"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nfroyd\/wp-json\/wp\/v2\/tags?post=117"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}