Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: "Richard Cooper" <spamandviruses@xxxxxx>
- Date: Mon, 10 Oct 2005 11:17:02 GMT
On Mon, 10 Oct 2005 01:39:52 -0400, ¬a\/b <al@xxx> wrote:
for me round in that stage is an error: there is no round,should be only a print_round at end
No, you round every time you truncate the precision of your numbers. What you don't do is reduce the precision of your numbers until the end of your calculation. However, since we're not free to keep an endless supply of digits in our numbers, we're frequently forced to truncate the precision of our numbers even when we're not at the end of our calculation.
For example, add and subtract don't give us any extra bits of precision, so we don't have to do any rounding when we do an add or subtract, and doing so would definately be a bad idea.
However, with multiply, we get extra precision behind the decimal point. For example, 0.7 times 0.7 gives us 0.49, but we can only have one decimal place, so we're forced to reduce the precision of our answer in order to fit it into the fixed point data type we're using.
Now, for example, if you know that you're following up that multiplication with a division, you would be far better off to not reduce the precision at all, since the first step of doing a division is increasing the precision of the number. So if the next step was to divide by 0.3, then instead of rounding 0.49 to 0.5, then increasing the precision again to 0.50 and dividing by 0.3, you'd be much better off to keep the more precise 0.49 and divide that by 0.3.
Keeping additional precision is exactly what floating point math is all about. The exponent field allows you to say how many bits of the matissa are the integer portion, and how many are the fractional portion, and so you can keep the extra precision and just modify the exponent to reflect that.
However, in fixed point math, we don't have an exponent field in the number. So we have to reduce the precision of 0.49, and the best way to do that is to round it to 0.5 rather than to simply truncate it to 0.4.
It's not the rounding that is bad, it's the loss of precision. It's just that people say "you should round until the end" because people usually round when they reduce precision, and they usually don't reduce precision until the end. Everyone uses calculators, and calculators do the precision reduction for them. When someone puts 2 / 3 into a calculator, and the calculator says the answer is 0.666666667, no one ever says that the calculator rounded the result, but that's what it did, because it had to reduce it's precision to 9 decimal places, and that's how that last 6 became a 7.
The rounding that you don't do until the end is when you're calculating a number to two decimal places, you don't round to two decimal places every step of the way, but instead just once at the end. However, if your calculator only has 9 decimal places, then of course you round to 9 decimal places every step of the way. That's what we're doing with the fixed point math. Numbers in "<< 8" format only have room for 2.4 decimal places, so we have to round every result along the way to the 2.4th decimal place.
With that said, my method for rounding division is clearly wrong:
10.0 / 2.8 -> 00001005 / 0028 -> 0035 = 3.5, actual answer 3.5714
The goal is to round that 3.5714 to 3.6, but since we never calculate the extra trailing digits, the only way to do it is to fix up the numerator before doing the divide so that the result will have an additional 1/2 of it's least signifigant bit added to it, so that when the division is done, we get the correct rounded answer. Adding 1/2 of the least signifigant bit doesn't do that, you have to add 1/2 of the denominator.
So the correct formula is this: (a << 8 + b >> 1) / b
Using that formula, you get these results:
10.0 / 2.8 -> 00001014 / 0028 -> 0036 = 3.6, actual answer 3.5714
I wrote a test program this time, which tested that formula with 100 million division problems, so I'm sure I got it right this time.
Of course, another way to do it is to use "(a << 8) / b", and then increment the result if the remainder is greater than "b >> 1", the result is exactly the same either way, however the "(a << 8 + b >> 1) / b" method avoids the need for a conditional jump.
round results are no good results too
Yes, but rounded results are closer to the correct answer than truncated results. With the "* 10" format numbers, it's the difference between each calculation having an error of +/- 0.05 if you round, or 0.05 +/- 0.05 if you don't round. The average error when rounding is 0.00, whereas the average error when truncating is -0.05.
.
- Follow-Ups:
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: randyhyde@xxxxxxxxxxxxx
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- References:
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: Richard Cooper
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: ¬a\\/b
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: Richard Cooper
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: ¬a\\/b
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: Richard Cooper
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: ¬a\\/b
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: Richard Cooper
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: ¬a\\/b
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: Richard Cooper
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- From: ¬a\\/b
- Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- Prev by Date: Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- Next by Date: Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- Previous by thread: Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- Next by thread: Re: Assembly Language - Mathematics WITHOUT maths coprocessor
- Index(es):
Relevant Pages
|