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

加減算

指数を合わせて加減算するだけです。指数は大きいほうに合わせます。その際に仮数部が四捨五入でなく切り捨てられることがありますが、細かいことは気にしないことにします。

@echo off

setlocal enabledelayedexpansion
call :int2float -5
set /a a = %ERRORLEVEL%
call :int2float 13
set /a b = %ERRORLEVEL%
call :subtract_float %a% %b%
call :float2int %ERRORLEVEL%
echo %ERRORLEVEL%
exit /b 0

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

:float2int
    setlocal
    call :decode_float %1
    if %exp% LSS 24 (
        set /a n = "sig >> (24 - exp)"
    ) else (
        set /a n = "sig << (exp - 24)"
    )
    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%

: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