Calculate With Decimal Numbers - Mikrotik Script RouterOS
This is a script to do any math operation (+, -, *, and /) with decimal numbers and/or integers in RouterOS. Make sure to copy the text below EXACTLY and paste into a blank Mikrotik script's source. There are no command dependencies, just scripting syntax.
To use it, it's simply a matter of setting the decimals and decimalop global variables from another script or the console.
3.x version
--Doug 06:26, 27 July 2009 (EEST) Update: internally remove leading zeros when calculating
# Performs operations on decimal numbers
# This script can be run from command line or other script.
# To run, set global variables then run this script
#
# Global variables (input):
# decimalop = math operator ( +, -, *, or / )
# decimalplaces = decimal point places (0 = no decimal point)
# decimals = string of decimal numbers seperated by spaces (non-negative (-))
#
# Global variables (output):
# decimalresult = result of calculation, "err" if error
#
# Example:
# add 3 numbers (4.32, 28 and 0.12):
# :global decimalop "+"
# :global decimals "4.32 28 0.21"
# /system script run
# :put $decimalresut
# ### Math Operations ###
# Do not change this part
:global decimals
:global decimalop
:global decimalplaces
:global decimalresult "err"
:local err 0
:local result ""
:local divprecision "10"
:local decimalsarray ""
# Sanity checks
:if ([:len $decimalplaces] = 0) do={:set decimalplaces 4} else={
:for cindex from=0 to=([:len $decimalplaces] - 1) do={
:local char [:pick $decimalplaces $cindex ($cindex + 1)]
:if ($char != "0" && $char != "1" && $char != "2" && $char != "3" && $char != "4" && \
$char != "5" && $char != "6" && $char != "7" && $char != "8" && $char != "9") do={
:set err ($err + 1)
}
}
}
:if ($decimalop != "+" && $decimalop != "-" && $decimalop != "*" && $decimalop != "/") do={
:set err ($err + 1)
}
# Check decimals input and convert decimals to array for easier use
:for d from=0 to=([:len $decimals] - 1) do={
:local char [:pick $decimals $d ($d + 1)]
:if ($char = " ") do={
:set decimalsarray ($decimalsarray . ",")
} else={
:if ($char = "." || $char = "0" || $char = "1" || $char = "2" || $char = "3" || $char = "4" || \
$char = "5" || $char = "6" || $char = "7" || $char = "8" || $char = "9" ) do={
:set decimalsarray ($decimalsarray . $char)
} else={:set err ($err + 1)}
}
}
:set decimalsarray [:toarray $decimalsarray]
:foreach d in=$decimalsarray do={
:local dps 0
:for cindex from=0 to=([:len $d] - 1) do={
:if ([:pick $d $cindex ($cindex + 1)] = ".") do={:set dps ($dps + 1)}
}
:if ($dps > 1) do={:set err ($err + 1)}
}
# Only preform calculation if no error
:if ($err = 0) do={
# Preform calculation from first to last number
:local cloopindex 0
:local decimallen 0
:local curdeclen 0
:local tmpnum ""
# get decimal length for use after calculation
:foreach d in=$decimalsarray do={
:local decfound 0
:for cindex from=0 to=([:len $d] - 1) do={
:if ([:pick $d $cindex ($cindex + 1)] = ".") do={
:set curdeclen ([:len $d] - ($cindex + 1))
:if ($decimalop = "+") do={:if ($curdeclen > $decimallen) do={:set decimallen $curdeclen}}
:if ($decimalop = "-") do={:if ($curdeclen > $decimallen) do={:set decimallen $curdeclen}}
:if ($decimalop = "*") do={:set decimallen ($decimallen + $curdeclen)}
:set decfound 1
}
}
:if ($decfound = 0) do={
:if ($decimalop = "+") do={:if ($decimallen < 1) do={:set decimallen 1}}
:if ($decimalop = "-") do={:if ($decimallen < 1) do={:set decimallen 1}}
:if ($decimalop = "*") do={:set decimallen ($decimallen + 1)}
}
}
# Loop through decimals from start to finish
:foreach d in=$decimalsarray do={
:local decfound 0
:for cindex from=0 to=([:len $d] - 1) do={
:if ([:pick $d $cindex ($cindex + 1)] = ".") do={
:set decfound 1
:set curdeclen [:len [:pick $d ($cindex + 1) [:len $d]]]
:set tmpnum ([:pick $d 0 $cindex] . [:pick $d ($cindex + 1) [:len $d]])
}
}
:if ($decfound = 0) do={
:set curdeclen 1
:set tmpnum ($d . "0")
}
# Strip leading zeros
:local zeroindex ([:len $tmpnum] - 1)
:for cindex from=([:len $tmpnum] - 1) to=0 do={
:if ($cindex >= 0 && [:pick $tmpnum $cindex ($cindex + 1)] != "0") do={
:set zeroindex $cindex
}
}
:set tmpnum [:pick $tmpnum $zeroindex [:len $tmpnum]]
# Add and Subtract calculation
:if ($decimalop = "+" || $decimalop = "-") do={
# add zeros to decimal
:if ($curdeclen < $decimallen) do={
:for cindex from=$curdeclen to=($decimallen - 1) do={
:set tmpnum ($tmpnum . "0")
}
}
:if ($decimalop = "+") do={
:if ($decimallen > 0) do={
:if ($cloopindex = 0) do={:set result $tmpnum} \
else={:set result ($result + $tmpnum) }
}
}
:if ($decimalop = "-") do={
:if ($decimallen > 0) do={
:if ($cloopindex = 0) do={:set result $tmpnum} \
else={:set result ($result - $tmpnum) }
}
}
# end add and subtract
}
# Multiply calculation
:if ($decimalop = "*") do={
:if ($decimallen > 0) do={
:if ($cloopindex = 0) do={:set result $tmpnum} \
else={:set result ($result * $tmpnum)}
}
# end multiply
}
# Divide calculation
:if ($decimalop = "/") do={
:if ($cloopindex = 0) do={:set result $d} else={
# calculate decimal point position and strip decimal point from result
:local resultdecpos 0
:local resultlen 0
:local tmpresult ""
# Strip out decimal point
:for cindex from=0 to=([:len $result] - 1) do={
:if ([:pick $result $cindex ($cindex + 1)] = ".") do={
:set resultdecpos $cindex
} else={:set tmpresult ($tmpresult . [:pick $result $cindex ($cindex + 1)])}
}
:set result $tmpresult
:set resultlen [:len $result]
:if ($curdeclen > ([:len $result] - $resultdecpos)) do={
:set resultdecpos 0
# add zeros to result
:for r from=([:len $result] - $resultdecpos) to=$curdeclen do={
:set result ($result . "0")
}
} else={
:set resultdecpos ($resultdecpos + $curdeclen)
}
# Strip leading zeros
:local zeroindex ([:len $result] - 1)
:for cindex from=([:len $result] - 1) to=0 do={
:if ($cindex >= 0 && [:pick $result $cindex ($cindex + 1)] != "0") do={
:set zeroindex $cindex
}
}
:set result [:pick $result $zeroindex [:len $result]]
:local decimal ""
:local dividend $result
:local divisor $tmpnum
:local quotient ($dividend / $divisor)
:local remainder ($dividend - ($divisor * $quotient))
:if ($remainder > 0) do={
:local tmpremainder ($remainder . "0")
:for x from=1 to=$divprecision do={
:local tmpdecimal ($tmpremainder / $divisor)
:set decimal ($decimal . $tmpdecimal)
:set tmpremainder (($tmpremainder - ($tmpdecimal * $divisor)) . "0")
}
} else={:set decimal "0"}
:set result ""
# shift decimal point left
:if (($resultlen - $resultdecpos) > 0) do={
:if ([:len $quotient] < ($resultlen - $resultdecpos)) do={
:for cindex from=[:len $quotient] to=($resultlen - $resultdecpos - 1) do={
:set result ("0" . $result)
}
:set result ("0." . $result . $quotient)
:set resultdecpos 1
} else={
:for cindex from=0 to=([:len $quotient]) do={
:if ($cindex = ([:len $quotient] - ($resultlen - $resultdecpos))) do={
:if ($cindex = 0) do={
:set result "0."
:set resultdecpos 1
} else={
:set result ($result . ".")
:set resultdecpos $cindex
}
}
:set result ($result . [:pick $quotient $cindex ($cindex + 1)])
}
}
:set result ($result . $decimal)
# :set resultdecpos [:len $result]
} else={:set result ($quotient . "." . $decimal); :set resultdecpos [:len $quotient]}
:set decimallen ([:len $result] - $resultdecpos - 1)
# if end of array, strip decimal point
:if ($cloopindex = ([:len $decimalsarray] - 1)) do={
:local tmpresult ""
:for cindex from=0 to=([:len $result] - 1) do={
:if ([:pick $result $cindex ($cindex + 1)] != ".") do={
:set tmpresult ($tmpresult . [:pick $result $cindex ($cindex + 1)])
}
}
:set result $tmpresult
}
# end cloopindex
}
# end divide
}
:set cloopindex ($cloopindex + 1)
# end d for
}
# Format result, insert decimal point
:local newresult ""
:if (([:len $result] - $decimallen) < 0) do={
:set newresult "0"
:for r from=[:len $result] to=($decimallen - 1) do={
:set newresult ($newresult . "0")
}
:set result ($newresult . $result)
}
:set newresult ""
:for r from=0 to=(([:len $result] - $decimallen - 1) + $decimalplaces) do={
:if ($r = ([:len $result] - $decimallen)) do={
:if ($r = 0) do={:set newresult ($newresult . "0.")}
:if ($r > 0) do={
:if ([:pick $result ($r - 1) $r] = "-") do={:set newresult ($newresult . "0")}
:set newresult ($newresult . ".")
}
}
:if ($r >= [:len $result]) do={:set newresult ($newresult . "0")} else={
:set newresult ($newresult . [:pick $result $r ($r + 1)])
}
}
:if ([:len $newresult] > 0) do={:set result $newresult}
#end err check
}
# If no errors occurred, set decimalresult to result of calculation
:if ($err = 0) do={:set decimalresult $result}
# ### End of Math Operations ###