C# 在给定数据类型的限制范围内,将值从一个任意范围映射到另一个任意范围
通常,您可以使用以下公式将一个值C# 在给定数据类型的限制范围内,将值从一个任意范围映射到另一个任意范围,c#,floating-point,C#,Floating Point,通常,您可以使用以下公式将一个值input从一个范围[inputMin,inputMax]转换为另一个范围[outputMin,outputMax]: output = (input-inputMin) * ((outputMax-outputMin)/(inputMax-inputMin)) + outputMin 但是如果inputMax=double.MaxValue和inputMin=double.MinValue呢?然后,inputMax-inputMin==double.Posit
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您可以做的是围绕中间旋转:
然后,通过中间输入范围:inputMiddle = 0.5*inputMax + 0.5*inputMin
计算输入距离中间有多远(介于-1和1之间): 然后,通过输出中间值和输出中间值,将此报告给输出inputMidRange = 0.5*inputsMax - 0.5*inputMin
正如Eric所指出的,如果某些区间边界已经低于正常值,这可能会出现下溢问题,但即使降至最小低于正常值的两倍,这仍应有效 编辑 上面的公式是关于溢出的答案,但在操作output = howFar * outputMidRange + outputMiddle
中仍然存在灾难性取消的问题(input inputMiddle)
例如,为了将
映射到[0,double.MaxValue]
所有的“小”值都将投影到[0,double.MaxValue/2]
,尽管一个简单的outputMin
操作将导致正确的输出…为了避免input/2
溢出到无穷大,缩放inputMax-inputMin
input,inputMin、inputMax均由
完成。除了一些小值外,这是准确的:具有最低有效位集的次法线/2
x = input/2; // scale `input` also x0 = inputMin/2; x1 = inputMax/2; dx = x1 - x0;
明确了以下改进精度方法的限制。稍后我们将进行回顾
保持精度是一件棘手的事情,但以x-解释的方式形成具有FP-math的优势。主要思想不是从
中形成一些小的y-mx*b
,因为这会在乘法后进行加法。任何加法/减法都可能由于取消而严重损失精度y
x-intercepty = (x - x_intercept)*slope
有自己的问题集。如果可能,这应该在扩展数学中完成。除此之外,我们可以利用现实生活中的应用,x_intercept=(x1*y2-x2*y1)/(y2-y1)
预计不会是极端的,因此在最重要的时候(x_截距
会很小),我们可以在y
中节省一些精度 特殊情况,如x-x_截距
,可以根据需要通过测试进行处理outputMin==outputMax
这种方法类似于。然而,代码不是中间的
,而是用来计算x-截距。您可能可以使用它。最简单的方法是通过NuGet。已经有好几年没有更新了,但它应该仍然可以正常工作。正如我所说的,我希望保持给定的数据类型。主要是出于性能方面的原因,但也因为我只是对手头的问题感兴趣。如果不受数据类型的限制,您就不能停留在数据类型内。您要求的是任意精度,很明显,这将需要超过x
双精度的64位(在某些情况下是80位)。我不要求任意精度。范围越来越小(检查限制),因此无论如何都会损失精度。因为范围越来越小,我很确定应该有一个解决问题的办法。一个单一的比例因子是不够的
可能很大,比如说大约2^1000,而inputMax-inputMin
可能很小,比如说大约2^1000。然后,比例因子大约为2^-2000,这太小,无法用IEEE 64位二进制浮点格式表示。因此,一个解决方案将涉及至少两个缩放。不应该将outputMax-outputMin
也缩放为0.5,否则input
看起来是错误的?@chux,不,不应该缩放。它不会溢出。到中间的距离不能超过input-inputmide
。它不能超过inputMidRange
,因为整个范围不能超过double.MaxValue
。您可以检查2*double.MaxValue
,inputMin->howFar=-1->outputMidRange+outputmidle=outputMin
,inputmidle->howFar=0->outputmidle
.NMDV,但建议的inputMax->howFar=1->+outputMidRange+outputmidle->outputMax
输入缩放并不是为了防止溢出。保持规模是我的错误。我也试图确定为什么DV.x_截取比x_中间wrt取消更健壮,但我将尝试展示一些棘手的情况,当我们不使用扩展精度时……在
中输入并将其映射到[-double.MaxValue,double.MaxValue]
,将拒绝x_截取到负无穷大(溢出)[double.MaxValue/4*3,double.MaxValue]
y = (x - x_intercept)*slope
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.