[Oberon] Wrong output of Texts.WriteReal and Texts.WriteRealFix in PO

Hans Klaver hklaver at dds.nl
Sun Oct 4 16:55:00 CEST 2020


Hi Jörg,

My version of In.Mod is after Martin Reiser's and indeed uses Texts.Scan.
It can be found here: https://github.com/hansklav/Oberon-07

I used n = 14 in the tests (Texts.WriteReal expanded to full width)

Regards,

Hans


> Op 4 okt. 2020, om 16:46 heeft Jörg <joerg.straube at iaeth.ch> het volgende geschreven:
> 
> Hans
> 
> Seems you ported In.Mod to PO. Am I right, that In.Real uses Texts.Scan?
> Just for completeness: what value of „n“ do you use in your tests?
> 
> br, Jörg
> 
>> Am 04.10.2020 um 07:46 schrieb Hans Klaver <hklaver at dds.nl>:
>> 
>> 
>> I wonder if it is known that the output of the present PO version of Texts.WriteReal and Texts.WriteRealFix for various borderline values can be completely false due to untrapped overflow of FLOOR(x)? Although it is acceptable that floating point representation of real numbers produces round-off errors, the output of obviously wrong values is undesirable. 
>> 
>> While looking into this I compared the output of these procedures with the same in my first Oberon (DOS Oberon V1.4, which I ordered from ETH Zurich in the early nineties). I don't have the source code of that early Oberon version, but I suppose it uses the source as published in the 1992 Project Oberon book. For the present PO version prof. Wirth rewrote these procedures, originally by J. Gutknecht.
>> 
>> I used the following test module:
>> 
>> MODULE Test1;
>> IMPORT In, Fonts, Texts, Oberon;
>> VAR W: Texts.Writer; 
>> 
>> PROCEDURE Do*;
>>   VAR x: REAL; k: INTEGER;
>> BEGIN
>>   In.Open;  In.Real(x);  In.Int(k);
>>   IF In.Done THEN 
>>     Texts.WriteReal(W, x, n); Texts.WriteRealFix(W, x, n, k)
>>   ELSE 
>>     Texts.WriteString(W, "Parameter error")
>>   END;
>>   Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)
>> END Do;
>> 
>> BEGIN Texts.OpenWriter(W); Texts.SetFont(W, Fonts.This("Courier10.Fnt"))
>> END Test1.Do^
>> 
>> 
>> Example input & output:
>> x            k  Texts.WriteReal      Texts.WriteRealFix    Texts.WriteRealFix (Ob.-90)
>> 1554.70E5    1  1.554700E+08         155470003.2           155470000.
>> 1554.70E5    2  1.554700E+08         21474836.47           155470000.
>> 1554.70E6    0  1.554700E+09         1554700032.           Trap 7  Overflow
>> 1.0E8        1  1.000000E+08         100000000.0           100000000.0
>> 1.0E9        1  1.000000E+09         214748364.7           Trap 7  Overflow
>> 1.0E9        0  1.000000E+09         1000000000.0          Trap 7  Overflow
>> 221500000.0  2 -2.079967E+08        -21474836.47           221499984.
>> 
>> In the overview of results below I call a result containing the digits 2147483647
>> 'overflow (Mersenne)' or 'overflow (-Mersenne)' because 2147483647 = MAX(INTEGER) = 2^(32)-1, which is a Mersenne prime. 
>> 
>> It can be seen from this table that the Oberon-90 version of Texts.WriteRealFix always gives a 'sane' result: either a proper approximation or a 'Trap 7 Overflow' (see the sceenprint below). The Oberon-90 version of Texts.WriteReal never gave wrong results and never trapped in my tests.
>> Texts.WriteRealFix in Oberon-90 prints at most 9 significant digits when there are no leading zeros; with leading zeros there are at most 13 digits after the decimal point.
>> 
>> The present Oberon-07 versions of these procedures never trap, but regularly give wrong results, due to silent overflow of the FLOOR(x) function.
>> 
>> x            k  Texts.WriteReal      Texts.WriteRealFix    Texts.WriteRealFix (Ob.-90)     
>> 1554.70E5    1  OK                   OK                    OK  (9 digits, no decimal)
>> 1554.70E5    2  OK                   overflow (Mersenne)   OK  (9 digits, no decimal)
>> 1554.70E6    0  OK                   OK                    Trap 7  Overflow
>> 1554.70E7    0  OK                   overflow (Mersenne)   Trap 7  Overflow
>> 1.55470E5    4  OK                   OK                    OK  (9 digits, no decimal)
>> 1.55470E5    5  OK                   overflow (Mersenne)   OK  (9 digits, no decimal)
>> 1.0E8        1  OK                   OK                    OK  (9 digits)
>> 1.0E9        1  OK                   overflow (Mersenne)   Trap 7  Overflow
>> 1.0E9        0  OK                   OK                    Trap 7  Overflow
>> 221500000.0  3  overflow (negative)  overflow (-Mersenne)  OK  (9 digits, no decimal)
>> 221500000.0  2  overflow (negative)  overflow (-Mersenne)  OK  (9 digits, no decimal)
>> 221500000.0  1  overflow (negative)  overflow (negative)   OK  (9 digits, no decimal)
>> 221500000.0  0  overflow (negative)  overflow (negative)   OK  (9 digits, no decimal)
>> 22150000.0   0  OK                   OK                    OK
>> 22150000.0   1  OK                   OK                    OK
>> 22150000.0   2  OK                   overflow (Mersenne)   OK  (1 decimal digit)
>> 22150000.0  11  OK                   overflow (Mersenne)   OK  (1 decimal digit)
>> 2215000.0    2  OK                   OK                    OK
>> 2215000.0    3  OK                   overflow (Mersenne)   OK  (2 decimal digits)
>> 2147483647.0 0  overflow (-1.0)      overflow (  -1.)      Trap 7  Overflow                         
>> 2147483648.0 1  overflow ( 0  )      overflow (   0 )      Trap 7  Overflow          
>> 2147483649.0 0  overflow ( 1.0)      overflow (   1.)      Trap 7  Overflow
>> 214748364.0  0  OK                   OK                    OK
>> 2147484000.0 0  overflow (352.)      overflow (352.)       Trap 7  Overflow
>> 214748400.0  0  overflow (-Mersenne) overflow (-Mersenne)  OK  (9 digits, no decimal)
>> 214740000.0  0  OK                   OK                    OK  (9 digits, no decimal)
>> 214740000.0  1  OK                   OK                    OK  (9 digits, no decimal)
>> 214740000.0  2  OK                   overflow (Mersenne)   OK  (9 digits, no decimal)
>> 0.000012345  9  OK                   .0000123              0.000012345)
>> 0.000012345 11  OK                   .0000123              0.00001234500)
>> 0.000012345 13  OK                   .0000123              0.0000123449984)
>> 0.000012345 14  OK                   .0000123              0.0000123449984)
>> --------------
>> Mersenne:  2147483647 (with decimal point)
>> -Mersenne: -2147483647 (with decimal point)
>> 
>> 
>> I tried to find a way to catch such overflow of FLOOR (so that an ASSERT(FALSE) run time trap can be produced), but was unable to find a satisfactory solution. The only special cases that I could catch were (FLOOR(x) = 2147483647) OR (FLOOR(x) = -2147483647), but this is not a real solution.
>> 
>> I also ported Jürg Gutknecht's Texts.WriteStringFix version to Oberon-07, but I could not think up an implementation of Reals.Convert(x, n, d) without using FLOOR(x)...
>> 
>> Does anyone have a suggestion? Could it be that the TRAP7 is not due to integer overflow but to an array index overflow?
>> 
>> --
>> Hans
>> 
>> 
>> -----------
>> 
>> (* Below is my port to Oberon-07 of Jürg Gutknecht's original Oberon-90 
>>  version of WriteRealFix from the book Project Oberon (1992) *)
>> 
>> PROCEDURE Ten(n: INTEGER): REAL;
>>   VAR t, p: REAL;
>> BEGIN t := 1.0; p := 10.0;   (*compute 10^n *)
>>   WHILE n > 0 DO
>>     IF ODD(n) THEN t := p * t END ;
>>     p := p*p; n := n DIV 2
>>   END ;
>>   RETURN t
>> END Ten;
>> 
>> PROCEDURE Convert (x: REAL; n: INTEGER; VAR d: ARRAY OF CHAR);
>> (* Convert REAL x to a string of n characters.
>>    This version still is unsatisfactory, because FLOOR overflows silently.
>>  *)
>>   VAR i, k: INTEGER;
>> BEGIN
>>   i := FLOOR(x);  k := 0;
>>   WHILE k < n DO
>>     d[k] := CHR(i MOD 10 + 30H); i := i DIV 10; INC(k)
>>   END 
>> END Convert;
>> 
>> PROCEDURE WriteRealFix* (VAR W: Texts.Writer; x: REAL; n, k: INTEGER);
>>   CONST maxD = 9;  (* maximal number of digits written, apart from leading zeros *)
>>   VAR e, i: INTEGER; sign: CHAR; x0: REAL;
>>     d: ARRAY maxD OF CHAR;  (* digits *)
>> 
>>   PROCEDURE seq (VAR W: Texts.Writer; ch: CHAR; n: INTEGER);
>>   (* Write a sequence of n characters ch *)
>>   BEGIN WHILE n > 0 DO Texts.Write(W, ch); DEC(n) END 
>>   END seq;
>> 
>>   PROCEDURE dig(VAR W: Texts.Writer; d: ARRAY OF CHAR; VAR i: INTEGER; n: INTEGER);
>>   (* Write n digits taken from string d *)
>>   BEGIN
>>     WHILE n > 0 DO
>>       DEC(i); Texts.Write(W, d[i]); DEC(n)
>>     END 
>>   END dig;
>> 
>> BEGIN e := ASR(ORD(x), 23) MOD 100H;  (*binary exponent*)
>>   IF k < 0 THEN k := 0 END;
>>   IF e = 0 THEN seq(W, " ", n-k-2); Texts.Write(W, "0"); seq(W, " ", k+1) 
>>   ELSIF e = 255 THEN Texts.WriteString(W, " NaN"); seq(W, " ", n-4)
>>   ELSE e := (e - 127) * 77 DIV 256;  (* decimal exponent *)
>>     IF x < 0.0 THEN sign := "-"; x := -x ELSE sign := " " END;
>>     IF e >= 0 THEN (*x >= 1.0, 77/256 = log 2*) x := x/Ten(e)
>>     ELSE (*x < 1.0*) x := Ten(-e) * x 
>>     END;
>>     IF x >= 10.0 THEN x := 0.1*x; INC(e) END; 
>>     (* 1 <= x < 10 *)
>>     IF k+e >= maxD-1 THEN k := maxD-1-e
>>     ELSIF k+e < 0 THEN k := -e; x := 0.0 
>>     END;
>>     x0 := Ten(k+e); x := x0*x + 0.5; 
>>     IF x >= 10.0*x0 THEN INC(e) END; 
>>     (*e = no. of digits before decimal point*) 
>>     INC(e); i := k+e; Convert(x, i, d); 
>>     IF e > 0 THEN
>>       seq(W, " ", n-e-k-2); Texts.Write(W, sign); dig(W, d, i, e);
>>       Texts.Write(W, "."); dig(W, d, i, k) 
>>     ELSE 
>>       seq(W, " ", n-k-3); Texts.Write(W, sign); Texts.Write(W, "0"); Texts.Write(W, ".");
>>       seq(W, "0", -e); dig(W, d, i, k+e) 
>>     END
>>   END
>> END WriteRealFix;
>> 
>> -----------------
>> 
>> The screenprint below shows the TRAP 7 produced by Text.WriteRealFix in DOS Oberon V1.4.
>> The present versions of Texts.WriteReal and Texts.WriteRealFix should show a TRAP in similar cases, imho.
>> 
>> 
>> <Schermafbeelding 2020-10-03 om 17.30.09.png>
>> 
>> --
>> Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems
>> https://lists.inf.ethz.ch/mailman/listinfo/oberon
> 
> --
> Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems
> https://lists.inf.ethz.ch/mailman/listinfo/oberon



More information about the Oberon mailing list