> var a byte = (1 << x) / 2
> var b byte = (1 << y) / 2
That one's more obvious [once the parentheses are added], and I find it helps understand the float case as well.
x is a constant, so 1 << x is calculated at compile time to arbitrary precision, and then converted to a byte. That's fine.
y is a variable, so this is an expression to be calculated at run time. The calculation is done at byte precision, because the context says that the result is being stored as a byte. You wouldn't expect it to be done at any higher precision, would you?
Similarly,
b := byte((1 << y) / 2)
and again, the implication is that the 1 is a byte. If you want the calculation to be performed at a higher precision, before converting to byte, you can force it to whatever you need:
b := byte((int64(1) << y) / 2)
But in any case, if you accept that byte(1 << y) should be performed as a byte calculation, then it's logical that
c := float64(1 << y)
should be treated the same way. That is, the 1 in that expression is a float64, unless told otherwise.