You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

汇编程序读写文件计算e^x/e^-x异常:无响应或输出垃圾数据

汇编程序计算ex/e(-x)的无限循环问题排查

问题描述

用汇编语言编写程序,从文件读取x值,通过泰勒展开公式计算e^x和e^(-x)。当前程序可正常打印表头字符串,但输出计算结果时无响应,输入操作会产生垃圾数据,需按Ctrl-C终止程序。

已尝试操作

  • 最初将计算逻辑放在主程序中,可正常打印x=0的结果,但后续触发段错误;
  • 将打印数值的printf调用移至exp_calc函数调用之后,为变量字符串添加空字符;
  • 将数据加载至未使用的寄存器,并用gdb调试,仅提示疑似无限循环,无法定位具体错误。

汇编代码

.data
limit:  .double 0r1.0e-13        // Limit for convergence

.text
variables: .string "     x       |     e^x       |     e^(-x)       |\n"
values:    .string "     %0.10f  |     %0.10f    |     %0.10f       |\n"
errors:    .string "ERROR! File not found!\n"

buffer = 8                       // Buffer size for reading from file
alloc = -(16 + buffer) & -16     // Allocate space on the stack
dealloc = -alloc
buffer_s = 16                    // Buffer size for storing input value

// Register aliases
define(argc, w20)
define(argv, x21)
define(buff, x22)
define(nread, x23)
define(fd, w24)

        .balign 4
        .global main
main:   stp     x29, x30, [sp, alloc]!  // Save frame pointer and link register
        mov     x29, sp                 // Set up new frame pointer

        mov     argc, w0                // argc (number of arguments)
        mov     argv, x1                // argv (argument values)

        cmp     argc, 1                 // Check if no file name is provided
        b.le    error                   // Branch to error if argc <= 1

        mov     w0, -100
        ldr     x1, [x1, buffer]    // Load file name from argv

        mov     w2, 0                   // Open file for reading
        mov     w3, 0
        mov     x8, 56
        svc     0
        mov     fd, w0                  // File descriptor
        cmp     fd, 0
        b.lt    error                   // Branch to error if file open failed

        // File open succeeded
        adrp    x0, variables
        add     x0, x0, :lo12:variables
        bl      printf                  // Print column headers

        add     buff, x29, buffer_s     // Buffer for storing input value

loop:
        mov     w0, fd                // File descriptor
        mov     x1, buff
        mov     w2, 8                   // Read 8 bytes (double precision)
        mov     x8, 63
        svc     0
        mov     nread, x0               // Number of bytes read
        cmp     nread, 8                // Check for end of file
        b.ne    end                     // Branch to end if end of file

        ldr     d0, [buff]              // Load input value
        bl      exp_calc
        fmov    d1, d7

        ldr     d0, [buff]              // Load input value
        fneg    d0, d0
        bl      exp_calc
        fmov    d2, d7

        adrp    x0, values
        add     x0, x0, :lo12:values
        bl      printf                  // Print x, e^x, and e^(-x)

        b       loop                    // Process next input value

exp_calc:
        fmov    d2, d0                  // x
        ldr     d1, [sp, limit]         // Load limit
        bl      exp_series              // Calculate e^x
        fmov    d0, d4                  // Store e^x in d0
        bl      exp_series              // Calculate e^(-x)
        fmov    d1, d4                  // Store e^(-x) in d1
        ret                             // Return to caller

exp_series:
        fmov    d4, 1.0                 // Initialize e^x
        mov     x4, 0                   // Initialize term counter
        fmov    d5, 1.0                 // Initialize factorial
        fmov    d6, d0                  // Initialize x^n
        fmov    d11, 1.0

exp_loop:
        fmul    d6, d6, d0              // x^(n+1)
        fadd    d5, d5, d11             // (n+1)!
        fdiv    d7, d6, d5              // x^(n+1)/(n+1)!
        fadd    d4, d4, d7              // e^x += term
        fabs    d7, d7                  // Absolute value of term
        fcmp    d7, d1                  // Check if term is less than limit
        b.gt    exp_loop                // Continue loop if term is greater than limit

        ret                             // Return to caller

error:
        adrp    x0, errors
        add     x0, x0, :lo12:errors
        bl      printf                  // Print error message

end:
        mov     w0, fd                // File descriptor
        mov     x8, 57                  // close I/O request
        svc     0                       // call system function
        mov     w0, 0                 // return 0

        ldp     x29, x30, [sp], dealloc // Restore frame pointer and link register
        ret                             // Return to caller

错误分析与修复方案

1. 核心无限循环原因

  • limit加载错误:exp_calc中ldr d1, [sp, limit]试图从栈偏移读取.data段的limit,实际读取的是栈上垃圾数据,导致泰勒展开的终止条件永远不满足,进入无限循环。
  • 阶乘计算错误:exp_series中用fadd d5, d5, d11累加1计算阶乘,实际阶乘应该是累乘,此写法导致分母增长过慢,项的绝对值无法收敛到limit以下。
  • e^(-x)参数传递错误:exp_calc中计算完e^x后,将d0设为e^x的值,再调用exp_series,此时传入的不是-x而是e^x,逻辑完全错误。

2. 具体修复点

修复exp_calc函数

exp_calc:
        sub     sp, sp, 16             // 保存原始x到栈(遵守AAPCS调用规范,d0-d7为临时寄存器)
        str     d0, [sp, 8]
        adrp    x0, limit
        add     x0, x0, :lo12:limit
        ldr     d1, [x0]               // 正确加载.data段的limit
        bl      exp_series
        fmov    d7, d4                 // 保存e^x结果到d7
        ldr     d0, [sp, 8]            // 取出原始x
        fneg    d0, d0                 // 生成-x
        bl      exp_series
        fmov    d8, d4                 // 保存e^(-x)结果到d8(被保存寄存器)
        add     sp, sp, 16
        ret

修复exp_series阶乘计算

exp_series:
        fmov    d4, 1.0                // e^x初始值(泰勒展开第一项)
        mov     x4, 1                  // 项数计数器从1开始
        fmov    d5, 1.0                // 阶乘初始值1!
        fmov    d6, d0                 // x^1初始值
        fmov    d10, d0                // 保存x到d10

exp_loop:
        fdiv    d7, d6, d5             // 计算当前项x^n/n!
        fadd    d4, d4, d7             // 累加至e^x
        fabs    d7, d7
        fcmp    d7, d1                 // 判断是否小于收敛阈值
        b.le    exp_exit               // 满足条件则退出循环

        add     x4, x4, 1              // 项数+1
        fmul    d6, d6, d10            // x^(n+1) = x^n * x
        fmul    d5, d5, x4.d           // (n+1)! = n! * (n+1)(整数转浮点)
        b       exp_loop

exp_exit:
        ret

修复main中文件名读取

ldr     x1, [argv, 8]    // 正确读取argv[1](文件名),原代码语义错误(虽数值巧合正确)

修复后效果

修复后程序可正确读取文件中的x值,通过泰勒展开收敛计算e^x和e^(-x),并正常输出结果,无无限循环或垃圾数据问题。

内容的提问来源于stack exchange,提问作者Jarvis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 03:45:57