Нецелые значения не должны использоваться для управления циклом, если вы правильно не создали цикл для арифметики с плавающей точкой.Как правило, проще реализовать циклическое управление с целочисленной арифметикой.(Когда в управлении циклом используются нецелые значения, ошибки округления с плавающей точкой могут привести к тому, что значение итератора будет немного выше или ниже конечного значения в итерации, когда в идеале оно будет равно конечному значению. Это позволяет точно контролировать, какая итерацияцикл заканчивается сложным.)
Для случая в вопросе, где мы хотим выполнить итерацию на одну десятую, простое решение состоит в том, чтобы масштабировать до 10, так что 0, 2.0 и 0.1 становятся 0, 20,и 1:
for (var ProxyI = 0; ProxyI <= 20; ProxyI = ProxyI += 1)
{
var RealI = ProxyI / 10.0;
text += myscale(RealI, 0.0, 2.0, 0.0, 0.8, 2) + "<br />";
}
В общем, если мы хотим выполнить итерацию от Start
до End
включительно, на Increment
, где Increment
идеально равномерно делит расстояние от Start
доEnd
но арифметика с плавающей точкой мешает, тогда мы можем использовать:
var NumberOfIntervals = Math.round((End - Start) / Interval);
for (var ProxyI = 0; ProxyI <= NumberOfIntervals; ProxyI = ProxyI + 1)
{
var RealI = Start + I / NumberOfIntervals * (End - Start);
…
}
Конструкция здесь такова, что NumberOfIntervals
задается как целое число интервалов, через которые мы ожидаем итерацию.Затем используется целочисленная арифметика с ProxyI
, увеличиваемая на единицу для подсчета интервалов.Эта целочисленная арифметика не имеет ошибок округления, поэтому ProxyI
правильно подсчитывает интервалы.Затем внутри цикла счетчик ProxyI
масштабируется и переводится в соответствующую точку в интервале от Start
до End
.Эта арифметика будет иметь некоторые ошибки округления, поэтому RealI
часто будет не совсем идеальным числом, но оно будет близко.Ошибки округления влияют только на значение RealI
;они не влияют на счетчик цикла, ProxyI
.Так что цикл считается правильно.(Получение точного числа, как правило, невозможно, так как оно не может быть представлено в формате с плавающей запятой.)
Этот дизайн решает проблему ошибок округления в итераторе, из-за чего он немного выше или ниже концазначение, но это дает дополнительное преимущество, позволяющее избежать сложных ошибок округления по сравнению со многими дополнениями.Ошибки округления ограничены несколькими операциями в Start + I / NumberOfIntervals * (End - Start)
.
(Примечание: я почти никогда не пишу на JavaScript, поэтому не могу убедиться, что приведенный выше код является правильным JavaScript. Также обратите внимание, что окончательное значение RealI
как вычислено выше, может быть не совсем End
, потому что End - Start + Start
в арифметике с плавающей точкой не обязательно приводит к End
.)