Маленькая базовая ошибка MoveTo черепахи Значение было либо слишком большим, либо слишком маленьким для десятичного числа.

Этот код выдает исключение «Значение слишком велико или слишком мало для десятичного числа».

Каким-то образом изменение переменных y1, y2, x убирает ошибку. Например, у2 от 41 до 38.

Как я могу это исправить?

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

Трассировка ошибки:

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()

То же самое и в версии 1.0 и в версии 1.2.


person L. Murashov    schedule 07.10.2018    source источник
comment
Возможно, ошибка потери значимости в источнике при преобразовании обратно в примитив: MoveTo(x,y) выполняет некоторые математические операции для создания пары Turn/Move (и значения, сгенерированные при преобразовании в примитив на этих сайтах вызовов). Десятичные значения имеют фиксированную точность, и ctor выдаст исключение, если (предоставленное двойное число) не может быть точно представлено.   -  person user2864740    schedule 08.10.2018
comment
Похоже, это проблема в самом Primitive: new Primitive((Decimal) primitiveDouble) .. независимо от каких-либо недорасходов ..   -  person user2864740    schedule 08.10.2018
comment
@user2864740, так и не понял, почему не со всеми номерами отображается и как это исправить.   -  person L. Murashov    schedule 08.10.2018
comment
Невозможно это исправить (кроме выбора других значений) без исправления типа Smallbasic Primitive. Возможно, удастся избежать проблемы с явным использованием поворота/движения. Вот тривиальный пример неудачной программы на C#: double x = double.MinValue; decimal f = (decimal)x;   -  person user2864740    schedule 08.10.2018
comment
@ user2864740, где я могу увидеть исходный код? (кажется, вы это видите)   -  person L. Murashov    schedule 08.10.2018
comment
Я использовал dotPeek от ReSharper (бесплатный инструмент). Существуют и различные альтернативы.   -  person user2864740    schedule 08.10.2018


Ответы (1)


Проблема в том, что в SmallBasic (в версии 1.2) реализация Primitive-from-double ошибочна. Вот как двойник преобразуется в примитив.

new Primitive((Decimal) primitiveDouble);

Однако это небезопасная операция, поскольку не все значения типа double могут быть (точно) представлены. В этих случаях приведение к Decimal вызовет исключение.

Вот тривиальный способ воспроизвести такое исключение в C#:

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

Это происходит в операции MoveTo(x,y), которая выполняет тригонометрическую математику, чтобы превратить MoveTo в комбинацию Turn+Move. Для некоторых входных данных (и там, где находится черепаха) это приведет к двойному значению, которое нельзя [безопасно] превратить в десятичные значения.

Использование Turn+Move явно позволит избежать проблематичной математики и, следовательно, должно избежать проблемы - по крайней мере, в этом конкретном случае.

Для справки, вот декомпилированный исходный код 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
}
person user2864740    schedule 07.10.2018
comment
Похоже, решение состоит в том, чтобы сделать это таким образом Turtle.Speed ​​= 10 x = 10 y1 = 42 y2 = 41 Turtle.Angle = 180 Turtle.X = x Turtle.Y = y2 Turtle.MoveTo(x, y1). Как вы прямо сказали - person L. Murashov; 08.10.2018
comment
Если/как это работает, то это потому, что остаток от первой черепахи MoveTo очищается. Обратите внимание, что MoveTo учитывает текущую позицию черепахи: и первый вызов MoveTo не установил позицию точно на 10/41. Однако явная установка X/Y черепахи сбрасывает местоположение до целочисленных значений, поэтому второй MoveTo не завершается ошибкой из-за математического недополнения при преобразовании. YMMV. - person user2864740; 08.10.2018
comment
Я очень впечатлен тем, что кто-то начал с черепахи и дошел до диагностики этой ошибки. Его настоящее имя может оставаться загадкой еще много лет. - person Hans Passant; 08.10.2018