small (1 and 2 word) floating point literals not parsed correctly

Fix gitlab issue #5.

The parse_float() function has several bugs:

    1. For 1- and 2-word floats it will always write all 4 bytes if the value is
       exactly 0.
    2. It incorrectly rounds and normalizes 1- and 2-word floats.

To see the issue easily try the following inputs:

     .flt2  1.0
     .flt4  1.0

These will assemble as '040100 000000' and '040200 000000 000000 000000'.
They should both begin '040200'.

In fact the test file test/test-prec.mac is incorrect in its only floating point value:
	.word	^f 1.5		; 040140
should actually assemble as 040300, not 040140 (040140 is actually ^F0.875).
I confirmed this on RT-11 running MACRO V05.06.

I fixed the problem with the following deltas:

[the patch]

The most crucial change is the very last one. 0x200000000000 is actually (1 << 45)
and because ufrac is normalized it means that it will always downshift ufrac by 1.
This commit is contained in:
Bjoren Davis 2021-01-18 20:22:34 +01:00 committed by Olaf Seibert
parent 07ee904c38
commit 6f7e7963bc
3 changed files with 36 additions and 11 deletions

43
parse.c
View File

@ -353,31 +353,56 @@ int parse_float(
*endp = cp;
if (d == 0.0) {
flt[0] = flt[1] = flt[2] = flt[3] = 0; /* All-bits-zero equals zero */
for (i = 0; i < size; i++) {
flt[i] = 0; /* All-bits-zero equals zero */
}
return 1; /* Good job. */
}
frac = frexp(d, &sexp); /* Separate into exponent and mantissa */
if (sexp < -128 || sexp > 127)
if (sexp < -127 || sexp > 127)
return 0; /* Exponent out of range. */
uexp = sexp + 128; /* Make excess-128 mode */
uexp &= 0xff; /* express in 8 bits */
/*
* frexp guarantees its fractional return value is
* abs(frac) >= 0.5 and abs(frac) < 1.0
* Another way to think of this is that:
* abs(frac) >= 2**-1 and abs(frac) < 2**0
*/
if (frac < 0) {
sign = 0100000; /* Negative sign */
sign = (1 << 15); /* Negative sign */
frac = -frac; /* fix the mantissa */
}
/* The following big literal is 2 to the 49th power: */
/*
* For the PDP-11 floating point representation the
* fractional part is 7 bits (for 16-bit floating point
* literals), 23 bits (for 32-bit floating point values),
* or 55 bits (for 64-bit floating point values).
* However the bit immediately above the MSB is always 1
* because the value is normalized. So it's actually
* 8 bits, 24 bits, or 56 bits.
* We multiply the fractional part of our value by
* 2**56 to fully expose all of those bits (including
* the MSB which is 1).
*/
/* The following big literal is 2 to the 56th power: */
ufrac = (ulong64) (frac * 72057594037927936.0); /* Align fraction bits */
/* Round from FLT4 to FLT2 */
if (size < 4) {
ufrac += 0x80000000; /* Round to nearest 32-bit
representation */
/* ufrac is now >= 2**55 and < 2**56 */
if (ufrac > 0x200000000000) { /* Overflow? */
/* Round from FLT4 to FLT2 or single-word float */
if (size < 4) {
/* Round to nearest 8- or 24- bit approximation */
ufrac += (1UL << (56 - (8 + 16*(size-1)) - 1));
if ((ufrac >> 56) > 0) { /* Overflow? */
ufrac >>= 1; /* Normalize */
uexp--;
}

View File

@ -45,7 +45,7 @@
45 000100 070204 .word ^r<R D> ; bracketed strings are an extension
46 000102 070250 .word ^r<RA > ; bracketed strings are an extension
47 000104 157614 .word ^R50. ; 157614 there is no whitespace in between.
48 000106 040140 .word ^f 1.5 ; 040140
48 000106 040300 .word ^f 1.5 ; 040300
49
50 ;;;;;
51 ;

View File

@ -45,7 +45,7 @@
.word ^r<R D> ; bracketed strings are an extension
.word ^r<RA > ; bracketed strings are an extension
.word ^R50. ; 157614 there is no whitespace in between.
.word ^f 1.5 ; 040140
.word ^f 1.5 ; 040300
;;;;;
;