Skip to content

Commit

Permalink
Fix floating point math error detection
Browse files Browse the repository at this point in the history
Working with floating point values were not handling errors correctly.

Most of the heavy lifting of performing floating point operations are
performed within C code.  Errors were not being properly caught which
resulted in invalid results.

Use the C99 provided functions for handling floating point exceptions.
  • Loading branch information
CheyenneWills committed Jun 15, 2023
1 parent d6a2b32 commit 93af96f
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 18 deletions.
21 changes: 9 additions & 12 deletions int.asm
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ reg_fl: db 0 ; condition code register for numeric operations

align 8
fl_flags: d_word 0 ; float flags
global reg_flerr
reg_flerr: d_word 0 ; Floatint point error

align 8
; constants
Expand Down Expand Up @@ -798,17 +800,8 @@ setovr: mov al,1 ; set overflow indicator
pop rsi
pop rdi

mov ax, word [reg_ra+6] ; get top 2 bytes
and ax, 0x7ff0 ; check for infinity or nan
add ax, 0x10 ; set/clear overflow accordingly
seto byte [reg_fl]
jo %%done

xor rax, rax
fstsw ax ; Get FPU status word
stmxcsr [fl_flags] ; Get SSE status
or rax, m_word [fl_flags] ; Combine
and al, 0x1f ; INVALID | DENORM | DIVBYZERO | OVERFLOW | UNDERFLOW
mov rax, m_word [reg_flerr] ; Combine
and al, 0x1f ; INVALID | DIVBYZERO | OVERFLOW | UNDERFLOW
mov byte [reg_fl], al
%%done: ret
%endmacro
Expand Down Expand Up @@ -855,7 +848,11 @@ setovr: mov al,1 ; set overflow indicator
pop rdx
pop rsi
pop rdi
ret
xor rax,rax
mov rax, [reg_flerr] ; 0x01 0x04 0x08 0x10
and al, 0x1f ; INVALID | DIVBYZERO | OVERFLOW | UNDERFLOW
mov byte [reg_fl], al
%%done ret
%endmacro

math_op atn_,f_atn
Expand Down
14 changes: 12 additions & 2 deletions osint/float.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ This file is part of Macro SPITBOL.

#include "port.h"
#include <math.h>
#include <fenv.h>
#define FE_SBL_EXCEPT (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW )

#if (FLOAT & !FLTHDWR) | EXTFUN


Expand All @@ -45,28 +48,35 @@ void f_str() { // store real
}

void f_adr() { // add real
feclearexcept(FE_ALL_EXCEPT);
reg_ra += *reg_rp;
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
return;
}

void f_sbr() { // subtract real
feclearexcept(FE_ALL_EXCEPT);
reg_ra -= *reg_rp;
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
return;
}

void f_mlr() { // multiply real
feclearexcept(FE_ALL_EXCEPT);
reg_ra *= *reg_rp;
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
return;
}

void f_dvr() { // divide real
feclearexcept(FE_ALL_EXCEPT);
if (*reg_rp != 0.0) {
reg_ra /= *reg_rp;
reg_fl = 0;
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}
else {
reg_fl = 1;
reg_ra = NAN;
reg_flerr = FE_DIVBYZERO;
}
return;
}
Expand Down
28 changes: 24 additions & 4 deletions osint/math.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,53 @@ Copyright 2012-2017 David Shields
*/

#include "port.h"

#include <errno.h>

#if FLOAT & !MATHHDWR

#include <math.h>
#include <fenv.h>

#ifndef errno
int errno;
#endif

#define FE_SBL_EXCEPT (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW )

extern double inf; // infinity
extern word reg_flerr; /* Floating point error */

/*
* f_atn - arctangent
*/
void f_atn()
{
reg_ra = atan(reg_ra);
feclearexcept(FE_ALL_EXCEPT);
reg_ra = atan(reg_ra);
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}

/*
* f_chp - chop
*/
void f_chp()
{
feclearexcept(FE_ALL_EXCEPT);
if (reg_ra >= 0.0)
reg_ra = floor(reg_ra);
else
reg_ra = ceil(reg_ra);
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}

/*
* f_cos - cosine
*/
void f_cos()
{
feclearexcept(FE_ALL_EXCEPT);
reg_ra = cos(reg_ra);
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}


Expand All @@ -60,10 +69,12 @@ void f_cos()
void f_etx()
{
errno = 0;
feclearexcept(FE_ALL_EXCEPT);
reg_ra = exp(reg_ra);
if (errno) {
reg_ra = inf;
reg_ra = inf;
}
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}

/*
Expand All @@ -72,26 +83,33 @@ void f_etx()
void f_lnf()
{
errno = 0;
feclearexcept(FE_ALL_EXCEPT);

reg_ra = log(reg_ra);
if (errno) {
reg_ra = inf;
reg_ra = inf;
}
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}

/*
* f_sin - sine
*/
void f_sin()
{
feclearexcept(FE_ALL_EXCEPT);
reg_ra = sin(reg_ra);
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}

/*
* f_sqr - square root (reg_range checked by caller)
*/
void f_sqr()
{
feclearexcept(FE_ALL_EXCEPT);
reg_ra = sqrt(reg_ra);
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}

/*
Expand All @@ -100,8 +118,10 @@ void f_sqr()
void f_tan()
{
double result;
feclearexcept(FE_ALL_EXCEPT);
result = tan(reg_ra);
errno = 0;
reg_ra = errno ? inf : result;
reg_flerr = fetestexcept(FE_SBL_EXCEPT);
}
#endif // FLOAT & !MATHHDWR
1 change: 1 addition & 0 deletions osint/osint.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Copyright 2012-2017 David Shields

extern word reg_cp, reg_wa, reg_wb, reg_wc, reg_xr, reg_xl, reg_xs, reg_w0;
extern signed char reg_fl;
extern word reg_flerr;
extern long reg_ia;
extern double reg_ra,*reg_rp;
extern uword minimal_id;
Expand Down

1 comment on commit 93af96f

@CheyenneWills
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit should address the problem described in #21

Please sign in to comment.