It's impossible to give a reasonable answer without knowing what it is you want to achieve.
void* Joe;
means Joe is a pointer to something. We just have no idea what kind of thing it points to.
float** Jim;
means Jim is a pointer. And we know what kind of thing it is supposed to point to: It is supposed to point to float*, that is to another pointer, pointing to a float.
Now you can assign:
Jim = (float **)Joe;
What happens now is that Jim points to whatever Joe was pointing to. We have no idea what Joe was pointing to, but Jim points to the same thing. If Joe pointed to an int, then Jim points to the same int, but the compiler thinks that Jim points to a float*. Since int and float* are very different things, using *Jim will lead to disaster.
If Joe happened to point to a float*, then Jim will now point to the exact same float* and everything is fine. If Joe happened to point to a float (remember a float is not the same as a float * ) or a double* (remember a double* is not the same as a float* ) things will go wrong.
To repeat: The assignment Jim = (float **)Joe; doesn't change what Joe points to. It changes what the compiler thinks Jim is pointing to. If what the compiler thinks Jim is pointing to, and what Jim actually points to, are not the same, then things will go wrong.
Your line
float** Jim = *(float*) &Joe;
is totally wrong. Joe is a pointer to something. We don't know what. &Joe is the address of that pointer, so it is a pointer to Joe, which is a pointer that points to something we don't know (void **). You cast that pointer to float* and that gives a pointer to Joe, but the compiler know thinks that the variable Joe contains a float and not a void*. * (float* ) &Joe; reads the void* stored in the variable Joe, but the compiler thinks it is not a void*, but a float. That couldn't be any wronger. Then you ask the compiler to assign that float value to a float * * which the compiler won't allow, because a float and a float * * are totally different things.