<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
<META NAME="Generator" CONTENT="MS Exchange Server version 6.5.7036.0">
<TITLE>RE: [racket] Inconsistency of `in-range` and numerical issues</TITLE>
</HEAD>
<BODY>
<!-- Converted from text/rtf format -->
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">IIRC, Fortran (70 or 90, I don't remember) already recognized the problem.</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">In Racket notation and for simplicity ignoring the fact that a Fortran-loop includes the finish and that the step may be negative:</FONT></SPAN></P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">(for ((f (in-range float-start float-finish float-step))) body)</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">was translated as sometining like:</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">(let ((begin float-begin) (finish float-finish) (step float-step))</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> (for ((integer (in-range 0 (inexact->exact (floor-or-ceiling (/ (- finish start) step))))))</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> (let ((f (+ begin (* integer step)))) body)))</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">Jos</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">-----Original Message-----</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">From: users [</FONT></SPAN><A HREF="mailto:users-bounces@racket-lang.org"><SPAN LANG="es"><U><FONT COLOR="#0000FF" SIZE=2 FACE="Courier New">mailto:users-bounces@racket-lang.org</FONT></U></SPAN></A><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">] On Behalf Of Neil Toronto</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">Sent: jueves, 26 de febrero de 2015 15:14</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">To: users@racket-lang.org</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">Subject: Re: [racket] Inconsistency of `in-range` and numerical issues</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">On 02/24/2015 01:11 PM, Konrad Hinsen wrote:</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> On 24/02/2015 16:41, Laurent wrote:</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">></FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> I've discovered a rather troubling behaviour when using `in-range` with</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> floating point numbers, which I think is worth knowing in case you</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> hadn't consider the issue before:</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>></FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> On my machine, I get the following:</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>></FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> (length (for/list ([i (in-range .1 .7 .1)]) i)) ; 6</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> (length (for/list ([i (in-range .1 .8 .1)]) i)) ; 8 (!)</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>></FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> But:</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> (length (for/list ([i (in-range 1/10 7/10 1/10)]) i)) ; 6</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> (length (for/list ([i (in-range 1/10 8/10 1/10)]) i)) ; 7</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>></FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>></FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> Would it be a good idea to safe-guard these kinds of cases directly in</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">>> `in-range`?</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">></FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> The problem is an old one that already troubled programmers in the age</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> of Fortran. I suspect there is no reasonable safe-guard, with</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> "reasonable" meaning that it does what people expect in all situations.</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">></FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> The only way to stay out of trouble is to avoid loops defined by an</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> accumulating floating-point value. This means either don't use floats</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> (write the loop over integers or rationals and convert to floats when</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> using the loop index), or don't use accumulation (define your range by</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> two points and the number of subdivisions, rather than the width of</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">> subintervals).</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">I should have chimed in to support this two days ago, but this is </FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">exactly the right answer. Here's what Konrad means by his first </FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">alternative (write the loop over integers or rationals):</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> (length</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> (for*/list ([i (in-range 1 8 1)]</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> [i (in-value (* i 0.1))])</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> i))</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">The second alternative is a little harder to get right because of </FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">fencepost errors [1]. Fortunately, Racket has a library function for it. </FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">Unfortunately, it's buried in `plot/utils`. Here it is in action:</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> > (require (only-in plot/utils linear-seq))</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> > (linear-seq 0.0 1.0 4)</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">'(0.0 0.3333333333333333 0.6666666666666666 1.0)</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> > (linear-seq 0.0 1.0 4 #:start? #f)</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">'(0.14285714285714285 0.42857142857142855 0.7142857142857142 1.0)</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> > (linear-seq 0.0 1.0 4 #:start? #f #:end? #f)</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">'(0.125 0.375 0.625 0.875)</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">I should really move this function into `math/base`.</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">If you must use a flonum step size, do something like this:</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> (define (flonum-range start end step)</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> (define n (exact-ceiling (/ (- end start) step)))</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> (for/list ([i (in-range 0 n)])</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> (+ start (* i step))))</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">To get points with 0.5 ulp error each, which is the best you can do, add </FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">(require math/flonum) to your program and change the loop body to (flfma </FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">step (fl i) start).</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">Arguably, `in-range` should do something like the above when given </FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">floating-point step lengths. I don't know how feasible that is, though.</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">Neil</FONT> <FONT SIZE=2 FACE="MS PGothic">$B"](J</FONT></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">[1] </FONT></SPAN><A HREF="http://en.wikipedia.org/wiki/Off-by-one_error#Fencepost_error"><SPAN LANG="es"><U><FONT COLOR="#0000FF" SIZE=2 FACE="Courier New">http://en.wikipedia.org/wiki/Off-by-one_error#Fencepost_error</FONT></U></SPAN></A><SPAN LANG="es"></SPAN>
</P>
<P><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New">____________________</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> Racket Users list:</FONT></SPAN>
<BR><SPAN LANG="es"><FONT SIZE=2 FACE="Courier New"> </FONT></SPAN><A HREF="http://lists.racket-lang.org/users"><SPAN LANG="es"><U><FONT COLOR="#0000FF" SIZE=2 FACE="Courier New">http://lists.racket-lang.org/users</FONT></U></SPAN></A><SPAN LANG="es"></SPAN>
</P>
</BODY>
</HTML>