# Calculate with decimal numbers

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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 <this script>
#        :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 ###

```