1

enter image description here

enter image description here I'm trying to write a loop in assembly that is the equivalent of:

for(i=0; x1 + i*h <= x2; i++)

And each time I call another function that uses (x1 + i*h) as one of its parameters for some reason it receives it with the addition of 0.00000000000000001. For example if I send it 0.26 it receives 0.26000000000000001 instead, which ends up in crashing the program.

Here's the C part of the program:

#include <stdio.h>
#include <math.h>

extern double find_delta1(double(*f)(double), double x0, double eps);
extern double find_delta2(double(*f)(double), double x1, double x2, double h, double eps);

double mysqr(double x)
{
    return x*x;
} // mysqr

int main()
{
    double x1 = -0.5, x2 = 1.0, x, eps = 0.001, h = 0.01;

    x = (x1 + x2) / 2.0;
    printf("\nfind_delta1(mysqr,%lf, %lf) = %lf\n", x, eps, find_delta1(mysqr, x, eps));
    printf("\nfind_delta2(mysqr, %lf, %lf,%lf, %lf) = %lf\n", x, x2, h, eps, find_delta2(mysqr, x, x2, h, eps));

    return 0;
} // main

And here's the assembly part:

.MODEL SMALL
.DATA
    DELTA       DQ  ?
    CURRENTX    DQ  ?
    TEMP        DQ  ?
    MIN         DQ  ?
    X           DQ  ?
    I           DW  ?
    TWO         DQ  2.0
.CODE
.386
.387
;double find_delta1(double (*f)(double),double x0, double eps)
;                       +4                  +6          +14

PUBLIC _find_delta1
_find_delta1 PROC NEAR

PUSH BP
MOV BP,SP

FLDZ        ;
FST X       ;
FSTP DELTA  ; Initialize parameters

FLD QWORD PTR [BP+6]    ;
FABS                    ;
FSTP DELTA              ; Delta = |X0|

LOOP1:
FLD DELTA
FDIV TWO                ;
FST DELTA               ;   Delta = Delta / 2.0
FLD QWORD PTR [BP+6]    ;
FADD                    ;
FSTP X                  ;  X = X0 + Delta

PUSH X                  ;
CALL WORD PTR [BP+4]    ;
ADD SP,8                ;  ST(0) = f(X)

PUSH QWORD PTR [BP+6]   ;
CALL WORD PTR [BP+4]    ;
ADD SP,8                ;  ST(0) = f(X0)

FSUB        ;
FABS        ;   ST(0) = |f(X) - f(X0)|

FCOMP QWORD PTR [BP+14]     ;
FSTSW AX                    ;
SAHF                        ;   Compare with epsilon
JNB LOOP1

FLD DELTA       ; Return Delta

POP BP
RET
_find_delta1 ENDP
;----------------------------------------------------------------------------------------------------------------------------


;----------------------------------------------------------------------------------------------------------------------------

;double find_delta2(double (*f)(double),double x1, double x2, double h, double eps)
;                           +4              +6          +14     +22         +30

PUBLIC _find_delta2
_find_delta2 PROC NEAR

PUSH BP
MOV BP,SP

MOV I,0
FLDZ            ;
FST TEMP        ;
FST CURRENTX    ;
FSTP MIN        ; Initialize parameters

PUSH QWORD PTR [BP+30]  ;
PUSH QWORD PTR [BP+6]   ;
PUSH WORD PTR [BP+4]    ;
CALL _find_delta1       ;
ADD SP,20               ;   MIN = find_delta1(mysqr, X1, eps)

FSTP MIN

LOOP2:
FLD QWORD PTR [BP+6]    ; ST(0) = X1
FLD QWORD PTR [BP+22]   ; ST(0) = h
FIMUL I                 ; ST(0) = I*h
FADD                    ; ST(0) = X1 + I*h
FSTP CURRENTX

PUSH QWORD PTR [BP+30]  ;
PUSH CURRENTX           ;
PUSH WORD PTR [BP+4]    ;
CALL _find_delta1       ;
ADD SP,20               ;   ST(0) = find_delta1(mysqr, X1 + I*h, eps)

FCOM MIN            ;
FSTSW AX            ;
SAHF                ;
JNB NOTBELOW        ;   Compare with current minimum

FSTP MIN            ;   If (find_delta1(mysqr, X1 + I*h, eps) < MIN)
INC I               ;   -> MIN = find_delta1(mysqr, X1 + I*h, eps)
JMP SHORT NEXT      ;       i++

NOTBELOW:           ;   Else
FSTP TEMP           ;   -> i++
INC I               ;

NEXT:
FLD CURRENTX
FCOMP QWORD PTR [BP+14] ;
FSTSW AX                ;
SAHF                    ;
JNE LOOP2               ; Compare (X1 + I*h) with X2


FLD MIN

POP BP
RET
_find_delta2 ENDP
END

The problem occurs in the second iteration of LOOP2, since the first time I*h = 0, hence no change is applied to x1.

