简体   繁体   中英

Atmel studio assembly : Square root of an 8-bit number

I am working with ATmega328P in Atmel Studio and I have to make a function/algorithm, that takes an 8-bit number and calculates the square root of it. The result has to be given in two registers, one for the integer part and another for the decimal part.

I was thinking about this: the root has to be somewhere between the number given and 0 (or 1). So I would lsr (divide by 2) and it would check if it's greater, lesser or equal, and then try again with more precision, and so it would get more and more precise.

The problem is that the integer won't be correct and that I don't know how to put it to code, because I'm a new to assembly. I can also post what I made so far.

Thank you for any advice and help.

Just for completeness (or useless show of), here is how to do the task with whole numbers only, without multiplication, by linear search (which should be in most of the cases for 0-15 values still faster, than binary search with multiplication).

root = -1
square = 0
addValue = 1
while (square <= input) {
    square += addValue
    addValue += 2
    ++root
}
; here root == trunc(sqrt(input))

So, if you would not need the decimal part, this would be enough, or if you would be allowed to use at least 256B long LUT for decimal parts, together with this.

Writing proper decimal sqrt with 8 bit integer registers is actually quite some work, and I'm not going to take away all the fun from you. :P ..

Check various algorithms, and don't forget that limited range of decimals can be turned into integers by multiplication by some "base_exp" value.

Ie values from 0.00 to 15.99 can be turned into 11 bit integers by doing *100 (0-1599), and that sqrt of (100*100) is 100, so by input*10000 you will get values 0-2550000 (needs at least 22 bits, round it up to 24b), then doing integer square root of that will give you result *100 (and fits into 11 bits), so you can further split it into two values by dividing by 100.

This may look simple for human, but in real world when doing decimal calculations with 8/16b registers it was often done by this principle, but instead powers of two were used, ie *256*256, which can be done simply by shifting value 16 bits left. And division by 256 is shifting values by 8 to right.

So for that per digit method with binary numbers you will need to create 24 bit addition/subtraction/shifter pieces of code. Then the input number is top-most 8 bits of 24 bit number. (ie input value 10 => (10<<16) == 0x0A0000 ) => simple shifting. Calculate sqrt of that (0x0329 or 0x032A, depends if you manage to do just truncating, or rounding at the last bit), and that's it, the result will surely fit into 12 bits, and upper 4 are the whole part 0-15, the lower 8 bits is decimals in "amount of 1/256" value (0x29/256 = 0.16015625). Which can be again split by simple shifting/anding, ie no mul/div operation needed.

So it will be still lot of work, but it's reasonable (doing 24 bit multiplication/division on 8b CPU is lot more pain, than doing add/sub/shifting extension). And explain why you opted for 4:8 fixed-point format of result, to use full 8 bits of precision from the decimal part of result and make it simpler for further binary calculations. (in 8:8 fixed point 0.5 = 0x0080 ... try to add that and see what happens: 0x0080 + 0x0080 = 0x0100 = 1.0 without any complex tampering with result .. that's how we did low-precision decimal calculations on 8 bit CPUs, for example for sin/cos effects, also multiplication/division is simpler, again for 0.5: 0x0080 * 0x0080 = (0x4000>>8) = 0x0040 = 0.25).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM