バッチファイルでは本来整数演算しかできないのですが、IEEE754の単精度をエミュレートすることにより浮動小数点数演算を実現します。Project Euler 25が解ければいいので、厳密にエミュレートはしません。
IEEE754の単精度は、簡単に言うと仮数部を23ビット、指数部を8ビット、符号を1ビットで表します。これをつなげて32ビットの整数とします。例えば、リンク先の0.15625は0x3E200000となります。整数とすることで、exit /bで関数から値を返すことができます。そうするとsetlocalが使えて安全にプログラミングすることができます。具体的には、例えば加算なら、
:add_float ... exit /b %n%
と2つの引数を整数で取って、1つの整数を返すようにします。
まず、エンコード/デコードを書きます。(符号付き整数, 指数) → 浮動小数点数をエンコード、逆をデコードとします。さきほどの例なら、(0x200000, -3) → 0x3E200000をエンコードと呼びます。
エンコードは2つ引数を取って1つ値を返します。しかし、デコードは2つ値を返すわけにはいかないので、setlocalは使わずに、sig*, exp*という環境変数を作ってそれを呼び元でも使うようにします。
call :decode_float %n% 1
とすれば、デコードした値はsig1とexp1に入ってきますし、
call :decode_float %n%
とすればsigとexpになります。ちなみに、sigはsignificand(仮数部)の略です。
下のコードは5を浮動小数点にし、再び整数化(切捨て)するだけのものです。
@echo off setlocal enabledelayedexpansion call :int2float 5 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% :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