I'm not sure your level of knowledge on computer architecture, so idk how deep to go on some things. There's also an insane amount of pedantry that you can get in to with these kinds of language/architecture/platform specific things. I'm going to avoid the pedantry, mostly because I'll probably get something wrong, but also because it's probably not that helpful to you. I also used to work professionally with C and later C++, but haven't done so for many years. I do currently use C for a memory constrained hobby project (neogeo: 8MB ROM, 64KB RAM) and closely pay attention to my memory usage via gcc compiler flags. I also have no clue about Rust or Python.
So mostly avoiding pedantry, on what you're likely working on (PC like platforms, common operating systems, common libc implementations), the stack and heap are both RAM, they're just a different addresses and there's not a performance implication to this (at least not because of where the variables are stored). The performance issue is the allocation and freeing process for heap is far more complicated whereas stack can be simply a matter of adding/subtracting the stack pointer and space can be allocated (or freed) for multiple variables simultaneously... Like if you have int x, y, z, the memory for all 3 variables can be allocated/freed by simply moving the stack pointer 12 bytes (assuming 4 byte ints * 3 variables), but this only works if the scope of the variables is limited to the function or code block. If the lifetime needs to extend beyond that, then it needs to be allocated to the heap. How do you do that? new/malloc. So for the most part you can just assume that if a variable has been allocated with new or malloc, then it's in heap, but if we're pedantic, this isn't defined by the language, it's implementation specific. It's just that you're most likely to be working on systems where what I said is true.
Using your custom double as an example. It's not correct to assume primitives are allocated on stack and custom types are heap. If you do: int *x = malloc(sizeof(int)); this is heap and similarly if you do BetterDouble db; then this is stack.
I'd say it doesn't actually matter in most cases and it's not worth the effort. Avoid using new/malloc unless it's necessary (like the size of the variable is only known at runtime or you need the data to exist beyond the lifetime of the function) because that is going to do a heap allocation. But what if you really want to know without looking for new/malloc or just to be sure the compiler didn't insert them for some reason? Well, there's various compiler flags that will give you information about how much memory is allocated for stack and some other data, but not necessarily on a variable by variable basis (I use -print-memory-usage). At least I don't know of any way to get variable level data out of gcc. If there is, I'd like to know for my own purposes.
If you need to know at runtime, like you want your program to do something differently if a variable points to an address in heap vs stack, there's not a language (C or C++) defined way to do this that I'm aware of, so you have to resort to platform specific approaches to get the stack pointer / address of stack and then compare it to your variable's address. If you just want to sort of manually inspect, as another commenter said, there's gdb info proc mappings, but that may not be very convenient depending on what you're doing.
So with all of that, if you really want to know and you're on a machine that has pthread_attr_getstack(), then use it compare your variable address to the stack address. Probably the easiest way to "be sure", but really I'd personally just assume stack unless I used new/malloc or I was really trying to micro optimize something.