In our routine we take a class(*) variable and promote it to the largest REAL type available and then compare it to the symmetric range of the type and kind from the second value assuming that the model requires -HUGE to HUGE to be valid values, checking for Inf and NAN input values, basically. The promotion is done using something that started from the M_anything module
https://github.com/urbanjost/M_anything
> pure elemental function anyscalar_to_real128(valuein) result(d_out)
> implicit none
> $@(#) M_anything::anyscalar_to_real128(3f): convert integer or real parameter of any kind to real128
> class(*),intent(in) :: valuein
> real(kind=real128) :: d_out
> character(len=3),save :: readable='NaN'
> select type(valuein)
> type is (integer(kind=int8)); d_out=real(valuein,kind=real128)
> type is (integer(kind=int16)); d_out=real(valuein,kind=real128)
> type is (integer(kind=int32)); d_out=real(valuein,kind=real128)
> type is (integer(kind=int64)); d_out=real(valuein,kind=real128)
> type is (real(kind=real32)); d_out=real(valuein,kind=real128)
> type is (real(kind=real64)); d_out=real(valuein,kind=real128)
> Type is (real(kind=real128)); d_out=valuein
> type is (logical); d_out=merge(0.0_real128,1.0_real128,valuein)
> type is (character(len=*)); read(valuein,*) d_out
> class default
> !!d_out=huge(0.0_real128)
> read(readable,*)d_out
> !!stop '*M_anything::anyscalar_to_real128: unknown type'
> end select
> end function anyscalar_to_real128
but has since been refined; but basically the C idea of promoting to the largest available is being used so far. Seeing if I can get permission to share the result as it has some "lessons learned" aspects that are interesting; about whether to keep the function pure or not, breaking the promotion up between integer and real, whether to use class(*) or templating. Generalizing it got to be a bit more complex than what the original issue warranted but interesting.
In the original code that started it the ABS() solution would have been fine for that particular usage but it sparked an interest in having a general function that is able to ensure a similar problem does not arise. So for the time being OUT_OF_RANGE is banned! There are a couple of versions still being bandied about; all seem to resolve the issue so performance is expected to be the tie-breaker.