There you go:
:%s/[\uff01-\uff5e]/\=nr2char(char2nr(submatch(0))-65248)/g
This matches the range of full-width characters, and via :help sub-replace-expr and char2nr() converts this to a number, subtracts to get this into the ASCII range for ! - ~, and converts it back to characters.
Addendum
If you need to support other (non-contiguous) characters, you can define a Map and check that first. For example, for the arrow symbols mentioned in your comment:
let g:fullToHalf = { "\u2190": "\uFFE9", "\u2192": "\uFFEB", "\u2191": "\uFFEA", "\u2193": "\uFFEC" }
:execute '%s/[\uff01-\uff5e' . join(keys(g:fullToHalf), ''). ']/\=get(g:fullToHalf, submatch(0), nr2char(char2nr(submatch(0))-65248))/g'
Note: As a one-liner, this gets really ugly. I would strongly recommend to put this into a function.
[\uff5f-\uff65\uffda-\uffee]? For example↓in this table. – Lerner Zhang Dec 10 '16 at 03:08sedorawk, but this may be a valid case for Vim. For details, see e.g. here. – Ingo Karkat Dec 19 '17 at 08:28