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

时间:2018-09-20 20:41:48

标签: c# floating-point

通常,您可以使用以下公式将值input从一个范围[inputMin, inputMax]转换为另一个范围[outputMin, outputMax]

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

但是如果inputMax = double.MaxValueinputMin = double.MinValue怎么办?然后inputMax - inputMin == double.PositiveInfinity,整个公式就会变得毫无用处。

在以下限制下,是否有更好的方法进行这种转换?

  • 完全在数据类型的限制范围内
  • 用于任意范围
  • 如果inputMax >= outputMaxinputMin <= outputMin

2 个答案:

答案 0 :(得分:2)

为避免lista = ['479 000', '350 000'] lista = list(map(lambda x: int(''.join((x.split()))), lista)) 溢出到无穷大,请按inputMax - inputMin缩放input, inputMin, inputMax。除了一些较小的值外,这应该是准确的:设置了最低有效位的子法线。

/2

@aka.nice对下面改进的精度方法的确定局限性。待会儿再审核。


保持精度是棘手的,但是以FP解释的方式以x解释的方式形成interpolation很有优势。主要思想不是从x = input/2; // scale `input` also x0 = inputMin/2; x1 = inputMax/2; dx = x1 - x0; 中形成一些小的y,因为在乘法之后会进行加法运算。由于取消,任何加法/减法都可能导致精度的严重损失。

y-mx*b

x截距y = (x - x_intercept)*slope 有其自身的问题。如果可能,应该在扩展数学中完成。除非我们可以利用,否则对于现实生活中的应用,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. 等特殊情况可以根据需要通过测试进行处理。

此方法类似于@aka.nice。然而,代码看起来不是中间的outputMin == outputMax,而是计算x截距。

答案 1 :(得分:0)

您可以做的是绕中间旋转:

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操作会导致正确的输出...