If you don't know a priori which array will be longer, iterate directly over indices from both:
# generate sum for all indices in a *or* b
declare -A finished=( )                # track which indices we've already added
for idx in "${!a[@]}" "${!b[@]}"; do   # iterate over indices from *both* arrays
  [[ ${finished[$idx]} ]] && continue  # skip anything tracked as finished
  printf '%s\n' "$idx => $(( ${a[$idx]:-0} + ${b[$idx]:-0} ))"
  finished[$idx]=1
done
This works with all kinds of arrays, including sparse arrays or associative arrays, so long as the indices used in arrays a and b match.
Let's take an example:
 #    0  1  2  3  4  5  6  7  8   ## indices
 a=(  1  2  3  4  5  6  7  8  9 )
 b=( 10 20 30 40 50 60 70 80 90 )
 # remove "3", at index 2, from array a
 unset a[2]
Now, after that unset a[2] was run, the highest index in a is still 8, but ${#a[@]} is 8 instead of 7; the rule that the highest index is one less than the number of entries in the array is broken.
However, this answer still works:
0 => 11
1 => 22
3 => 44
4 => 55
5 => 66
6 => 77
7 => 88
8 => 99
2 => 30
We're still aligning the 3 and the 30 by their index values, and treating the absent element 2 from a as a 0.
When run with:
declare -A a=( [foo]=1 [bar]=1 [baz]=1 ) b=( [bar]=1 [baz]=1 [qux]=1 )
...this emits:
bar => 2
baz => 2
foo => 1
qux => 1
To give a concrete example of a case where this works and many of the other answers don't:
a=( [10234]=20 [25432]=30 )
b=( [10234]=1  [25432]=2  )
...and the result is properly:
10234 => 21
25432 => 32
As another:
declare -A a=( [bob]=20 [jim]=30 )
declare -A b=( [bob]=1  [jim]=2  )
and the proper result:
bob => 21
jim => 32
I'm printing indices in the above to demonstrate more about what the code is doing under-the-hood, but of course you can just remove $idx => from the format strings to exclude them.