代码之家  ›  专栏  ›  技术社区  ›  D.R.

在给定数据类型的限制内将值从一个任意范围映射到另一个范围

  •  2
  • D.R.  · 技术社区  · 8 年前

    通常您可以转换一个值 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 回复  |  直到 8 年前
        1
  •  2
  •   chux    8 年前

    避免 inputMax - inputMin 溢出到无穷大,缩放 input, inputMin, inputMax ,全部由 /2 . 除了一些很小的值:具有最低有效位集的次法线之外,这一点应该是准确的。

    x = input/2;  // scale `input` also
    x0 = inputMin/2;
    x1 = inputMax/2;
    dx = x1 - x0;
    

    @aka.nice 识别良好 限制 改进后的精度方法如下。以后再复习。


    保持精确是很难的,但是 interpolation 在x-解释方式中,FP-math具有优势。主要的想法是不要形成一些小的 y 从 y-mx*b 就像乘法后的加法一样。任何加法/减法都容易由于取消而导致严重的精度损失。

    y = (x - x_intercept)*slope
    

    x截距 x_intercept = (x1*y2 - x2*y1)/(y2 - y1) 有自己的问题。如果可能的话,这应该用扩展数学来完成。除此之外,我们可以在实际应用中利用 x_intercept 不可能是极端的,所以我们在 x - x_intercept 最重要的时候( 是的 会很小)。

    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截距。

        2
  •  0
  •   aka.nice    8 年前

    你能做的就是绕着中间转:

    inputMiddle = 0.5*inputMax + 0.5*inputMin
    

    然后,通过输入的中间范围:

    inputMidRange = 0.5*inputsMax - 0.5*inputMin
    

    计算输入距离中间的距离(介于-1和1之间):

    howFar = (input-inputMiddle) / inputMidRange
    

    然后,通过输出中间和输出中间范围,将其均匀地报告给输出

    output = howFar * outputMidRange + outputMiddle
    

    正如Eric所指出的,如果某些区间边界已经是次正规的,这可能会受到下溢问题的影响,但即使是最小的次正规的两倍,这仍然应该有效。

    编辑

    上面的公式是关于溢出的一个解答,但在操作中仍然存在灾难性的取消问题 (input-inputMiddle) .

    例如映射 [0,double.MaxValue] ,到 [0,double.MaxValue/2] 所有的“小”值将被投射到 outputMin ,尽管 input/2 操作将导致正确的输出。。。