F#浮点范围是实验性的,可能会被弃用 [英] F# Floating point ranges are experimental and may be deprecated

查看:133
本文介绍了F#浮点范围是实验性的,可能会被弃用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  [1.0 ..我试图做一个小函数来在两个值之间插入一个给定的增量。 0.5 .. 20.0] 

编译器告诉我这已经被弃用了,建议使用ints然后转换为浮动。但是,如果我有一个分数增量,这似乎有点啰嗦 - 我必须将我的开始和结束值除以我的增量,然后再多倍吗? ($!
$ b< p>我曾经在某个地方看到过使用序列综合的方法来做这件事,但是我不记得怎么做。 b $ b

Help,please。

解决方案

TL; DR: F#PowerPack's BigRational 类型是要走的路




点循环



正如很多人指出的那样, float 值不适合循环:




  • 它们具有四舍五入误差,就像十进制中的 1/3 一样,我们不可避免地丢失了所有数字从某个指数开始;
  • 他们确实经历了灾难性抵消(当两个几乎相等的数字相减时,结果四舍五入为零);

  • 他们总是有非零的机器epsilon ,所以错误是增加每个数学运算(除非我们正在添加不同的数字多次,以便错误相互抵消 - 但是这不是循环的情况);
  • 它们在整个范围内具有不同的准确性:范围 [0.0000001 .. 0.0000002] 相当于 [1000000 .. 2000000] 中唯一值的数目;



解决方案



什么能瞬间解决上述问题,整数逻辑。

使用 F#PowerPack ,您可以使用 BigRational 键入:

 打开Microsoft.FSharp.Math 
// [1 .. 1/3 .. 20]
[1N .. 1N / 3N .. 20N]
|> List.map float
|> List.iter(printf%f;)

我自由地将步骤设置为 1/3 ,因为您的问题中的 0.5 实际上具有精确的二进制表示形式0.1 < sub> b ,表示为+1.00000000000000000000000 * 2 -1 ;因此它不会产生任何累积求和错误。

输出:

$ block $ $ b $ 1.000000 ; 1.333333; 1.666667; 2.000000; 2.333333; 2.666667; 3.000000; (跳过) 18.000000; 18.333333; 18.666667; 19.000000; 19.333333; 19.666667; 20.000000;



  // [0.2 .. 0.1 .. 3] 
[ 1N / 5N .. 1N / 10N .. 3N]
|> List.map float
|> List.iter(printf%f;)

输出:


0.200000; 0.300000; 0.400000; 0.500000; (跳过) 2.800000; 2.900000; 3.000000;



结论




  • BigRational 使用整数计算,不比浮点

  • 舍入只发生(转换为 float ,但不在循环中);
  • BigRational 就好像机器的epsilon为零一样;


    有一个明显的限制:不能使用非理性数字如 pi sqrt(2),因为它们没有精确的表示形式。这似乎并不是一个很大的问题,因为通常我们并没有在 之间循环。 [1 .. pi / 2 .. 42] 。如果我们这样做(比如几何计算),通常有一种方法可以减少非理性的部分,例如从弧度切换到度数。




    延伸阅读:


    I'm trying to make a little function to interpolate between two values with a given increment.

    [ 1.0 .. 0.5 .. 20.0 ]
    

    The compiler tells me that this is deprecated, and suggests using ints then casting to float. But this seems a bit long-winded if I have a fractional increment - do I have to divide my start and end values by my increment, then multiple again afterwards? (yeuch!).

    I saw something somewhere once about using sequence comprehensions to do this, but I can't remember how.

    Help, please.

    解决方案

    TL;DR: F# PowerPack's BigRational type is the way to go.


    What's Wrong with Floating-point Loops

    As many have pointed out, float values are not suitable for looping:

    • They do have Round Off Error, just like with 1/3 in decimal, we inevitably lose all digits starting at a certain exponent;
    • They do experience Catastrophic Cancellation (when subtracting two almost equal numbers, the result is rounded to zero);
    • They always have non-zero Machine epsilon, so the error is increased with every math operation (unless we are adding different numbers many times so that errors mutually cancel out -- but this is not the case for the loops);
    • They do have different accuracy across the range: the number of unique values in a range [0.0000001 .. 0.0000002] is equivalent to the number of unique values in [1000000 .. 2000000];

    Solution

    What can instantly solve the above problems, is switching back to integer logic.

    With F# PowerPack, you may use BigRational type:

    open Microsoft.FSharp.Math
    // [1 .. 1/3 .. 20]
    [1N .. 1N/3N .. 20N]
    |> List.map float
    |> List.iter (printf "%f; ")
    

    Note, I took my liberty to set the step to 1/3 because 0.5 from your question actually has an exact binary representation 0.1b and is represented as +1.00000000000000000000000 * 2-1; hence it does not produce any cumulative summation error.

    Outputs:

    1.000000; 1.333333; 1.666667; 2.000000; 2.333333; 2.666667; 3.000000; (skipped) 18.000000; 18.333333; 18.666667; 19.000000; 19.333333; 19.666667; 20.000000;

    // [0.2 .. 0.1 .. 3]
    [1N/5N .. 1N/10N .. 3N]
    |> List.map float
    |> List.iter (printf "%f; ")
    

    Outputs:

    0.200000; 0.300000; 0.400000; 0.500000; (skipped) 2.800000; 2.900000; 3.000000;

    Conclusion

    • BigRational uses integer computations, which are not slower than for floating-points;
    • The round-off occurs only once for each value (upon conversion to a float, but not within the loop);
    • BigRational acts as if the machine epsilon were zero;

    There is an obvious limitation: you can't use irrational numbers like pi or sqrt(2) as they have no exact representation as a fraction. It does not seem to be a very big problem because usually, we are not looping over both rational and irrational numbers, e.g. [1 .. pi/2 .. 42]. If we do (like for geometry computations), there's usually a way to reduce the irrational part, e.g. switching from radians to degrees.


    Further reading:

    这篇关于F#浮点范围是实验性的,可能会被弃用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