Сопоставить значение из одного произвольного диапазона с другим в пределах данного типа данных - PullRequest
0 голосов
/ 20 сентября 2018

Обычно вы можете преобразовать значение input из одного диапазона [inputMin, inputMax] в другой диапазон [outputMin, outputMax], используя следующую формулу:

output = (input-inputMin) * ((outputMax-outputMin)/(inputMax-inputMin)) + outputMin

Но что если inputMax = double.MaxValue и inputMin = double.MinValue?Затем inputMax - inputMin == double.PositiveInfinity и вся формула идет вниз.

Есть ли лучший способ сделать это преобразование при следующих ограничениях?

  • полностью в пределах типа данных
  • для произвольных диапазонов
  • , если inputMax >= outputMax и inputMin <= outputMin

Ответы [ 2 ]

0 голосов
/ 22 сентября 2018

Чтобы избежать переполнения inputMax - inputMin до бесконечности, масштабируйте input, inputMin, inputMax, все по /2.Ожидается, что он будет точным, за исключением некоторых небольших значений: субнормалей с установленным наименьшим значащим битом.1011 * к улучшенному подходу точности ниже.Мы рассмотрим позже.


Сложно поддерживать точность, но формирование интерполяции в интерпретации по x имеет преимущества с математикой FP.Основная идея не в том, чтобы сформировать какой-нибудь маленький y из y-mx*b, как это делает сложение после умножения.Любое сложение / вычитание может привести к серьезной потере точности из-за отмены.

y = (x - x_intercept)*slope

У x-intercept x_intercept = (x1*y2 - x2*y1)/(y2 - y1) есть свой собственный набор проблем.По возможности это должно быть сделано в расширенной математике.За исключением этого, мы можем воспользоваться тем, что для реальных приложений значение x_intercept не должно быть экстремальным и, таким образом, сохраняет нам некоторую точность в x - x_intercept, когда это имеет наибольшее значение (y будет небольшим).

y0 = outputMin/2;
y1 = outputMax/2;
dy = y1 - y0;

// Avoid (y1*x0 - y0*x1)/dy to prevent overflow in the multiplications
x_intercept = y1/dy*x0 - y0/dy*x1;
y = (x - x_intercept)/dx*dy;
output = 2*y; // scale `output` at the end.

Особые случаи, такие как outputMin == outputMax, могут обрабатываться при необходимости с помощью теста.

Этот подход аналогичен @aka.nice.Тем не менее, вместо среднего x код выглядит для вычисления x-перехвата.

0 голосов
/ 21 сентября 2018

Что вы могли бы сделать, это повернуть вокруг середины:

inputMiddle = 0.5*inputMax + 0.5*inputMin

, а затем через средний диапазон ввода:

inputMidRange = 0.5*inputsMax - 0.5*inputMin

вычислить, как далеко от середины находится вход (между-1 и 1):

howFar = (input-inputMiddle) / inputMidRange

затем через выходной средний и выходной средний диапазон, гомотетически сообщите об этом на выход

output = howFar * outputMidRange + outputMiddle

Как отметил Эрик, это может зависеть отпроблемы переполнения, если некоторые из интервалов уже субнормальны, но даже вдвое меньше наименьшего субнормального, это все равно должно работать.

РЕДАКТИРОВАТЬ

Приведенная выше формулировкаответьте о переполнении, но все еще есть проблема катастрофического отмены в операции (input-inputMiddle).

Например, для отображения [0,double.MaxValue], на [0,double.MaxValue/2] все «малые» значения будут спроецированы на outputMin,хотя простая операция input/2 привела бы к правильному выводу ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...