In addition to @Mark Bluemel good answer:
Overlap
A difference between memcpy() and memmove() is the ability to handle overlapping buffers. Notice the keyword restrict.
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
void *memmove(void *s1, const void *s2, size_t n);
restrict roughly implies access to the buffer is not interfered with by other activity.
memCopy() code does not copy well overlapping buffers when the source precedes the destination. It is more like
void* memCopy(void* restrict destination, const void* restrict source, size_t size)
By using restrict, the compiler can invoke additional optimizations.
A solution to act like memmove() involves looping up or down depending on which is greater source or destination. The trick is that this compare for pointer greatness is not certainly possible in C. memmove(), as a library function, has access to information C does not. Yet a compare of pointers converted to an integer is usually sufficient.
Types
double is not the way to go. *b = *a; is not guaranteed to copy the bit pattern nor potentially triggering a signaling not-a-number exception. Best to avoid involving floating-point concerns. Instead use a wide unsigned integer type, perhaps uintmax_t, unsigned long long, or uint64_t. (Pros and cons for each. I'd go for uint64_t when available.)
On ancient machines, *b2 = *a2; can invoke a trap. Instead use unsigned char *a2; unsigned char *b2;
Zero size
memCopy(..., ..., 0) is properly handled. Bravo! - a common mistake avoided here.
whileloops for anything other than (near-)infinite loops. Is there a reason you used them instead offorloops? For reference, this is how one version ofmemcpydoes it. \$\endgroup\$while; rather becauseforis specifically a better fit here since it has a clear loop counter \$\endgroup\$