Advertisement
hxrussia

Arbitrary-precision arithmetic in Windows CMD/Batch

Dec 25th, 2014
191
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Batch 8.14 KB | None | 0 0
  1.     @echo off
  2.     setlocal enabledelayedexpansion
  3.    
  4.     set base=10
  5.     set /a half_base = base / 2
  6.     set nan_value=NaN
  7.    
  8.     set action=%~1
  9.     if "%action%"=="/?" goto :show_help
  10.     if "%action%"=="" goto :show_help
  11.     echo "add" "sub" "mul" "div" "mod" "sqrt" | findstr ""%action%"" >nul || goto :show_help
  12.    
  13.     set a=%~2
  14.     set b=%~3
  15.     if "!a!"=="" goto :show_help
  16.     call :check_number "!a!"
  17.     if not "!res!"=="1" goto :show_format_error
  18.     call :from_string "!a!"
  19.     set a=!res!
  20.     if not "%action%"=="sqrt" (
  21.         if "!b!"=="" goto :show_help
  22.         call :check_number "!b!"
  23.         if not "!res!"=="1" goto :show_format_error
  24.         set b=!b!
  25.         call :from_string "!b!"
  26.         set b=!res!
  27.     )    
  28.    
  29.     call :%~1 "%a%" "%b%"
  30.     call :to_string "%res%"
  31.     echo Result: %res%
  32.    
  33.     goto :eof
  34.    
  35.    
  36. :show_format_error
  37.     echo Error: Only unsigned integer numbers are supported
  38.     goto :eof
  39.    
  40.  
  41. :show_help
  42.     echo Calculate result of an operation using long arithmetic
  43.     echo]
  44.     echo %0 add A B
  45.     echo %0 sub A B
  46.     echo %0 mul A B
  47.     echo %0 div A B
  48.     echo %0 mod A B
  49.     echo %0 sqrt NUMBER
  50.     echo]
  51.     echo Only unsigned integer numbers are supported.
  52.     echo Non-integer results will be rounded down.
  53.     goto :eof
  54.  
  55.    
  56. :show_wait
  57.     echo | set /p=Please wait.
  58.     goto :eof
  59.    
  60.    
  61. :show_progress
  62.     echo | set /p=.
  63.     goto :eof
  64.    
  65.    
  66. :check_number
  67.     set res=1
  68.     echo %~1| findstr /r "^[0-9][0-9]*$">nul || set res=0
  69.     goto :eof
  70.  
  71.    
  72. :reverse
  73.     setlocal enabledelayedexpansion
  74.     set str=%~1
  75.     if "%str%"=="" (
  76.         set res=
  77.         goto :reverse_return
  78.     )
  79.     if "%str%"=="%nan_value%" (
  80.         set res=%nan_value%
  81.         goto :reverse_return
  82.     )
  83.     set index=0
  84.     set res=
  85. :reverse_loop
  86.     set ch=!str:~%index%,1!
  87.     if "%ch%"=="" goto :reverse_return
  88.     set res=%ch%%res%
  89.     set /a index += 1
  90.     goto :reverse_loop
  91. :reverse_return
  92.     endlocal && set res=%res%
  93.     goto :eof
  94.  
  95.    
  96. :from_string
  97.     if "%~1"=="0" (
  98.         set res=
  99.     ) else (
  100.         call :reverse "%~1"
  101.     )
  102.     goto :eof
  103.  
  104.    
  105. :to_string
  106.     if "%~1"=="%nan_value%" (
  107.         set res=%~1
  108.         goto :eof
  109.     )
  110.     if "%~1"=="" (
  111.         set res=0
  112.     ) else (
  113.         call :reverse "%~1"
  114.     )
  115.     goto :eof
  116.    
  117.    
  118. :skip_zeros
  119.     setlocal
  120.     set number=%~1
  121.     if "%number%"=="" (
  122.         set res=
  123.         goto :skip_zeros_return
  124.     )
  125.     set last_non_zero=-1
  126.     set index=0
  127. :skip_zeros_loop
  128.     set digit=!number:~%index%,1!
  129.     if "%digit%"=="" goto :skip_zeros_cut
  130.     if not "%digit%"=="0" set last_non_zero=%index%
  131.     set /a index += 1
  132.     goto :skip_zeros_loop
  133. :skip_zeros_cut
  134.     set /a length = last_non_zero + 1
  135.     set res=!number:~0,%length%!
  136. :skip_zeros_return
  137.     endlocal && set res=%res%
  138.     goto :eof
  139.    
  140.    
  141. :add
  142.     setlocal
  143.     set a=%~1
  144.     set b=%~2
  145.     if "%a%"=="" (
  146.         set res=%b%
  147.         goto :add_return
  148.     )
  149.     if "%b%"=="" (
  150.         set res=%a%
  151.         goto :add_return
  152.     )
  153.     set res=
  154.     set carry=0
  155.     set index=0
  156. :add_loop
  157.     set a_digit=!a:~%index%,1!
  158.     set b_digit=!b:~%index%,1!
  159.     if "%a_digit%"=="" if "%b_digit%"=="" if %carry%==0 goto :add_return
  160.     if "%a_digit%"=="" set a_digit=0
  161.     if "%b_digit%"=="" set b_digit=0
  162.     set /a res_digit = a_digit + b_digit + carry
  163.     if %res_digit% geq %base% (
  164.         set carry=1
  165.         set /a res_digit -= base
  166.     ) else (
  167.         set carry=0
  168.     )
  169.     set res=%res%%res_digit%
  170.     set /a index += 1
  171.     goto :add_loop
  172. :add_return
  173.     endlocal && set res=%res%
  174.     goto :eof
  175.    
  176.    
  177. :sub
  178.     setlocal
  179.     set a=%~1
  180.     set b=%~2
  181.     if "%b%"=="" (
  182.         set res=%a%
  183.         goto :sub_return
  184.     )
  185.     if "%a%"=="" (
  186.         set res=%nan_value%
  187.         goto :sub_return
  188.     )
  189.     set res=
  190.     set carry=0
  191.     set index=0
  192. :sub_loop
  193.     set a_digit=!a:~%index%,1!
  194.     set b_digit=!b:~%index%,1!
  195.     if "%a_digit%"=="" (
  196.         if %carry% neq 0 set res=%nan_value%
  197.         if not "%b_digit%"=="" set res=%nan_value%
  198.         goto :sub_skip_zeros
  199.     )
  200.     if "%b_digit%"=="" set b_digit=0
  201.     set /a res_digit = a_digit - b_digit - carry
  202.     if %res_digit% lss 0 (
  203.         set carry=1
  204.         set /a res_digit += base
  205.     ) else (
  206.         set carry=0
  207.     )
  208.     set res=%res%%res_digit%
  209.     set /a index += 1
  210.     goto :sub_loop
  211. :sub_skip_zeros
  212.     call :skip_zeros "%res%"
  213. :sub_return
  214.     endlocal && set res=%res%
  215.     goto :eof
  216.    
  217.    
  218. :mul
  219.     setlocal
  220.     set a=%~1
  221.     set b=%~2
  222.     set res=
  223.     if "%a%"=="" goto :mul_return
  224.     if "%b%"=="" goto :mul_return
  225.     set carry=0
  226.     set i=0
  227. :mul_loop_i
  228.     set a_digit=!a:~%i%,1!
  229.     if "%a_digit%"=="" goto :mul_return
  230.     set j=0
  231. :mul_loop_j
  232.     set b_digit=!b:~%j%,1!
  233.     if "%b_digit%"=="" if %carry%==0 goto :mool_loop_i_continue
  234.     if "%b_digit%"=="" set b_digit=0
  235.     set /a k = i + j
  236.     set /a k_next = k + 1
  237.     if "%res%" == "" (
  238.         set res=0
  239.         set prev_digit=0
  240.     ) else (
  241.         set prev_digit=!res:~%k%,1!
  242.         if "!prev_digit!" == "" (
  243.             set res=!res!0
  244.             set prev_digit=0
  245.         )
  246.     )
  247.     set /a res_digit = prev_digit + a_digit * b_digit + carry
  248.     set /a carry = res_digit / base
  249.     set /a res_digit %%= base
  250.     set res=!res:~0,%k%!%res_digit%!res:~%k_next%!
  251.     set /a j += 1
  252.     goto :mul_loop_j
  253. :mool_loop_i_continue
  254.     set /a i += 1
  255.     goto :mul_loop_i
  256. :mul_return
  257.     endlocal && set res=%res%
  258.     goto :eof
  259.    
  260.    
  261. :div2
  262.     setlocal
  263.     set number=%~1
  264.     if "%number%"=="" (
  265.         set res=
  266.         goto :div2_return
  267.     )
  268.     call :reverse "%number%"
  269.     set number=%res%
  270.     set res=
  271.     set carry=0
  272.     set index=0
  273. :div2_loop
  274.     set digit=!number:~%index%,1!
  275.     if "%digit%"=="" goto :div2_skip_zeros
  276.     set /a next_carry = digit & 1
  277.     set /a digit /= 2
  278.     if %carry%==1 set /a digit += half_base
  279.     set carry=%next_carry%
  280.     set res=%digit%%res%
  281.     set /a index += 1
  282.     goto :div2_loop
  283. :div2_skip_zeros
  284.     call :skip_zeros "%res%"
  285. :div2_return
  286.     endlocal && set res=%res%
  287.     goto :eof
  288.    
  289.    
  290. :div
  291.     setlocal
  292.     set a=%~1
  293.     set b=%~2
  294.     if "%b%"=="" (
  295.         set res=%nan_value%
  296.         goto :div_return
  297.     )
  298.     if "%a%"=="" (
  299.         set res=
  300.         goto :div_return
  301.     )
  302.     set l=
  303.     call :add "%a%" 1
  304.     set r=%res%
  305.     call :show_wait
  306. :div_binsearch_loop
  307.     call :sub "%l%" "%r%"
  308.     if not "%res%"=="%nan_value%" (
  309.         call :sub "!l!" 1
  310.         goto :div_return
  311.     )
  312.    
  313.     call :add "%l%" "%r%"
  314.     call :div2 "%res%"
  315.     set middle=%res%
  316.     call :show_progress
  317.    
  318.     call :mul "%middle%" "%b%"
  319.     call :sub "%res%" "%a%"
  320.     set need_change_l=0
  321.     if "%res%"=="%nan_value%" set need_change_l=1
  322.     if "%res%"=="" set need_change_l=1
  323.     if %need_change_l%==1 (
  324.         call :add "!middle!" 1
  325.         set l=!res!
  326.     ) else (
  327.         set r=!middle!
  328.     )
  329.     goto :div_binsearch_loop
  330. :div_return
  331.     echo]
  332.     endlocal && set res=%res%
  333.     goto :eof
  334.    
  335.    
  336. :mod
  337.     setlocal
  338.     set a=%~1
  339.     set b=%~2
  340.     call :div "%a%" "%b%"
  341.     call :mul "%res%" "%b%"
  342.     call :sub "%a%" "%res%"
  343.     endlocal & set res=%res%
  344.     goto :eof
  345.    
  346.    
  347. :sqrt
  348.     setlocal
  349.     set number=%~1
  350.     if "%number%"=="" (
  351.         set res=
  352.         goto :sqrt_return
  353.     )
  354.     set l=
  355.     call :add "%number%" 1
  356.     set r=%res%
  357.     call :show_wait
  358. :sqrt_binsearch_loop
  359.     call :sub "%l%" "%r%"
  360.     if not "%res%"=="%nan_value%" (
  361.         call :sub "!l!" 1
  362.         goto :sqrt_return
  363.     )
  364.    
  365.     call :add "%l%" "%r%"
  366.     call :div2 "%res%"
  367.     set middle=%res%
  368.     call :show_progress
  369.    
  370.     call :mul "%middle%" "%middle%"
  371.     call :sub "%res%" "%number%"
  372.     set need_change_l=0
  373.     if "%res%"=="%nan_value%" set need_change_l=1
  374.     if "%res%"=="" set need_change_l=1
  375.     if %need_change_l%==1 (
  376.         call :add "!middle!" 1
  377.         set l=!res!
  378.     ) else (
  379.         set r=!middle!
  380.     )
  381.     goto :sqrt_binsearch_loop
  382. :sqrt_return
  383.     echo]
  384.     endlocal && set res=%res%
  385.     goto :eof
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement