Batch operation returns "Divide by zero error" with the delayed expansion on and a valid operation

0

The following line of code is run trough my current project :

set /a "hp = hp - ((atk*arandom)/((edef*6)/edefbonus))"

And here's what it looks like when you replace the variables by their values :

set /a "hp = 50 - ((2*2)/((1*6)/8))"

The answer to this is 44.666667, which should be rounded up to 45. But instead Batch just tells me "Divide by zero error" when the code runs, both with the version that uses the variables and with the version that uses numbers instead.

I'm really lost for clues. I checked for uneven parenthesis, I checked for unset variables and EnabledDelayedExpansion is set. So what am I doing wrong...?

Mathys Oliveira

Posted 2016-04-19T15:31:34.397

Reputation: 61

Answers

2

Unless there's a reason for dividing the denominator by edfbonus instead of multiplying the numerator by it, you can avoid the zero divisor problem by rearranging the calculation:

set /a "hp = hp - ((atk*arandom*edefbonus)/(edef*6))"

The outer parentheses and the quotation marks are unnecessary, and set /a has an operator which saves some typing and tidies up expressions like this:

set /a hp -= atk*arandom*edefbonus/(edef*6)

Since you mentioned rounding, I'll add this:

Provided the value to the right of the -= operator above is always positive, the simplest way to do the rounding is:

set /a hp -= (10*atk*arandom*edefbonus/(edef*6)+5)/10

Notice that we multiply by 10 before the division, not after it, so the calculation goes:

10*2*2*8 =320
1*6      =6
320/6    =53
53+5     =58
58/10    =5

If we multiplied by 10 after the division, as in

set /a hp -= (atk*arandom*edefbonus/(edef*6)*10+5)/10,

the calculation would go:

2*2*8 =32
1*6   =6
32/6  =5   (we just lost the fraction digit, so rounding won't work)
5*10  =50
50+5  =55
55/10 =5

Same result, but only because the rounding would have rounded down anyway.

DickN

Posted 2016-04-19T15:31:34.397

Reputation: 46

1

Batch can't handle floating point numbers, only integers.

So ((1*6)/8) = 0 instead of 0.75. You then try to divide (2*2) by that 0, giving you a "Divide by zero" error.

Suggested solution? Use PowerShell instead:

PS Y:\> $hp = 50 - ((2*2)/((1*6)/8))
PS Y:\> $hp
44.6666666666667

Related SU question:

Related SO question:

Ƭᴇcʜιᴇ007

Posted 2016-04-19T15:31:34.397

Reputation: 103 763

1

Batch operation returns "Divide by zero error"

set /a "hp = 50 - ((2*2)/((1*6)/8))"

This is because 1*6 = 6 and 6/8 = 0.75.

And:

Any SET /A calculation that returns a fractional result will be rounded down to the nearest whole integer.

So 0.75 is rounded down to 0.

6/0 returns "Divide by zero error".


Workarounds

There are no real workarounds that allow floating point math, except using other scripting languages.

The only exception may be if you have a limited and fixed number of decimals (e.g. 2), then you can just multiply everything by 100.

To display a decimal delimiter in the end results, concatenate the integer divide by 100, followed by the decimal delimiter, followed by the modulo divide by 100:

SET Whole = Result / 100
SET "Fraction = Result %% 100"
SET Result=%Whole%.%Fraction%

This may break on the 32-bit limit, though.

In general, for floating point math I would recommend using other scripting languages.

Source Math in NT batch files


Arithmetic expressions (SET /a)

Placing expressions in "quotes" is optional for simple arithmetic but required for any expression using logical operators.

Any SET /A calculation that returns a fractional result will be rounded down to the nearest whole integer.

Source set


Further Reading

  • An A-Z Index of the Windows CMD command line - An excellent reference for all things Windows cmd line related.
  • set - Display, set, or remove CMD environment variables. Changes made with SET will remain only for the duration of the current CMD session.

DavidPostill

Posted 2016-04-19T15:31:34.397

Reputation: 118 938