Thread: [Assembly] Binary to decimal conversion for 64bit> numbers

1. [Assembly] Binary to decimal conversion for 64bit> numbers

Say you have a value (&#37;rsp):8(%rsp) how would you go about printing this number to stdout in decimal form?

One method would be dividing it by 10 of course recursively but I cant figure out how to apply a divide operation across a 128bit number (using 64bit registers). To test this I have a little program that's doubling the value 2^64 - 2 (18446744073709551614) hens the value when doubled is 2 ^ 65 - 4 (36893488147419103228) and would be stored
(%rsp)
00000000000000000000000000000000000000000000000000 00000000000001
8(%rsp)
11111111111111111111111111111111111111111111111111 11111111111100
Code:
```.section	.data
.text
.global main

.type	main, @function
main:
pushq	%rbp
movq	%rsp, %rbp
subq	\$8, %rsp

movq	\$18446744073709551614, %r13	#our number
#	movq	%r13, (%rsp)

jnc	nocarry
movq	\$1, %rbx
movq	%rbx, (%rsp)

nocarry:
movq	%r13, (%rsp)

###Wanting to print number in decimal, I can output the binary by a recursive div2 operation.  Here the value is at -8(%rsp):(%rsp).

movl	\$0, %eax
leave
ret```
Converting from decimal to ascii is no problem, and in fact I think the best way to do this is to use printf. Essentially the number has to be converted so 8(%rsp) represents the first 64 decimal bits of the number and (%rsp) would have the binary representation for the most significant decimal part. Eg the number is 36893488147419103228 so after the conversion 8(%rsp) would contain 6893488147419103228 (in binary) and (%rsp) would contain 3.

So this....
(%rsp)
00000000000000000000000000000000000000000000000000 00000000000001
8(%rsp)
11111111111111111111111111111111111111111111111111 11111111111100

Gets converted to this.... (%rsp doesn't necessarily have to refer to the same location)
(%rsp)
00000000000000000000000000000000000000000000000000 00000000000011
8(%rsp)
01011111101010101001011011110010011000100100011111 11111111111100

This would allow two printf statements to be executed. There are alternative methods, of course.

edit -- Btw this program is linked with g++ so `as ./file.s -o file.o && g++ file.o -o file` is how you assembly / link it.

2. Passing from a representation in base 2 to a representation in base 10 isn't all that trivial with computers (as you can see). Normally, if you don't care much about efficiency, you would divide successively by 10 (or some power of 10) the number until it equals 0. Which, if you have number spanning more than 1 "word" (in our case, with the x86-64 ISA, a word is 64-bit wide), can mean you'll have to implement an algorithm for dividing such numbers. In fact, it will get dirty quite fast; you should be able to find the representation in base 10 of some 2-words long numbers (like the one in your example) using only the DIV instruction and some well chosen power of 10 divider, else you'll have to go for the big guns.

So, I would advise you to use a "math" library, like gmp. It's quite easy to use, yet powerful (and quite efficient). The input and output functions on integers are especially what you are looking for. In fact, you would also want to take a look at the mpz_set_str() function.

On a side note, your assembly code seems a bit broken. Be careful on how you handle the stack.

3. What's wrong with the code? Just curious. You do know leave is the same as
Code:
```movq    &#37;rbp, %rsp
popq    %rbp```
And yes I do know there are math libraries for this. If this were C I'd probably be using it. I wrote a very inefficient one (floating point) a few months ago and recently in learning 64bit assembly decided to look at this just for the technical aspect. You're probably right though; it's probably best done by division.

4. Code:
```main:
pushq	%rbp
movq	%rsp, %rbp
subq	\$8, %rsp

movq	\$18446744073709551614, %r13	#our number

jnc	nocarry    // This branch will never be taken
movq	\$1, %rbx
movq	%rbx, (%rsp)
addq	\$8, %rsp   // After this instruction, %rsp == %rbp

nocarry:
movq	%r13, (%rsp)  // Equivalent to "movq %r13, (%rbp)" -- you just trashed (%rbp), but since we are in the "main" function, I'm not sure of the consequences

movl	\$0, %eax
leave```
Don't forget that the PUSH instruction on the x86-64 ISA is "Decrements the stack pointer and then stores the source operand on the top of the stack". So when you write "subq \$8, %rsp", you are creating 8 bytes of stack space, which is not enough to hold your 16 bytes value. Personnaly, I would do it this way

Code:
```main:
pushq	%rbp
movq	%rsp, %rbp
subq	\$16, %rsp

movq	\$18446744073709551614, %r13	#our number