! { dg-do run }
! { dg-options "-std=gnu" }
! PR47567 Wrong output for small absolute values with F editing
! Test case provided by Thomas Henlich 
call verify_fmt(1.2)
call verify_fmt(-0.1)
call verify_fmt(1e-7)
call verify_fmt(1e-6)
call verify_fmt(1e-5)
call verify_fmt(1e-4)
call verify_fmt(1e-3)
call verify_fmt(1e-2)
call verify_fmt(-1e-7)
call verify_fmt(-1e-6)
call verify_fmt(-1e-5)
call verify_fmt(-1e-4)
call verify_fmt(-1e-3)
call verify_fmt(-1e-2)
call verify_fmt(tiny(0.0))
call verify_fmt(-tiny(0.0))
call verify_fmt(0.0)
call verify_fmt(-0.0)
call verify_fmt(100.0)
call verify_fmt(.12345)
call verify_fmt(1.2345)
call verify_fmt(12.345)
call verify_fmt(123.45)
call verify_fmt(1234.5)
call verify_fmt(12345.6)
call verify_fmt(123456.7)
call verify_fmt(99.999)
call verify_fmt(-100.0)
call verify_fmt(-99.999)
end

! loop through values for w, d
subroutine verify_fmt(x)
    real, intent(in) :: x
    integer :: w, d
    character(len=80) :: str, str0
    integer :: len, len0
    character(len=80) :: fmt_w_d
    logical :: result, have_num, verify_fmt_w_d
    
    do d = 0, 10
        have_num = .false.
        do w = 1, 20
            str = fmt_w_d(x, w, d)
            len = len_trim(str)
            
            result = verify_fmt_w_d(x, str, len, w, d)
            if (.not. have_num .and. result) then
                have_num = .true.
                str0 = fmt_w_d(x, 0, d)
                len0 = len_trim(str0)
                if (len /= len0) then
                    call errormsg(x, str0, len0, 0, d, "selected width is wrong")
                else
                    if (str(:len) /= str0(:len0)) call errormsg(x, str0, len0, 0, d, "output is wrong")
                end if
            end if
        end do
    end do

end subroutine

! checks for standard-compliance, returns .true. if field contains number, .false. on overflow
function verify_fmt_w_d(x, str, len, w, d)
    real, intent(in) :: x
    character(len=80), intent(in) :: str
    integer, intent(in) :: len
    integer, intent(in) :: w, d
    logical :: verify_fmt_w_d
    integer :: pos
    character :: decimal_sep = "."

    verify_fmt_w_d = .false.
    
    ! check if string is all asterisks
    pos = verify(str(:len), "*")
    if (pos == 0) return
    
    ! check if string contains a digit
    pos = scan(str(:len), "0123456789")
    if (pos == 0) call errormsg(x, str, len, w, d, "no digits")

    ! contains decimal separator?
    pos = index(str(:len), decimal_sep)
    if (pos == 0) call errormsg(x, str, len, w, d, "no decimal separator")
    
    ! negative and starts with minus?
    if (sign(1., x) < 0.) then
        pos = verify(str, " ")
        if (pos == 0) call errormsg(x, str, len, w, d, "only spaces")
        if (str(pos:pos) /= "-") call errormsg(x, str, len, w, d, "no minus sign")
    end if
    
    verify_fmt_w_d = .true.
end function

function fmt_w_d(x, w, d)
    real, intent(in) :: x
    integer, intent(in) :: w, d
    character(len=*) :: fmt_w_d
    character(len=10) :: fmt, make_fmt
    
    fmt = make_fmt(w, d)
    write (fmt_w_d, fmt) x
end function

function make_fmt(w, d)
    integer, intent(in) :: w, d
    character(len=10) :: make_fmt
    
    write (make_fmt,'("(f",i0,".",i0,")")') w, d
end function

subroutine errormsg(x, str, len, w, d, reason)
    real, intent(in) :: x
    character(len=80), intent(in) :: str
    integer, intent(in) :: len, w, d
    character(len=*), intent(in) :: reason
    integer :: fmt_len
    character(len=10) :: fmt, make_fmt
    
    fmt = make_fmt(w, d)
    fmt_len = len_trim(fmt)
    
    !print *, "print '", fmt(:fmt_len), "', ", x, " ! => ", str(:len), ": ", reason
    call abort
end subroutine
