Bullet proof:
mid = lo / 2 + hi / 2;
Well, that is, if your algorithm can tolerate a mid == 6, for hi == 7 and lo == 7. :) :) :)
Cough, cough; seriously though: here is a variant based on the above idea which takes care of the remainders, avoiding that problem:
;; TXR Lisp
(defun mid (lo hi)
(tree-bind ((lq lr) . (hq hr)) (cons (trunc-rem lo 2) (trunc-rem hi 2))
(+ lq hq (trunc (+ lr hr) 2))))
trunc-rem has toward-zero truncation, with a remainder that is harmonized to that.
Based on my testing in the REPL, this is behaving sensibly.
Adding the quotients, and then adding to them the sum of the remainders, truncated by two, seems to be doing the trick.
(mid 7 7) is 7, (mid -7 -7) is -7 and various other cases are all sensible. (mid k (+ 2 k)) is yielding (+ 1 k), for both values being negative, either being zero, and zero-crossing cases. (mid k k) seems to be k for all k.
I think as of ISO C99, the / and % operators truncate toward zero. Unless I'm gravely mistaken, this is then nicely expressible in C as:
lo / 2 + hi / 2 + ((lo % 2) + (hi % 2)) / 2;
It's mathematically well founded: we have added the truncations, and then continue working with the remainders. This is because the following expression in fact expresses the exact result.
trunc(lo,2) + trunc(hi,2) + (rem(lo, 2) + rem(hi, 2))/2
where / is exact rational division! Dividing the remainders by two just continues the inexact division, completing it to exactness. What we're doing differently in the machine calculation is using truncating division on the sum of the remainders, which we need because mid is to be an integer result.