If someone can tell me what I'm doing wrong I'd be very grateful.

  • Use a debugger to step through the code and see what's wrong. That said, I find your FPU stack usage suspicious, I don't think you balance it properly. – Jester Feb 01 '19 at 14:13
  • @Jester I have debugged it. The FPU stack is empty after each function call, and even before sending the parameters ST(0) contains the correct value(which is 0.26 during the second loop) but then it pushes it and it receives the addition. – Leran Dagash Feb 01 '19 at 14:17
  • Maybe your assembler treats `FADD` as `FADDP`, still, it's better to write that out. Anyway, show trace or screenshot of the problem in the debugger. Also how come you can do `PUSH QWORD PTR [BP+30]`? – Jester Feb 01 '19 at 14:21
  • @Jester Is there anyway for me to attach an image? Pushing that way works fine. – Leran Dagash Feb 01 '19 at 14:37
  • Okay, I attached the image showing the number after pushing. And what difference would it make if my assembler treats FADD as FADDP? – Leran Dagash Feb 01 '19 at 14:44
  • 3
    That looks like simple precision issue. You know that `0.26` can't be exactly represented in binary float, right? – Jester Feb 01 '19 at 14:50
  • x86 still supports x87 so you don't need to use DOSbox – phuclv Feb 01 '19 at 14:54
  • @Jester I added another picture showing the value before pushing. If this isn't the problem I don't know what is, since everything else should be running just fine. – Leran Dagash Feb 01 '19 at 14:54
  • @phuclv This is what my college wants us to use. We run all assembly related programs using DOSbox(in this course, at least). – Leran Dagash Feb 01 '19 at 14:56
  • 1
    FPU registers use 80 bit extended precision (which of course still can't represent `0.26` exactly). Once you round-trip that through a 64 bit double you will get rounding errors. – Jester Feb 01 '19 at 15:01
  • @Jester Then what should I do to fix the problem? There doesn't seem to be any other reason causing it. – Leran Dagash Feb 01 '19 at 15:03
  • 1
    That's how floats work. Fix your code so that it doesn't cause a crash. – Jester Feb 01 '19 at 15:04
  • @Jester I tried. If I could I wouldn't be here asking. – Leran Dagash Feb 01 '19 at 15:06
  • 2
    Show the actual problem. This is not a problem. Anyway, as I said, there are various strange things in your program, such as pushing qword in 16 bit mode which simply should not work unless your assembler is doing something automagically. – Jester Feb 01 '19 at 15:07
  • @Jester The problem is that during the third iteration of LOOP2 the program just freezes then exits. I was taught that every single operand operation performed on [ ] requires casting, hence the QWORD PTR. – Leran Dagash Feb 01 '19 at 15:15
  • 2
    Single step that part in the debugger and see what exactly is happening. As for the qword ptr, I'd love to see a disassembly of your code to find out what your assembler is doing. Sane assemblers (e.g. `nasm`) just throw an error. – Jester Feb 01 '19 at 15:22
  • @Jester I can trace it just fine during debugging until FSTP is performed which pops it out of the FPU and into CURRENTX which is represented as qword 3FD0A3D70A3D70A4 which I'm having trouble converting to a floating point number. By the way I'm using TASM and TLINK for assembly only programs, and TurboC for assembly + C programs (Borland International). – Leran Dagash Feb 01 '19 at 15:32
  • See [here for online converter](http://binaryconvert.com/result_double.html?hexadecimal=3FD0A3D70A3D70A4). That is the correct value. I have `tasm` version 2.0 and that produces errors as expected, e.g. `Argument to operation or instruction has illegal size`. – Jester Feb 01 '19 at 15:33
  • Also check [https://floating-point-gui.de/ "What Every Programmer Should Know About Floating-Point Arithmetic"](https://floating-point-gui.de/) – Ped7g Feb 01 '19 at 16:17
  • Thanks for the links. – Leran Dagash Feb 01 '19 at 17:05
  • Are you allowed to use [`fcomi` (Pentium Pro and newer)](https://www.felixcloutier.com/x86/fcomi:fcomip:fucomi:fucomip) to compare into FLAGS, instead of needing that clunky `FSTSW AX / SAHF` before branching? But obviously that's not the problem, the problem is `JNE`. https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ – Peter Cordes Feb 02 '19 at 09:05
  • @PeterCordes Never seen that one before. I know I can use FUCOM and FICOM. Anyways, how would that help? – Leran Dagash Feb 02 '19 at 11:14
  • By making your code more readable (and efficient), just `fcomi / jnb` or whatever. – Peter Cordes Feb 02 '19 at 11:23
  • @PeterCordes I see. Thanks. – Leran Dagash Feb 02 '19 at 15:02
  • You tagged this emu8086, but your screenshots show DosBOX. I think DOSBOX emulates a modern CPU in 16-bit mode (so `fcomi` would be available.) emu8086 does *not*, it emulates a real 8086 + 8087. At least I think it does. `fstsw ax` was new in 286, according to http://www.posix.nl/linuxassembly/nasmdochtml/nasmdoca.html, so you couldn't use that on 8086+8087 either (only `fstsw [mem]`). If you don't care about 8086 compat, might as well use `fcomi` unless you care about P5 pentium or 486 or something. – Peter Cordes Feb 02 '19 at 18:03
  • 1
    @PeterCordes the dosbox reality is some more complicated, the more accurate is "it emulates whatever it takes to run old DOS era SW", so the support for 586+ is only limited, and done more on "show me the **old** SW which does not work" basis, than fixing the dosbox to have proper emulation of pentium and later. There were also some direct clashes in forum about its state and multiple forks, so one should rather treat dosbox support of anything of 586+ cautiously and verify it works as expected... Or use one of the forks which decided to push for full emulation and not just old SW/games. – Ped7g Feb 04 '19 at 15:44
  • 1
    The problem has been solved. Instead of ADD SP,20 I should have been adding 18. That was causing the program to crash at a certain point. Also JNE for LOOP2 should be JBE. – Leran Dagash Feb 04 '19 at 16:27

0 Answers0