Отображение одного диапазона номеров в другой - PullRequest
0 голосов
/ 28 октября 2018

Я пытаюсь отобразить один диапазон десятичных чисел на другой.В приведенном ниже примере это диапазон 0,0 -> 2,0, сопоставленный с 0,0 -> 0,8.Я не могу заставить выходной диапазон когда-либо заканчиваться на 0,8 - он останавливается на 0,722.Я думаю, что проблема в том, как вычисляется переменная масштаба, но я не уверен, как это исправить.Кто-нибудь может увидеть, где я иду не так?

function myscale (num, in_min, in_max, out_min, out_max, factor)
{
	// number map
	var scale = Math.max(0.0, num - in_min) / (in_max - in_min);

	// calculate easing curve
	var r = out_min + (Math.pow(scale, factor) * (out_max - out_min));

	// 64-bit floating point representation fix
	r = parseFloat(r.toFixed(10));

	// return mapped scale number
	return r;
}

var text = "";
var i;

for (i = 0.0; i <= 2.0; i = i + 0.1)
{ 
    text += myscale(i, 0.0, 2.0, 0.0, 0.8, 2) + "<br />";
}

document.getElementById("demo").innerHTML = text;
<!DOCTYPE html>
<html>
<body>

<b>Numbers mapped from 0 to 0.8</b>

<p id="demo"></p>

</body>
</html>

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

Нецелые значения не должны использоваться для управления циклом, если вы правильно не создали цикл для арифметики с плавающей точкой.Как правило, проще реализовать циклическое управление с целочисленной арифметикой.(Когда в управлении циклом используются нецелые значения, ошибки округления с плавающей точкой могут привести к тому, что значение итератора будет немного выше или ниже конечного значения в итерации, когда в идеале оно будет равно конечному значению. Это позволяет точно контролировать, какая итерацияцикл заканчивается сложным.)

Для случая в вопросе, где мы хотим выполнить итерацию на одну десятую, простое решение состоит в том, чтобы масштабировать до 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.)

0 голосов
/ 28 октября 2018

Вы можете использовать другой подход и выполнять итерации до тех пор, пока значение не станет меньше требуемого значения.Наконец, возьмите значение greates и вызовите функцию с этим значением вне цикла for со значением без ошибок с плавающей запятой при добавлении чисел.

function myscale (num, in_min, in_max, out_min, out_max, factor) {
	// number map
	var scale = Math.max(0.0, num - in_min) / (in_max - in_min);

	// calculate easing curve
	var r = out_min + (Math.pow(scale, factor) * (out_max - out_min));

	// 64-bit floating point representation fix
	r = parseFloat(r.toFixed(10));

	// return mapped scale number
	return r;
}

var text = "";
var i;

for (i = 0; i < 2; i += 0.1) { 
    text += myscale(i, 0, 2, 0, 0.8, 2) + "<br />";
}
text += myscale(2, 0, 2, 0, 0.8, 2) + "<br />";

document.getElementById("demo").innerHTML = text;
<b>Numbers mapped from 0 to 0.8</b>
<p id="demo"></p>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...