This code is an ugly hack, and will only serve to confuse you, now and when you come back to look at this code in the future. The idea behind pointer arithmetic is to make accessing arrays easier, not harder. If you change
float *p;
to
float **p;
your pointer is now a double pointer, which means that when you use the indirection operator (*) once, you get another pointer. The array you've created looks like this:
[0,0][0,1][0,2][0,3][0,4]
[1,0][1,1][1,2][1,3][1,4]
[2,0][2,1][2,2][2,3][2,4]
[3,0][3,1][3,2][3,3][3,4]
[4,0][4,1][4,2][4,3][4,4]
[5,0][5,1][5,2][5,3][5,4]
[6,0][6,1][6,2][6,3][6,4]
[7,0][7,1][7,2][7,3][7,4]
[8,0][8,1][8,2][8,3][8,4]
[9,0][9,1][9,2][9,3][9,4]
Since you have two levels of indirection, the first pointer will point to your row, and the second one will point to your column. Since indirection is done backwards, that means that in pointer notation the innermost element is the element that would be furthest left in array notation.
arr[ROW][COL] === *(*(p + ROW) + COL)
Now, if you want to access elements, you can do that using array notation arr[ROW][COL], or with pointer arithmetic. With a double pointer, there are two levels of indirection. So whereas you had to use
p = (float *) balance;
*(p + 16)
to get the location you wanted, it would actually be a lot easier (in my opinion) to simply write
p = balance;
*(*(p + 3) + 1)
because now you're using pointer arithmetic similarly to array indexing, and it's easier to tell just at a glance which element you want to point at.
As far as casting a double pointer to a single pointer, it's really easier to just handle the double pointer in the first place, and that's all that the name of an array is.
int arr[ROW][COL] = {0};
int **p_arr = arr; /* Valid operation. arr without array notation is a double pointer */