1

What I am able to learn from YouTube videos are the single digit multiplication or division.

But what about if I wish to calculate more than a single digit with decimal points?

For example

Input1 : 123

Input2 : 320

Calculation = Input1 x Input1 / Input2

Answer will be 47.28 (Actual answer is 47.278125 but convert to 2 decimal points)

Anyone can provide examples would be nice. Thanks!

Zorev Gnoz
  • 221
  • 4
  • 20

2 Answers2

1

Use the Floating Point Unit (FPU).
This is the essence of your calculation:

num1    dd 123
num2    dd 320
hundred dd 100
result  dd 0
...
fild  dword [num1]    ; [st0] 123
fmul  st0             ; [st0] 123 x 123 == 15129
fidiv dword [num2]    ; [st0] (123 x 123) / 320 == 47.278125
fimul dword [hundred] ; [st0] ((123 x 123) / 320) * 100 == 4727.8125
fistp dword [result]  ; floating point stack is empty

The fimul dword [hundred] instruction is there so as to not loose the 2-digit fraction when the value gets stored back in main memory. The fistp dword [result] instruction will round the value to the nearest integer.

result now contains 4728 that you can divide by 100 to obtain the integer part and the 2-digit fraction:

mov eax, [result]
xor edx, edx
div dword [hundred]   ; -> Quotient EAX == 47, Remainder EDX == 28

Process these further as required.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • Since this is 64-bit code, the standard way to do FP math is with SSE2. That would need a few more instructions for explicit conversion from int to FP. (Or provide your inputs as double, or at least a `100.0` constant). SSE doesn't have instructions like `fidiv` that convert *and* divide by the conversion result; that's unique to x87. But you can convert from double directly into EAX, not needing memory space for `result` . – Peter Cordes Dec 03 '20 at 23:01
1

One approach for handling fractional values is by using a fixed-point representation, which can be useful in various situations. E.g., neural networks can utilize a fixed-point representation alongside weight quantization to improve prediction speed and reduce storage requirements.

In fixed-point, numbers are represented by an integer and a scaling factor. For example, 2.020 can be represented by integer 2020 and scaling factor 1/1000. Using scaling factors that are powers of two is computationally convenient, since bit-shifts can be used for rescaling. For some operations, like addition and subtraction, the resulting scaling factor matches the scaling factor of the operands. For multiplication, the scaling factors multiply, which could be rescaled using a subsequent division (or bit-shift). For division, the scaling factors divide out, which can be accounted for with a preceding scaling of one of the operands.

For example, here's an implementation in x86-64 assembly for the example in the question, which results in 47 in rax and 28 in rbx. I believe that division (and right bit-shifts) used as-is can introduce a bias since there will be truncation instead of rounding. The code below does not handle this in general, but does for the case of rounding the decimal to two digits (otherwise the decimal would truncate to 27). Adding 1/2 prior to right bit-shifts would result in rounding when rescaling. Adding (divisor - 1) / 2 to a dividend would result in rounding for division. However, the error incurred from truncating may be small relative to other sources of errors.

  mov rax, 123   ; rax = 123
  imul rax, rax  ; rax *= rax
  shl rax, 8     ; use scaling factor of 2 ^ -8 for the numerator
  mov rbx, 320   ; rbx = 320
  xor rdx, rdx   ; set numerator high bits to 0 for division
  div rbx        ; rax /= rbx
  movzx ebx, al  ; move fractional part from al to bl
  shr rax, 8     ; rescale to unit scaling factor
  imul rbx, 100  ; multiply leftmost two decimals out of fractional part
  add rbx, 0x80  ; if fractional part > 1/2, carry into integer
  shr rbx, 8     ; drop fraction from rbx
dannyadam
  • 3,950
  • 2
  • 22
  • 19