<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Thu, May 19, 2016 at 4:31 AM, Nicola Salmoria via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><span class="">&gt; * What is your evaluation of the proposal?<br>
<br>
</span>+1. It might not be pretty, but it is an improvement.<br>
<br>
The Proposed Solution doesn&#39;t compile with development snapshot 05-09<br>
(SE-0067 hasn&#39;t landed yet, IIRC).</blockquote><div><br></div><div>An (almost complete) implementation of SE-0067 floating point protocols have landed in master; there have been a few nips and tucks as compared to SE-0067 itself and I&#39;ve created a version of the code in this proposal to compile accordingly. You can see it here:</div><div><a href="https://github.com/xwu/swift/tree/SE-0050">https://github.com/xwu/swift/tree/SE-0050</a><br></div><div><br></div><div>There&#39;s is a wrinkle in the proposal worth discussing here during the review period, and you&#39;ll notice a difference related to it if you compare the code above to what&#39;s in this proposal. What should be the type of the internal step counter `_step`? [BigInt types not currently in the stdlib are out of scope.]</div><div><br></div><div>As proposed, _step has the same floating point type as the stride size. Originally, Erica proposed an Int step counter, and I too thought that it was plenty good enough. Functionally, 2^64 iterations is effectively an infinite loop on modern machines. Moreover, strides have always been intended to conform to Collection (though they do not in fact do so), and by analogy to arrays and other collection types a maximum of Int elements is quite consistent.</div><div><br></div><div>However, Stephen Canon has pointed out that using an Int step counter means that the maximum number of steps on 32-bit systems is dramatically lower. On the other hand, while I can readily accept that there are meaningful loops with 2^32 iterations, I&#39;m unsure whether there are as many use cases for floating point strides with more than 2^32 steps.</div><div><br></div><div>More concerning, though, there&#39;s a hit in performing integer-to-floating-point conversion at every iteration. Recently, I tried to compare the performance of floating point stride using an Int step counter or a floating point one. What I had handy at the time was Swift 2.2; I backported both solutions and measured performance using XCTest framework (making sure to use the result of the computation at each iteration to prevent the compiler from optimizing away my loop). I found that, at least in the several times that I performed the test, one solution was not consistently faster than the other. I&#39;m not sure whether this can be generalized to 32-bit platforms, and I did not dig to see if one or both solutions was compiled to make use of fused multiply-add.</div><div><br></div><div>What one gains from using an Int step counter over a floating point one is a consistent maximum number of steps regardless of the floating point type of the endpoints. Less pertinently, when it comes to implementation detail, some of the computations are a little more elegant (for instance, precondition total number of steps less than Int.max rather than testing whether the ULP of the total number of steps is less than or equal to one).</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">I hacked it to get it to compile, and<br>
tested it with two problematic cases:<br>
<br>
Test #1:<br>
let expected = Array(stride(from: 10, through: 20, by: 1).map { Float($0) *<br>
0.1 })<br>
// [1.0, 1.10000002, 1.20000005, 1.30000007, 1.39999998, 1.5, 1.60000002,<br>
1.70000005, 1.80000007, 1.89999998, 2.0]<br>
<br>
let actual = Array(stride(from: Float(1.0), through: 2.0, by: 0.1))<br>
// old result: one iteration less than expected<br>
// [1.0, 1.10000002, 1.20000005, 1.30000007, 1.4000001, 1.50000012,<br>
1.60000014, 1.70000017, 1.80000019, 1.90000021]<br>
// new result: correct<br>
// [1.0, 1.10000002, 1.20000005, 1.29999995, 1.39999998, 1.5, 1.60000002,<br>
1.70000005, 1.79999995, 1.9000001, 2.0]<br>
<br>
Test #2:<br>
let expected = Array(stride(from: 10, to: 100, by: 9).map { Float($0) * 0.1 })<br>
// [1.0, 1.89999998, 2.79999995, 3.70000005, 4.5999999, 5.5, 6.4000001,<br>
7.30000019, 8.19999981, 9.10000038]<br>
<br>
let actual = Array(stride(from: Float(1.0), to: 10.0, by: 0.9))<br>
// old result: one iteration more than expected<br>
// [1.0, 1.89999998, 2.79999995, 3.69999981, 4.5999999, 5.5, 6.4000001,<br>
7.30000019, 8.19999981, 9.09999943, 9.99999905]<br>
// new result: correct<br>
// [1.0, 1.89999998, 2.79999995, 3.69999981, 4.5999999, 5.5, 6.39999962,<br>
7.29999971, 8.19999981, 9.09999943]<br>
<br>
So the proposed solution works as advertised.<br>
<br>
I would suggest to include two cases like the above in the test suite.<br>
<span class=""><br>
<br>
&gt; * Is the problem being addressed significant enough to warrant a change to<br>
Swift?<br>
<br>
</span>Yes. This is a higher level abstraction, so it&#39;s important that it<br>
guarantees the best possible accuracy. The current generic implementation<br>
gives unacceptably surprising results.<br>
<span class=""><br>
&gt; * Does this proposal fit well with the feel and direction of Swift?<br>
<br>
</span>Yes. It nicely follows the principle of least astonishment.<br>
<span class=""><br>
&gt; * If you have used other languages or libraries with a similar feature,<br>
how do you feel that this proposal compares to those?<br>
<br>
</span>I always flat-out avoided floating point loops in other languages, fearing<br>
that they would be unreliable. It&#39;s good that with this change it&#39;ll be<br>
possible to use them with more confidence.<br>
<span class=""><br>
&gt; * How much effort did you put into your review? A glance, a quick reading,<br>
or an in-depth study?<br>
<br>
</span>careful reading, tested the proposed solution, and read and contributed to<br>
the relevant threads.<br>
<br>
--<br>
Nicola<br>
<div class=""><div class="h5"><br>
<br>
_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</div></div></blockquote></div><br></div></div>