I am a new assembly programer and I could not succeed in finding how many digits a number has.My purpose is to find factorials. I program in an emulator of assembly 8086.
Asked
Active
Viewed 2,142 times
4
-
1is there an instruction that can do a base10 logarithm of the number? – Lefteris E Apr 21 '13 at 20:09
-
Take a look at this for the theory: http://stackoverflow.com/q/6655754/1353098. In practice I don't think there is an instruction for log10. Do you have access to any libraries that might expose this function (like a c library, or a maths library) – Will Apr 21 '13 at 20:14
-
The x87 might come in handy here though. Logs are things that floating point processors are more likely to have. I reckon that `FYL2X` might be the instruction for you. – Will Apr 21 '13 at 20:19
-
3Sorry could you elaborate on what you mean by "find factorials"? I'm not sure how finding the number of digits is related. – someguy Apr 21 '13 at 20:22
-
3Just divide by 10 until you get to zero. Count the times. – stdcall Apr 21 '13 at 20:40
-
How many bits does an "emulator of assembly 8086" have? The `mul` instruction, with a 16-bit operand, produces a 32-bit result. 21! overflows 64 bits. How high do you need to go? – Frank Kotler Apr 21 '13 at 21:11
-
@someguy, it helps me to print the number. – TJR Apr 22 '13 at 06:50
1 Answers
2
The most efficient way to perform this operation is to use the bsr
instruction (see this slides, 20 to 25).
This should make a code like this:
.text
.globl main
.type main, @function
main:
movl $1024, %eax ;; pushing the integer (1024) to analyze
bsrl %eax, %eax ;; bit scan reverse (give the smallest non zero index)
inc %eax ;; taking the 0th index into account
But, I guess you need the base 10 log and not the base 2... So, here would be the code:
.text
.globl main
.type main, @function
main:
movl $1024, %eax ;; pushing the integer (1024) to analyze
bsrl %eax, %eax ;; bit scan reverse (give the smallest non zero index)
inc %eax ;; taking the 0th index into account
pushl %eax ;; saving the previous result on the stack
fildl (%esp) ;; loading the previous result to the FPU stack (st(0))
fldlg2 ;; loading log10(2) on the FPU stack
fmulp %st, %st(1) ;; multiplying %st(0) and %st(1) and storing result in %st(0)
;; We need to set the FPU control word to 'round-up' (and not 'round-down')
fstcw -2(%esp) ;; saving the old FPU control word
movw -2(%esp), %ax ;; storing the FPU control word in %ax
andw $0xf3ff, %ax ;; removing everything else
orw $0x0800, %ax ;; setting the proper bit to '1'
movw %ax, -4(%esp) ;; getting the value back to memory
fldcw -4(%esp) ;; setting the FPU control word to the proper value
frndint ;; rounding-up
fldcw -2(%esp) ;; restoring the old FPU control word
fistpl (%esp) ;; loading the final result to the stack
popl %eax ;; setting the return value to be our result
leave
ret
I am curious to know if somebody can find better than that ! Indeed, using SSE instructions might help.

perror
- 7,071
- 16
- 58
- 85
-
Worth mentioning that BSR requires 386, so it wouldn't be usable for future readers who were stuck with the 8086 restriction mentioned in the question. (Some schools teach asm using emu8086, unfortunately.) Knowing the number of significant base-2 digits gets you within 1 of the right number of base-10 digits, so you can make a lookup table of base-10 digits and a compare threshold to increment that by 1 or not. – Peter Cordes Apr 17 '21 at 07:40
-
FP tricks: instead of changing the rounding mode, you could maybe subtract 32 or something to make it negative, then convert (with truncation) to integer, e.g. SSE `cvttss2si eax, xmm0`, or SSE3 `fisttp` and undo the bias. You can test it for all 32 or 64 possible BSR results; if necessary tweak the log10(2) constant up or down by 1ulp if the rounding error is in the wrong direction and pushing it up past the next integer when it shouldn't be. (With SSE, you'd have to define your own log10_2 constant in memory anyway.) – Peter Cordes Apr 17 '21 at 07:48