【问题标题】:assembly multiply operation 64 bits汇编乘法运算 64 位
【发布时间】:2012-11-27 22:52:17
【问题描述】:

我在汇编中的以下操作时遇到了一些问题。 我正在组装 IA32。假设 -4(%ebp)=x 和 -8(%ebp)=y 我已经从用户那里得到它们(都是 32 位长)。 这是代码:

format1:    .string "Multiply : %u * %u = %llu\n"
format2:    .string "Divide : %u / %u = %u\n"

# operation multiply
movl    -4(%ebp),   %eax
mull    -8(%ebp)
pushl   %edx
pushl   %eax
pushl   -8(%ebp)
pushl   -4(%ebp)
pushl   $format1
call    printf

# operation divide
movl    -4(%ebp),    %eax   
divl    -8(%ebp)
pushl    %eax
pushl   -8(%ebp)
pushl   -4(%ebp)
pushl   $format2
    call    printf

乘法的结果在 %llu 中的原因是因为我希望能够将 2 个长数字相乘并打印结果,即使它达到 64 个字节。 而且在 %edx 中,mull 命令保存了 64 字节结果的“其他 32 字节”,所以我需要将它推送到堆栈以及 printf。 例如我想要这个输出:

 Multiply : 4000000000 * 2 = 16000000000

另外,我希望 3 与 4 的除法运算返回 X.YZ 结果。 (尾数不超过2个数字,不四舍五入) 例如

Divide : 3 / 4 = 0.75

对于 19 和 1000:

Divide : 19 / 1000 = 0.01

对于 8 和 2:

Divide : 8 / 2 = 4.00

我确实尝试了很多来获得结果,但没有成功。 多谢! :)

【问题讨论】:

  • 4000000000 * 2 = 16000000000?星球大战:奔腾反击?
  • 只是想举个例子,我需要能够得到最大 64 位的结果。我还不能让这个例子在我的代码中工作:/
  • 为什么?为什么要标记这个 C 或 C++?如果你坚持C,你有没有问过你当地的C专家,也就是你的C编译器,他会怎么做? (例如,使用 gcc 或 clang 选项 -S 会产生非常好的汇编代码)
  • 对于除法,将分子乘以 100(注意溢出),然后在最后两位数之前插入一个小数点。

标签: c++ c assembly x86


【解决方案1】:

mull 用于整数乘法,divl 用于整数除法。对于浮点数,您可以使用浮点指令fmulfdiv

另一种方法是 Jerry Coffin 在他的评论中建议,在整数乘法之前缩放因子,例如。乘以 100 并将得到的整数视为 100*100 = 10000 倍太大。

【讨论】:

    【解决方案2】:

    你可以找到一些有用的例子here

    这里是一个如何将 2 32 位数字相乘并输出 64 位结果的示例 [Linux, GCC]:

    #include <stdio.h>
    
    char *fr = "MUL %u * %u = %llu\n";
    
    int main()
    {
            __asm__ (
                            "subl $0x14, %esp\n\t"
                            "movl $10, %eax\n\t"
                            "movl %eax, 0x4(%esp)\n\t"
                            "movl $100, %ebx\n\t"
                            // Multimpy two 32bit values and save 64bit result in edx:eax
                            "mull %ebx\n\t"
    
                            // Call printf
                            "movl fr, %esi\n\t"
                            "movl %esi, (%esp)\n\t"
                            "movl %ebx, 0x8(%esp)\n\t"
                            "movl %eax, 0xC(%esp)\n\t"
                            "movl %edx, 0x10(%esp)\n\t"
                            "call printf\n\t"
                            "addl $0x14, %esp\n\t");
    
            return 0;
    }
    
    gcc -m32 ./ttt.c; ./a.out
    MUL 10 * 100 = 1000
    

    对于除法,您需要将数据转换为浮点值并使用fdiv 指令。

    PS。 push 更改 %esp 所以你必须对恢复堆栈指针执行相同次数的 pop。否则你会得到未定义的行为。

    【讨论】:

      【解决方案3】:

      乘法should work as-is。 如何除以得到浮点结果我已经有了answered in your other question。 如果只需要打印成两位数,可以使用相应的格式字符串。

      更新:显示截断为两位数的工作代码:

      .comm x,4,4
      .comm y,4,4
      
      .section    .rodata
      
      format1:    .string "Div : %d / %d = %.2f\n"
      format2:    .string "Mod : %d %% %d = %d\n"
      format3:    .string "Multiply : %u * %u = %llu\n"
      format4:    .string "%d %d"
      const100:   .int 100
      
      .text
      .globl  main
      .type   main, @function
      main:
          subl $32, %esp # allocate space, preserve alignment
      
          movl $format4, (%esp)
          movl $x, 4(%esp)
          movl $y, 8(%esp)
          call scanf
      
      # operation divide
          fildl x
          fimul const100
          fidivl y
      # truncate to integer
      # use this if current FPU rounding mode
      # is known to be truncate
      #   frndint
      # otherwise use this
          fnclex
          fnstcw (%esp)       # save a copy to modify
          fnstcw 2(%esp)      # and a copy to preserve
          orw $0x0c00, (%esp) # rounding mode = truncate
          fldcw (%esp)        # activate
          frndint             # do the truncate
          fldcw 2(%esp)       # restore original
      # end of truncate code
          fidiv const100
          fstpl 12(%esp) # x / y
      
          movl $format1, (%esp)
          movl x, %eax
          movl %eax, 4(%esp)
          movl y, %eax
          movl %eax, 8(%esp)
          call printf
      
      # operation modulo
          movl x, %eax
          cltd
          idivl y
          movl $format2, (%esp)
          movl x, %eax
          movl %eax, 4(%esp)
          movl y, %eax
          movl %eax, 8(%esp)
          movl %edx, 12(%esp)
          call printf
      
      # operation multiply
          movl x, %eax
          mull y
          movl $format3, (%esp)
          movl x, %ecx
          movl %ecx, 4(%esp)
          movl y, %ecx
          movl %ecx, 8(%esp)
          movl %eax, 12(%esp)
          movl %edx, 16(%esp)
          call printf
      
          addl $32, %esp
          xor %eax, %eax
          ret
      

      See in operation.

      【讨论】:

      • 好吧,mul 不起作用 :S 和关于除法,我理解你的答案,但我只需要 2 位数字。你在说什么合适的格式?
      • your multiplication code here in operation。至于格式字符串,试试%.2f打印成两位数。
      • 浮点数对结果进行四舍五入,尝试 19 和 1000。而不是给出 0.1,而是给出 0.2..
      • 确实如此。更新了答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-05
      • 2018-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-13
      相关资源
      最近更新 更多