turtle-graphicssmallbasic

Small basic turtle MoveTo error "Value was either too large or too small for a Decimal."


This code appears to throw an exception "Value was either too large or too small for a Decimal."

Somehow changing variables y1, y2, x removes an error. For example, y2 from 41 to 38.

How can I fix this?

Turtle.Speed = 10
x = 10
y1 = 42
y2 = 41
Turtle.Angle = 180
Turtle.MoveTo(x, y2)
Turtle.MoveTo(x, y1)

Error trace:

in System.Decimal..ctor(Double value)
in System.Decimal.op_Explicit(Double value)
in Microsoft.SmallBasic.Library.Primitive.op_Implicit(Double value)
in Microsoft.SmallBasic.Library.Turtle.MoveTo(Primitive x, Primitive y)
in _SmallBasicProgram._Main()

The same in both 1.0 and 1.2 versions.


Solution

  • The problem is the SmallBasic (in version 1.2) Primitive-from-double implementation is flawed. Here is how a double is converted to a Primitive.

    new Primitive((Decimal) primitiveDouble);
    

    However, this is an unsafe operation as not all values of a double can be (precisely) represented. In these cases the cast to a Decimal will throw an Exception.

    Here is a trivial way to reproduce such an exception in C#:

    double x = double.MinValue; // [smallest] denormalized value
    decimal f = (decimal)x;
    

    This happens in the MoveTo(x,y) operation which does trigonometry math to turn the MoveTo into a Turn+Move combination. For some inputs (and where the turtle is), such will result in doubles that cannot be [safely] turned into decimal values.

    Using Turn+Move explicitly will avoid the problematic math and thus should avoid the problem - at least in this particular case.

    For reference, here is the decompiled source of MoveTo:

    /// <summary>
    /// Turns and moves the turtle to the specified location.  If the pen is down, it will draw a line as it moves.
    /// </summary>
    /// <param name="x">The x co-ordinate of the destination point.</param>
    /// <param name="y">The y co-ordinate of the destination point.</param>
    public static void MoveTo(Primitive x, Primitive y)
    {
      double d = (double) ((x - Turtle.X) * (x - Turtle.X) + (y - Turtle.Y) * (y - Turtle.Y));
      if (d == 0.0)
        return;
      double num1 = System.Math.Sqrt(d);
      double num2 = System.Math.Acos((double) (Turtle.Y - y) / num1) * 180.0 / System.Math.PI;
      if ((bool) (x < Turtle.X))
        num2 = 360.0 - num2;
      double num3 = num2 - (double) ((int) Turtle.Angle % 360);
      if (num3 > 180.0)
        num3 -= 360.0;
      Turtle.Turn((Primitive) num3); // goes boom here..
      Turtle.Move((Primitive) num1); // ..or here
    }