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

为何fprem1指令未输出预期结果?技术求助

fprem1指令大指数差时余数异常问题分析与解决

《汇编语言的艺术》中提到:fprem1指令用于计算部分余数,计算st0/st1的部分余数。若ST0与ST1的指数差小于64,fprem1可通过一次运算得到精确余数。

ST0和ST1是FPU数据寄存器,我针对该指令做了测试,编写的代码如下:

program  exercise;

#include( "stdlib.hhf" );
static
          x: real80:=156.5e18;//first number
          y: real80:=14.36;  //second number
          z:real80;  // partial remainder
begin exercise;
     fld(y);    //y goes into st0
     fld(x);    //now x goes into st0 but previous content of st0 
                //(that is st1) will move into st1, so x is into st0 and y is into st1
     fprem1();  //calculates the remainder of st0/st1 (here x/y )but 
                // for getting the remainder, the quotient will round to the nearest value and then
                //  remainder = st0 - rounded quotient*st1==>>that is
                // equivalent by remainder= x-rounded quotient*y 
                //  ===>> then the remainder  can be positive or negative.
     fstp(z);   //this will store the partial remainder in the z real80 variable.
     stdout.put("z:  ",z); 
end  exercise;

运行程序后发现,指数差较小时结果符合预期,但指数差较大时结果与预期相差甚远,整理的测试结果如下:

序号被除数/除数预期余数程序输出余数
1156.5/14.36-1.46-1.4599..9e000
2156.5e1/14.36-0.24-2.39e-1
3156.5e2/14.36-2.4-2.39
4156.5e3/14.364.724.7200...619
5156.5e4/14.364.124.1200...6195
6156.5e5/14.36-1.88-1.8799...805
7156.5e6/14.36-4.44-4.4399..3805
8156.5e7/14.36-1.32-1.3199...5017
9156.5e8/14.361.161.16006..826
10156.5e9/14.36-2.76-2.7599..737
11156.5e10/14.361.121.1200619..627
12156.5e11/14.36-3.16-3.159380..739
13156.5e12/14.36-2.88-2.873805..346
14156.5e13/14.36-0.08-7.99..e-2 ~= -0.079
15156.5e14/14.36-0.8-7.99..e-1 ~= -0.79
16156.5e15/14.366.366.36340...924
17156.5e16/14.366.166.1940300..120
18156.5e17/14.364.164.500300559
19156.5e18/14.361.485.13274..e10
20156.5e19/14.360.445.13274..e10

第19、20项结果明显不符合预期,余数远大于除数,这是错误的。请问是我的操作有误,还是fprem1指令存在问题?能否解释此问题并给出解决办法?


问题原因

你没有操作错误,问题出在fprem1指令的设计限制:

  • fprem1单次最多只能处理指数差小于64的情况,此时能直接得到符合数学定义的余数(绝对值小于除数)。
  • 当ST0与ST1的指数差≥64时,单次fprem1只能完成部分迭代计算,输出的是中间结果(也就是你看到的超大值),同时会设置FPU状态字中的C2标志位(位10),提示需要重复执行指令直到C2清零,才能得到最终正确的余数。

解决办法

必须循环执行fprem1,直到FPU状态字的C2标志位为0。具体做法是每次执行fprem1后,读取FPU状态字并检查C2位,若为1则继续执行fprem1,直到C2为0。

修改后的代码如下:

program  exercise;

#include( "stdlib.hhf" );
static
          x: real80:=156.5e18;//first number
          y: real80:=14.36;  //second number
          z:real80;  // partial remainder
begin exercise;
     fld(y);    //y存入st0
     fld(x);    //x存入st0,原st0的y移到st1,此时st0=x,st1=y
     fprem1();  //首次执行部分余数计算
     fstsw ax;  //将FPU状态字加载到AX寄存器
     test ax, 0x400;  //检查C2标志位(对应掩码0x400)
     jnz $-6;         //如果C2为1,跳回fprem1重新执行($-6是回退到fprem1指令的位置)
     fstp(z);   //将最终余数存入z
     stdout.put("z:  ",z); 
end  exercise;

代码说明

  • fstsw ax:把FPU的16位状态字读取到AX寄存器,状态字的第10位(二进制位10)就是C2标志。
  • test ax, 0x400:通过位掩码检查C2标志是否被设置(0x400是2^10的十六进制表示)。
  • jnz $-6:如果C2标志为1(结果不为0),就跳转到当前指令往前6字节的位置,也就是fprem1指令,重复执行直到C2标志清零。

这样修改后,无论指数差多大,都能得到绝对值小于除数的正确余数。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 01:23:11