A 32-bit Windows process has a default address space of 2 gigabytes. The lower half of the full address space (2, 32), the upper 2 GB is used by the operating system. Since almost no one else uses a 32-bit OS, you can get 4 GB when you link your program to / LARGEADESSESSAWARE.
This 2 GB virtual machine space must be shared by code and data. Your program usually loads with 0x00400000, any operating system DLL used (for example, kernel32.dll and ntdll.dll) has large download addresses (outside of 0x7F000000). And at least the default thread stack and the heap of the process are created by default before your program starts, their addresses are usually unpredictable.
Your program will be susceptible to virus attacks packaged in shrink wrappers, in most cases when you install the OS, you will have DLLs that will provide "services", such as protection against malware and cloud storage. The download address of these DLLs is unpredictable. Also, any DLL files that you linked to and implicitly loaded when your program started. Few programmers pay attention to their preferred base address and leave it at default 0x1000000. You can see these DLL files from the debugger modules window. Such DLLs often have their own CRT and tend to create their own heaps.
The calculations you highlight, especially the very large ones that cannot be obtained from the heap of low fragmentation, must find the address space in the holes left between the existing code and the data distribution. If you get 1500 MB, then your virtual machine will be pretty clean. In general, you will encounter problems of more than 650 MB, quickly losing time when the program is running for some time and fragmenting the space of the virtual machine. Highlighting failures is almost always caused by the fact that the OS cannot find a large enough hole, and not because you do not have enough VMs. The sum of the holes can be significantly larger than your unsuccessful distribution request.
These details are quickly becoming a folk tale, there are very few remaining reasons to continue to focus on x86. Target fragmentation of x64 and address space will not be a problem for the next 20 years, it is very difficult to fragment 8 terabytes of virtual machine. With a large margin to grow beyond this.
So, it should be clear why you cannot get 2048 MB, you cannot get all this. Get more information from the VMMap utility from SysInternals , it shows you how the VM is cut. And Mark Russinovich's blog post and book give a lot of background.