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
    
    然后,通过中间输入范围:

    inputMidRange = 0.5*inputsMax - 0.5*inputMin
    
    计算输入距离中间有多远(介于-1和1之间):

    然后,通过输出中间值和输出中间值,将此报告给输出

    output = howFar * outputMidRange + outputMiddle
    
    正如Eric所指出的,如果某些区间边界已经低于正常值,这可能会出现下溢问题,但即使降至最小低于正常值的两倍,这仍应有效

    编辑

    上面的公式是关于溢出的答案,但在操作
    (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
    ,因为这会在乘法后进行加法。任何加法/减法都可能由于取消而严重损失精度

    y = (x - x_intercept)*slope
    
    x-intercept
    x_intercept=(x1*y2-x2*y1)/(y2-y1)
    有自己的问题集。如果可能,这应该在扩展数学中完成。除此之外,我们可以利用现实生活中的应用,
    x_截距
    预计不会是极端的,因此在最重要的时候(
    y
    会很小),我们可以在
    x-x_截距
    中节省一些精度

    特殊情况,如
    outputMin==outputMax
    ,可以根据需要通过测试进行处理


    这种方法类似于。然而,代码不是中间的
    x
    ,而是用来计算x-截距。

    您可能可以使用它。最简单的方法是通过NuGet。已经有好几年没有更新了,但它应该仍然可以正常工作。正如我所说的,我希望保持给定的数据类型。主要是出于性能方面的原因,但也因为我只是对手头的问题感兴趣。如果不受数据类型的限制,您就不能停留在数据类型内。您要求的是任意精度,很明显,这将需要超过
    双精度的64位(在某些情况下是80位)。我不要求任意精度。范围越来越小(检查限制),因此无论如何都会损失精度。因为范围越来越小,我很确定应该有一个解决问题的办法。一个单一的比例因子是不够的
    inputMax-inputMin
    可能很大,比如说大约2^1000,而
    outputMax-outputMin
    可能很小,比如说大约2^1000。然后,比例因子大约为2^-2000,这太小,无法用IEEE 64位二进制浮点格式表示。因此,一个解决方案将涉及至少两个缩放。不应该将
    input
    也缩放为0.5,否则
    input-inputmide
    看起来是错误的?@chux,不,不应该缩放。它不会溢出。到中间的距离不能超过
    inputMidRange
    。它不能超过
    double.MaxValue
    ,因为整个范围不能超过
    2*double.MaxValue
    。您可以检查
    inputMin->howFar=-1->outputMidRange+outputmidle=outputMin
    inputmidle->howFar=0->outputmidle
    inputMax->howFar=1->+outputMidRange+outputmidle->outputMax
    .NMDV,但建议的
    输入缩放并不是为了防止溢出。保持规模是我的错误。我也试图确定为什么DV.x_截取比x_中间wrt取消更健壮,但我将尝试展示一些棘手的情况,当我们不使用扩展精度时……在
    [-double.MaxValue,double.MaxValue]
    中输入并将其映射到
    [double.MaxValue/4*3,double.MaxValue]
    ,将拒绝x_截取到负无穷大(溢出)
    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.