Skip to content

Commit 69fe60b

Browse files
committed
feat: add StrictRounding statement to round floats and to trim excess GMP precision
- The user can specify a precision (in bits or digits) for the rounding. If no number is given, the default precision is used. - Ensures that extra internal GMP precision (used for tracking accuracy) is cut off and properly rounded - Internally the mpf_get_str and mpf_set_str are used. This means we could also implement base 2 rounding. - refactor: changed AO.floatsize such that it can now also hold floats in base 2. - This resolves #698
1 parent 7fa4cef commit 69fe60b

File tree

5 files changed

+101
-1
lines changed

5 files changed

+101
-1
lines changed

sources/compiler.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ static KEYWORD com2commands[] = {
218218
,{"splitarg", (TFUN)CoSplitArg, STATEMENT, PARTEST}
219219
,{"splitfirstarg", (TFUN)CoSplitFirstArg, STATEMENT, PARTEST}
220220
,{"splitlastarg", (TFUN)CoSplitLastArg, STATEMENT, PARTEST}
221+
#ifdef WITHFLOAT
222+
,{"strictrounding", (TFUN)CoStrictRounding, STATEMENT, PARTEST}
223+
#endif
221224
,{"stuffle", (TFUN)CoStuffle, STATEMENT, PARTEST}
222225
,{"sum", (TFUN)CoSum, STATEMENT, PARTEST}
223226
,{"switch", (TFUN)CoSwitch, STATEMENT, PARTEST}

sources/declare.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,8 @@ SBYTE *ReadFloat(SBYTE *);
17431743
UBYTE *CheckFloat(UBYTE *,int *);
17441744
void SetfFloatPrecision(LONG);
17451745
int EvaluateFun(PHEAD WORD *, WORD, WORD *);
1746+
int CoStrictRounding(UBYTE *);
1747+
int StrictRounding(PHEAD WORD *, WORD, WORD, WORD);
17461748
#endif
17471749

17481750
/*

sources/float.c

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,7 @@ int SetFloatPrecision(WORD prec)
917917
else {
918918
AC.DefaultPrecision = prec;
919919
if ( AO.floatspace != 0 ) M_free(AO.floatspace,"floatspace");
920-
AO.floatsize = ((10*prec)/33+20)*sizeof(char);
920+
AO.floatsize = (prec+64)*sizeof(char);
921921
AO.floatspace = (UBYTE *)Malloc1(AO.floatsize,"floatspace");
922922
mpf_set_default_prec(prec);
923923
return(0);
@@ -1348,6 +1348,50 @@ int CoToRat(UBYTE *s)
13481348

13491349
/*
13501350
#] CoToRat :
1351+
#[ CoStrictRounding :
1352+
1353+
Syntax: StrictRounding [precision][base]
1354+
- precision: number of digits to round to (optional)
1355+
- base: 'd' for decimal (base 10) or 'b' for binary (base 2)
1356+
1357+
If no arguments are provided, uses default precision with binary base.
1358+
*/
1359+
int CoStrictRounding(UBYTE *s)
1360+
{
1361+
GETIDENTITY
1362+
WORD x;
1363+
int base;
1364+
if ( AT.aux_ == 0 ) {
1365+
MesPrint("&Illegal attempt for strict rounding without activating floating point numbers.");
1366+
MesPrint("&Forgotten %#startfloat instruction?");
1367+
return(1);
1368+
}
1369+
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
1370+
if ( *s == 0 ) {
1371+
/* No subkey, which means round to default precision */
1372+
x = AC.DefaultPrecision - AC.MaxWeight - 1;
1373+
Add4Com(TYPESTRICTROUNDING,x,2);
1374+
return(0);
1375+
}
1376+
if ( FG.cTable[*s] == 1 ) { /* number */
1377+
ParseNumber(x,s)
1378+
if ( tolower(*s) == 'd' ) { base = 10; s++; } /* decimal base */
1379+
else if ( tolower(*s) == 'b' ){ base = 2; s++; } /* binary base */
1380+
else goto IllPar; /* invalid base specification */
1381+
}
1382+
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
1383+
1384+
/* Check for invalid arguments */
1385+
if ( *s ) {
1386+
IllPar:
1387+
MesPrint("&Illegal argument(s) in StrictRounding statement: '%s'",s);
1388+
return(1);
1389+
}
1390+
Add4Com(TYPESTRICTROUNDING,x,base);
1391+
return(0);
1392+
}
1393+
/*
1394+
#] CoStrictRounding :
13511395
#[ ToFloat :
13521396
13531397
Converts the coefficient to floating point if it is still a rat.
@@ -1416,6 +1460,52 @@ int ToRat(PHEAD WORD *term, WORD level)
14161460

14171461
/*
14181462
#] ToRat :
1463+
#[ StrictRounding :
1464+
1465+
Rounds floating point numbers to a specified precision
1466+
in a given base (decimal or binary).
1467+
*/
1468+
int StrictRounding(PHEAD WORD *term, WORD level, WORD prec, WORD base) {
1469+
WORD *tstop, *t, *oldworkpointer = AT.WorkPointer;
1470+
int retval,sign;
1471+
1472+
tstop = term + *term; tstop -= ABS(tstop[-1]);
1473+
t = term+1;
1474+
while ( t < tstop ) {
1475+
if ( *t == FLOATFUN && t + t[1] == tstop ) {
1476+
char *s;
1477+
mp_exp_t exp;
1478+
/* Extract the floating point value */
1479+
UnpackFloat(aux4,t);
1480+
/* Convert to string: the generated string is a fraction with an implicit
1481+
radix point immediately to the left of the first digit.
1482+
The applicable exponent is written in exp. */
1483+
s = mpf_get_str(0,&exp, base, prec, aux4);
1484+
/* Format as MeN with M the mantissa and N the exponent */
1485+
snprintf((char *)AO.floatspace,AO.floatsize,".%se%ld",s,exp);
1486+
/* Negative base values are used to specify that the exponent is in decimal */
1487+
mpf_set_str(aux4,(char *)AO.floatspace,-base);
1488+
free(s);
1489+
break;
1490+
}
1491+
t += t[1];
1492+
}
1493+
if ( t < tstop ) {
1494+
/* Pack the rounded floating point value back into the term */
1495+
PackFloat(t,aux4);
1496+
t+=t[1];
1497+
if ( term[*term-1] < 0 ) sign = -1;
1498+
else sign = 1;
1499+
*t++ = 1; *t++ = 1; *t++ = 3*sign;
1500+
*term = t - term;
1501+
}
1502+
AT.WorkPointer = t;
1503+
retval = Generator(BHEAD term,level);
1504+
AT.WorkPointer = oldworkpointer;
1505+
return(retval);
1506+
}
1507+
/*
1508+
#] StrictRounding :
14191509
#] Float Routines :
14201510
#[ Sorting :
14211511

sources/ftypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ typedef int (*TFUN1)();
605605
#define TYPEEXPAND 88
606606
#define TYPETOFLOAT 89
607607
#define TYPETORAT 90
608+
#define TYPESTRICTROUNDING 91
608609
#endif
609610

610611
/*

sources/proces.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3990,6 +3990,10 @@ SkipCount: level++;
39903990
AT.WorkPointer = term + *term;
39913991
if ( ToRat(BHEAD term,level) ) goto GenCall;
39923992
goto Return0;
3993+
case TYPESTRICTROUNDING:
3994+
AT.WorkPointer = term + *term;
3995+
if ( StrictRounding(BHEAD term,level,C->lhs[level][2],C->lhs[level][3]) ) goto GenCall;
3996+
goto Return0;
39933997
#endif
39943998
}
39953999
goto SkipCount;

0 commit comments

Comments
 (0)