バッチファイルで浮動小数点数演算(4)

除算

被除数が除数より小さければ被除数を2倍します。そして割り算をします。1ビットずつ求めれば簡単です。例えば、111を101で割るなら、まず、被除数のほうが大きいので商が1で、被除数から除数を引いて10、2倍して100。被除数のほうが小さいので商は10、被除数を2倍して1000。被除数のほうが大きいので、商は101となります。四捨五入はしていません。

これで準備ができました。

@echo off

setlocal enabledelayedexpansion
call :int2float 100
set /a a = %ERRORLEVEL%
call :int2float 7
set /a b = %ERRORLEVEL%
call :divide_float %a% %b%
call :float2int %ERRORLEVEL%
echo %ERRORLEVEL%
exit /b 0

:int2float
    setlocal
    call :encode_float %1 23
    exit /b %ERRORLEVEL%

:float2int
    setlocal
    call :decode_float %1
    if %exp% LSS 23 (
        set /a n = "sig >> (23 - exp)"
    ) else (
        set /a n = "sig << (exp - 23)"
    )
    exit /b %n%

:add_float
    setlocal
    call :align_exp %1 %2
    
    set /a sig = %sig1% + %sig2%
    call :encode_float %sig% %exp1%
    exit /b %ERRORLEVEL%

:subtract_float
    setlocal
    call :align_exp %1 %2
    
    set /a sig = %sig1% - %sig2%
    call :encode_float %sig% %exp1%
    exit /b %ERRORLEVEL%

:multiply_float
    setlocal
    call :decode_float %1 1
    call :decode_float %2 2
    
    set /a sign1 = 0
    if %sig1% LSS 0 (
        set /a sign1 = 1
        set /a sig1 = -%sig1%
    )
    set /a sign2 = 0
    if %sig2% LSS 0 (
        set /a sign2 = 1
        set /a sig2 = -%sig2%
    )
    
    set /a a1 = "sig1 >> 12"
    set /a b1 = "sig1 & 0xFFF"
    set /a a2 = "sig2 >> 12"
    set /a b2 = "sig2 & 0xFFF"
    
    set /a m3 = %b1% * %b2%
    set /a m2 = %a1% * %b2% + %a2% * %b1% + "(m3 >> 12)"
    set /a m1 = %a1% * %a2% + "(m2 >> 12)"
    set /a "m1 <<= 1"
    
    set /a sign = "sign1 ^ sign2"
    if %sign% == 1 set /a m1 = -%m1%
    set /a exp = %exp1% + %exp2%
    call :encode_float %m1% %exp%
    exit /b %ERRORLEVEL%

:divide_float
    setlocal
    call :decode_float %1 1
    call :decode_float %2 2
    
    set /a sign1 = 0
    if %sig1% LSS 0 (
        set /a sign1 = 1
        set /a sig1 = -%sig1%
    )
    set /a sign2 = 0
    if %sig2% LSS 0 (
        set /a sign2 = 1
        set /a sig2 = -%sig2%
    )
    
    if %sig1% LSS %sig2% (
        set /a "sig1 <<= 1"
        set /a exp1 -= 1
    )
    
    set /a sig = 0
    for /L %%k in (1, 1, 24) do (
        set /a sig = "(!sig! << 1) + !sig1! / sig2"
        set /a sig1 = !sig1! %% %sig2% * 2
    )
    
    set /a sign = "sign1 ^ sign2"
    if %sign% == 1 set /a sig = -%sig%
    set /a exp = %exp1% - %exp2%
    call :encode_float %sig% %exp%
    exit /b %ERRORLEVEL%

:align_exp
    call :decode_float %1 1
    call :decode_float %2 2
    
    if %exp1% GTR %exp2% (
        set /a de = %exp1% - %exp2%
        set /a "sig2 >>= !de!"
    ) else (
        set /a de = %exp2% - %exp1%
        set /a "sig1 >>= !de!"
        set /a exp1 += !de!
    )
    exit /b 0

:encode_float
    setlocal
    if %1 == 0 exit /b 0
    if %1 GTR 0 (
        set /a sig = %1
    ) else (
        set /a sig = -%1
    )
    set /a sign = "%1 & 0x80000000"
    set /a e = %2
    :loop_encode_float
        if %sig% GEQ 0x1000000 (
            set /a "sig >>= 1"
            set /a e += 1
            goto :loop_encode_float
        )
        if %sig% LSS 0x800000 (
            set /a "sig <<= 1"
            set /a e -= 1
            goto :loop_encode_float
        )
    
    set /a sig -= 0x800000
    set /a code = "sig | ((e + 127) << 23) | sign"
    exit /b %code%

:decode_float
    set /a sig%2 = "(%1 & 0x7FFFFF) + 0x800000"
    set /a exp%2 = "((%1 >> 23) & 0xFF) - 127"
    set /a sign = "%1 >> 31"
    if %sign% == -1 set /a sig%2 = "-sig%2"
    exit /b 0