Integer arithmetic. Divide with rounding result. Part 1

The simpler, at first glance, the task, the less the developer thinks about how to correctly implement it, and, at best, he makes a mistake late, at all he does not notice at all. We will discuss one of these problems, namely, the fractional division and scaling in controllers that support only integer arithmetic.

Why the subtleties of computation in the conditions of such arithmetic application developers do not pay attention, the question. I would venture only to assume that, in all likelihood, the habit of making calculations on a calculator affects ... In any case, with enviable regularity, “I have the happiness” to contemplate how colleagues in the shop step on the same rake. This material is aimed at ensuring that the same “rake” is neutralized.

In integer arithmetic, the result of dividing one integer by another consists of two numbers — the particular and the remainder. If the remainder of the division is discarded, we get the result, in absolute value rounded to a smaller integer.

Realizing calculations with fractions, this moment is often overlooked, and, having missed, they get losses in the accuracy of calculations. Moreover, the accuracy of calculations decreases with increasing magnitude of the divider. For example, that 53/13, that 64/13 will result in 4, although, in fact, the quotient of the second fraction is much closer to 5.
In fact, rounding up the result to the nearest integer is elementary. To do this, it is enough to double the remainder of the division, summing it with itself, and then again divide it by the same number that it was originally divided, and add the quotient from this division to the quotient obtained from the initial division operation.
In the first simple example, I will demonstrate how such rounding is implemented programmatically using the example of calculating the ratio of two quantities

Y=k=A/B


Taking into account that such calculations in the program may be required more than once, the calculation algorithm is implemented in a format suitable for packaging into a subroutine.

To correctly perform the intermediate calculations required for this, we will need an array of five registers; we denote it conditionally as TEMP [0..4]. Why five and no less, I will explain a little lower.

Algorithm of actions:

1. TEMP[2]= A 2. TEMP[3]= B ----- 3. TEMP[0,1]= TEMP[2]/TEMP[3] 4. TEMP[1,2]= TEMP[1]*2 5. TEMP[4]= 0 6. TEMP[1..4]= TEMP[1,2]/TEMP[3,4] 7. TEMP[0]= TEMP[0]+TEMP[1] ----- 8. Y= TEMP[0] 

Steps from the 3rd to the 7th can be made into a subroutine.

If desired, the result can be recorded directly by summing TEMP [0] with TEMP [1] outside the calculation subroutine. This is not fundamental. The only thing that should be borne in mind is that with a multitude of calculations of the same type, taking out an addition operation into the main body of a program can lead to an increase in the amount of program memory used by it.

So why did intermediate registers require as many as 5 registers? And the operation of summing up the remainder of dividing oneself with oneself, as mentioned earlier, is replaced by multiplying the remainder by two? Very simple - in order to operate with an unlimited set of integers.

Let me explain: if you divide, for example, the number 32767 by -32768, we get 32767 in the remainder, and the result of its addition will undoubtedly go beyond the integer range.
That is, the doubled remainder of the integer division of a fraction in the interests of rounding the result of such a division should always be presented in double integer format.
To be continued...

Source: https://habr.com/ru/post/412613/


All Articles