<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class="">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. </div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">I used the following test module:</div><div class=""><br class=""></div><div class=""><div class="">MODULE Test1;</div><div class=""> IMPORT In, Fonts, Texts, Oberon;</div><div class=""> VAR W: Texts.Writer; </div><div class=""><br class=""></div><div class=""> PROCEDURE Do*;</div><div class=""> VAR x: REAL; k: INTEGER;</div><div class=""> BEGIN</div><div class=""> In.Open; In.Real(x); In.Int(k);</div><div class=""> IF In.Done THEN </div><div class=""> Texts.WriteReal(W, x, n); Texts.WriteRealFix(W, x, n, k)</div><div class=""> ELSE </div><div class=""> Texts.WriteString(W, "Parameter error")</div><div class=""> END;</div><div class=""> Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)</div><div class=""> END Do;</div><div class=""><br class=""></div><div class="">BEGIN Texts.OpenWriter(W); Texts.SetFont(W, Fonts.This("Courier10.Fnt"))</div><div class="">END Test1.Do^</div></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">Example input & output:</div><div class="">x k Texts.WriteReal Texts.WriteRealFix Texts.WriteRealFix (Ob.-90)</div><div class="">1554.70E5 1 1.554700E+08 155470003.2 155470000.<br class="">1554.70E5 2 1.554700E+08 21474836.47 155470000.</div><div class="">1554.70E6 0 1.554700E+09 1554700032. Trap 7 Overflow</div><div class="">1.0E8 1 1.000000E+08 100000000.0 100000000.0</div><div class="">1.0E9 1 1.000000E+09 214748364.7 Trap 7 Overflow</div><div class="">1.0E9 0 1.000000E+09 1000000000.0 Trap 7 Overflow</div><div class="">221500000.0 2 -2.079967E+08 -21474836.47 221499984.</div><div class=""><br class=""></div><div class="">In the overview of results below I call a result containing the digits 2147483647</div><div class="">'overflow (Mersenne)' or 'overflow (-Mersenne)' because 2147483647 = MAX(INTEGER) = 2^(32)-1, which is a Mersenne prime. </div><div class=""><br class=""></div><div class="">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.</div><div class="">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.</div><div class=""><br class=""></div><div class="">The present Oberon-07 versions of these procedures never trap, but regularly give wrong results, due to silent overflow of the FLOOR(x) function.</div><div class=""><br class=""></div><div class="">x k Texts.WriteReal Texts.WriteRealFix Texts.WriteRealFix (Ob.-90) <br class="">1554.70E5 1 OK OK OK (9 digits, no decimal)<br class="">1554.70E5 2 OK overflow (Mersenne) OK (9 digits, no decimal)<br class="">1554.70E6 0 OK OK Trap 7 Overflow</div><div class="">1554.70E7 0 OK overflow (Mersenne) Trap 7 Overflow</div><div class="">1.55470E5 4 OK OK OK (9 digits, no decimal)<br class="">1.55470E5 5 OK overflow (Mersenne) OK (9 digits, no decimal)<br class="">1.0E8 1 OK OK OK (9 digits)<br class="">1.0E9 1 OK overflow (Mersenne) Trap 7 Overflow<br class="">1.0E9 0 OK OK Trap 7 Overflow<br class="">221500000.0 3 overflow (negative) overflow (-Mersenne) OK (9 digits, no decimal)<br class="">221500000.0 2 overflow (negative) overflow (-Mersenne) OK (9 digits, no decimal)<br class="">221500000.0 1 overflow (negative) overflow (negative) OK (9 digits, no decimal)<br class="">221500000.0 0 overflow (negative) overflow (negative) OK (9 digits, no decimal)<br class="">22150000.0 0 OK OK OK<br class="">22150000.0 1 OK OK OK<br class="">22150000.0 2 OK overflow (Mersenne) OK (1 decimal digit)<br class="">22150000.0 11 OK overflow (Mersenne) OK (1 decimal digit)<br class="">2215000.0 2 OK OK OK<br class="">2215000.0 3 OK overflow (Mersenne) OK (2 decimal digits)<br class="">2147483647.0 0 overflow (-1.0) overflow ( -1.) Trap 7 Overflow <br class="">2147483648.0 1 overflow ( 0 ) overflow ( 0 ) Trap 7 Overflow <br class="">2147483649.0 0 overflow ( 1.0) overflow ( 1.) Trap 7 Overflow<br class="">214748364.0 0 OK OK OK<br class="">2147484000.0 0 overflow (352.) overflow (352.) Trap 7 Overflow<br class="">214748400.0 0 overflow (-Mersenne) overflow (-Mersenne) OK (9 digits, no decimal)<br class="">214740000.0 0 OK OK OK (9 digits, no decimal)<br class="">214740000.0 1 OK OK OK (9 digits, no decimal)<br class="">214740000.0 2 OK overflow (Mersenne) OK (9 digits, no decimal)<br class="">0.000012345 9 OK .0000123 0.000012345)<br class="">0.000012345 11 OK .0000123 0.00001234500)<br class="">0.000012345 13 OK .0000123 0.0000123449984)<br class="">0.000012345 14 OK .0000123 0.0000123449984)<br class="">--------------<br class=""> Mersenne: 2147483647 (with decimal point)<br class="">-Mersenne: -2147483647 (with decimal point)<br class=""><br class=""><br class="">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.</div><div class=""><br class=""></div><div class="">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)...</div><div class=""><br class=""></div><div class="">Does anyone have a suggestion? Could it be that the TRAP7 is not due to integer overflow but to an array index overflow?<br class=""><br class="">--</div><div class="">Hans</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">-----------</div><div class=""><br class=""></div><div class="">(* Below is my port to Oberon-07 of Jürg Gutknecht's original Oberon-90 </div><div class=""> version of WriteRealFix from the book Project Oberon (1992) *)</div><div class=""><br class=""></div><div class=""> PROCEDURE Ten(n: INTEGER): REAL;<br class=""> VAR t, p: REAL;<br class=""> BEGIN t := 1.0; p := 10.0; (*compute 10^n *)<br class=""> WHILE n > 0 DO<br class=""> IF ODD(n) THEN t := p * t END ;<br class=""> p := p*p; n := n DIV 2<br class=""> END ;<br class=""> RETURN t<br class=""> END Ten;<br class=""><br class=""> PROCEDURE Convert (x: REAL; n: INTEGER; VAR d: ARRAY OF CHAR);<br class=""> (* Convert REAL x to a string of n characters.</div><div class=""> This version still is unsatisfactory, because FLOOR overflows silently.</div><div class=""> *)<br class=""> VAR i, k: INTEGER;<br class=""> BEGIN<br class=""> i := FLOOR(x); k := 0;<br class=""> WHILE k < n DO<br class=""> d[k] := CHR(i MOD 10 + 30H); i := i DIV 10; INC(k)<br class=""> END <br class=""> END Convert;<br class=""></div><div class=""><br class=""></div><div class=""> PROCEDURE WriteRealFix* (VAR W: Texts.Writer; x: REAL; n, k: INTEGER);<br class=""> CONST maxD = 9; (* maximal number of digits written, apart from leading zeros *)<br class=""> VAR e, i: INTEGER; sign: CHAR; x0: REAL;<br class=""> d: ARRAY maxD OF CHAR; (* digits *)<br class=""><br class=""> PROCEDURE seq (VAR W: Texts.Writer; ch: CHAR; n: INTEGER);<br class=""> (* Write a sequence of n characters ch *)<br class=""> BEGIN WHILE n > 0 DO Texts.Write(W, ch); DEC(n) END <br class=""> END seq;<br class=""><br class=""> PROCEDURE dig(VAR W: Texts.Writer; d: ARRAY OF CHAR; VAR i: INTEGER; n: INTEGER);<br class=""> (* Write n digits taken from string d *)<br class=""> BEGIN<br class=""> WHILE n > 0 DO<br class=""> DEC(i); Texts.Write(W, d[i]); DEC(n)<br class=""> END <br class=""> END dig;<br class=""> <br class=""> BEGIN e := ASR(ORD(x), 23) MOD 100H; (*binary exponent*)<br class=""> IF k < 0 THEN k := 0 END;<br class=""> IF e = 0 THEN seq(W, " ", n-k-2); Texts.Write(W, "0"); seq(W, " ", k+1) <br class=""> ELSIF e = 255 THEN Texts.WriteString(W, " NaN"); seq(W, " ", n-4)<br class=""> ELSE e := (e - 127) * 77 DIV 256; (* decimal exponent *)<br class=""> IF x < 0.0 THEN sign := "-"; x := -x ELSE sign := " " END;<br class=""> IF e >= 0 THEN (*x >= 1.0, 77/256 = log 2*) x := x/Ten(e)<br class=""> ELSE (*x < 1.0*) x := Ten(-e) * x <br class=""> END;<br class=""> IF x >= 10.0 THEN x := 0.1*x; INC(e) END; <br class=""> (* 1 <= x < 10 *)<br class=""> IF k+e >= maxD-1 THEN k := maxD-1-e<br class=""> ELSIF k+e < 0 THEN k := -e; x := 0.0 <br class=""> END;<br class=""> x0 := Ten(k+e); x := x0*x + 0.5; <br class=""> IF x >= 10.0*x0 THEN INC(e) END; <br class=""> (*e = no. of digits before decimal point*) <br class=""> INC(e); i := k+e; Convert(x, i, d); <br class=""> IF e > 0 THEN<br class=""> seq(W, " ", n-e-k-2); Texts.Write(W, sign); dig(W, d, i, e);<br class=""> Texts.Write(W, "."); dig(W, d, i, k) <br class=""> ELSE <br class=""> seq(W, " ", n-k-3); Texts.Write(W, sign); Texts.Write(W, "0"); Texts.Write(W, ".");<br class=""> seq(W, "0", -e); dig(W, d, i, k+e) <br class=""> END<br class=""> END<br class=""> END WriteRealFix;</div><div class=""><br class=""></div><div class="">-----------------</div><div class=""><br class=""></div><div class="">The screenprint below shows the TRAP 7 produced by Text.WriteRealFix in DOS Oberon V1.4.</div><div class="">The present versions of Texts.WriteReal and Texts.WriteRealFix should show a TRAP in similar cases, imho.</div><div class=""><br class=""></div><div class=""> <img apple-inline="yes" id="6D9820F3-8FF5-487E-AAD1-5F95827B0CF9" width="720" height="539" src="cid:6B14FA56-131A-435D-905A-6EFCBA6653F7@fritz.box" class=""></div><div class=""><br class=""></div></body></html>