Thank all for replying to this post. As usual, the members on this forum have a lot of knowledge to share and there are many excellent methods posted above. Ultimately this is my fault for providing too simple an example and not clearly explaining my objection to wrapper routines, but this is the actual module I am trying to write/use:

```
module random
use, intrinsic :: iso_fortran_env, only: real32, real64, real128, int8, int16, int32, int64
implicit none
private
interface randrng
module procedure randrng_sp
module procedure randrng_dp
module procedure randrng_qp
module procedure randrng_i8
module procedure randrng_i16
module procedure randrng_i32
module procedure randrng_i64
end interface randrng
public :: randrng
interface normrnd
module procedure normrnd_sp
module procedure normrnd_dp
module procedure normrnd_qp
end interface normrnd
public :: normrnd
integer, parameter :: sp = real32
integer, parameter :: dp = real64
integer, parameter :: qp = real128
integer, parameter :: i8 = int8
integer, parameter :: i16 = int16
integer, parameter :: i32 = int32
integer, parameter :: i64 = int64
real, parameter :: twopi_sp = 2.0_sp*acos(-1.0_sp)
real, parameter :: twopi_dp = 2.0_dp*acos(-1.0_dp)
real, parameter :: twopi_qp = 2.0_qp*acos(-1.0_qp)
contains
subroutine randrng_sp(val, n, val_min, val_max)
implicit none
real(sp), intent(out) :: val(*)
integer, intent(in) :: n
real(sp), intent(in) :: val_min, val_max
call random_number(val(1:n))
val(1:n) = val(1:n)*(val_max - val_min) + val_min
end subroutine randrng_sp
subroutine randrng_dp(val, n, val_min, val_max)
implicit none
real(dp), intent(out) :: val(*)
integer, intent(in) :: n
real(dp), intent(in) :: val_min, val_max
call random_number(val(1:n))
val(1:n) = val(1:n)*(val_max - val_min) + val_min
end subroutine randrng_dp
subroutine randrng_qp(val, n, val_min, val_max)
implicit none
real(qp), intent(out) :: val(*)
integer, intent(in) :: n
real(qp), intent(in) :: val_min, val_max
call random_number(val(1:n))
val(1:n) = val(1:n)*(val_max - val_min) + val_min
end subroutine randrng_qp
subroutine randrng_i8(val, n, val_min, val_max)
implicit none
integer(i8), intent(out) :: val(*)
integer, intent(in) :: n
integer(i8), intent(in) :: val_min, val_max
real :: work(n)
call random_number(work)
val(1:n) = floor(work*(real(val_max) - real(val_min) + 1.0)) + val_min
end subroutine randrng_i8
subroutine randrng_i16(val, n, val_min, val_max)
implicit none
integer(i16), intent(out) :: val(*)
integer, intent(in) :: n
integer(i16), intent(in) :: val_min, val_max
real :: work(n)
call random_number(work)
val(1:n) = floor(work*(real(val_max) - real(val_min) + 1.0)) + val_min
end subroutine randrng_i16
subroutine randrng_i32(val, n, val_min, val_max)
implicit none
integer(i32), intent(out) :: val(*)
integer, intent(in) :: n
integer(i32), intent(in) :: val_min, val_max
real :: work(n)
call random_number(work)
val(1:n) = floor(work*(real(val_max) - real(val_min) + 1.0)) + val_min
end subroutine randrng_i32
subroutine randrng_i64(val, n, val_min, val_max)
implicit none
integer(i64), intent(out) :: val(*)
integer, intent(in) :: n
integer(i64), intent(in) :: val_min, val_max
real :: work(n)
call random_number(work)
val(1:n) = floor(work*(real(val_max) - real(val_min) + 1.0)) + val_min
end subroutine randrng_i64
subroutine normrnd_sp(val, n, val_mu, val_sig)
implicit none
real(sp), intent(out) :: val(*)
integer, intent(in) :: n
real(sp), intent(in) :: val_mu, val_sig
real(sp) :: u(ceiling(n/2.0),2)
integer :: i
call random_number(u)
do i=1,size(u, dim=1)
do while (u(i,1).eq.0.0)
call random_number(u(i,1))
end do
end do
val(1:n/2) = val_mu + val_sig*sqrt(-2.0*log(u(1:n/2,1)))*cos(twopi_sp*u(1:n/2,2))
val(n/2+1:n) = val_mu + val_sig*sqrt(-2.0*log(u(1:n-n/2,1)))*sin(twopi_sp*u(1:n-n/2,2))
end subroutine normrnd_sp
subroutine normrnd_dp(val, n, val_mu, val_sig)
implicit none
real(dp), intent(out) :: val(*)
integer, intent(in) :: n
real(dp), intent(in) :: val_mu, val_sig
real(dp) :: u(ceiling(n/2.0),2)
integer :: i
call random_number(u)
do i=1,size(u, dim=1)
do while (u(i,1).eq.0.0)
call random_number(u(i,1))
end do
end do
val(1:n/2) = val_mu + val_sig*sqrt(-2.0*log(u(1:n/2,1)))*cos(twopi_dp*u(1:n/2,2))
val(n/2+1:n) = val_mu + val_sig*sqrt(-2.0*log(u(1:n-n/2,1)))*sin(twopi_dp*u(1:n-n/2,2))
end subroutine normrnd_dp
subroutine normrnd_qp(val, n, val_mu, val_sig)
implicit none
real(qp), intent(out) :: val(*)
integer, intent(in) :: n
real(qp), intent(in) :: val_mu, val_sig
real(qp) :: u(ceiling(n/2.0),2)
integer :: i
call random_number(u)
do i=1,size(u, dim=1)
do while (u(i,1).eq.0.0)
call random_number(u(i,1))
end do
end do
val(1:n/2) = val_mu + val_sig*sqrt(-2.0*log(u(1:n/2,1)))*cos(twopi_qp*u(1:n/2,2))
val(n/2+1:n) = val_mu + val_sig*sqrt(-2.0*log(u(1:n-n/2,1)))*sin(twopi_qp*u(1:n-n/2,2))
end subroutine normrnd_qp
end module random
```

I want my routines to be callable with a single name, either `randrng`

for uniform or `normrnd`

for normal random numbers. They should work for the intrinsic real and integer types, regardless of rank, with the compiler selecting the correct routine at compile time for minimal overhead. It is already bad enough having to copy/paste the same logic 7 times for the intrinsic types. I do not want to also have 15 versions of each one to cover each possible array rank.