[Oberon] Fighting a dragon ...

Hans Klaver hklaver at dds.nl
Thu Jul 11 18:40:57 CEST 2024


Hi all,

The dragon curve is an interesting space filling curve that you can approximate by endlessly folding a slip of paper. Apparently I'm not the only one who is fascinated by it; Don Knuth has one decorating a wall in his home: https://www.youtube.com/watch?v=v678Em6qyzk (and yes, it has a bug).

When making a program to draw dragon curves of different orders for Oberon V5 (Project Oberon 2013) I noticed that odd order dragons look well and healthy but even order dragons degrade into shapeless worms (see the two screenprints on my github page for dragons of order 9 and 10).
The same algorithm in BASIC and Python produces nice dragons of all orders, albeit MUCH slower that my Project Oberon version.

For the source files and screenprints see: 
https://github.com/hansklav/Fractals-in-Oberon-07/tree/main/Dragon-curves

First I suspected that a difference between Oberon's MOD operator versus BASIC's MOD and Python's % operators was the culprit, so I made a function REM to mimic them, but that had no effect.

Then I pinpointed a problem in the way the Oberon V5 compiler handles an expression for certain values of its variables in my code. Please run the following two commands in Oberon V5, and behold that they unexpectedly give different results: 


MODULE TestDragon;   (* hk 11 July 2024 *)
  IMPORT Math, Out;  
  (* For Math.power see: https://github.com/hansklav/Oberon-07/blob/master/Math.Mod *)

  CONST
    pi = 3.1415926535;
    p = 4.0;  (* order of dragon curve *)

  PROCEDURE Correct*;  (* TestDragon.Correct *)   (* correct result for x2 *)
    VAR h, x1, x2: REAL;  s: INTEGER;
  BEGIN
    s := 0;  x1 := 7.499998E-01;
    h := 2.500000E-01;
    Out.String("s  = "); Out.Int(s, 2); Out.String("    h ="); Out.Real(h, 14); Out.Ln;
    x2 := x1 + h * Math.cos( (FLT(s) - p/2.0) * pi/2.0 );  (* <-- expression tested *)
    Out.String("x1 ="); Out.Real(x1, 14); Out.Ln;
    Out.String("x2 ="); Out.Real(x2, 14); Out.Ln;
    Out.Ln
  END Correct;

  PROCEDURE Incorrect*;  (* TestDragon.Incorrect *)   (* incorrect result for x2 *)
    VAR h, x1, x2: REAL;  s: INTEGER;
  BEGIN
    s := 0;  x1 := 7.499998E-01;
    h := Math.power(2.0, -p/2.0);  (* the result for h from Math.power() is correct: *)
    Out.String("s  = "); Out.Int(s, 2); Out.String("    h ="); Out.Real(h, 14); Out.Ln;
    x2 := x1 + h * Math.cos( (FLT(s) - p/2.0) * pi/2.0 );  (* <-- why incorrect result?? *)
    Out.String("x1 ="); Out.Real(x1, 14); Out.Ln;
    Out.String("x2 ="); Out.Real(x2, 14); Out.Ln;
    Out.Ln
  END Incorrect;

END TestDragon.


The first command gives the correct result (0.4999998), which can easily be verified by pasting the following line in https://www.wolframalpha.com/ :
  7.499998E-01 + 2.500000E-01 * cos((0 - 4/2) * pi/2.0)

But why the same expression in the second command gives a wrong result (0.6249998) for the same values of its variables as the first command escapes me.

Could anyone give an explanation??

Because I cannot reproduce this incorrect behaviour using the OBNC compiler, I suspect a compiler error.

Regards,

Hans Klaver






More information about the Oberon mailing list