Bug 6531 – assertion failure in std.range.iota

Status
RESOLVED
Resolution
WORKSFORME
Severity
major
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
Other
OS
Windows
Creation time
2011-08-19T07:49:29Z
Last change time
2020-03-21T03:56:42Z
Keywords
bootcamp, pull
Assigned to
No Owner
Creator
Andrej Mitrovic
Depends on
7455

Comments

Comment #0 by andrej.mitrovich — 2011-08-19T07:49:29Z
import std.range; import std.stdio; void main() { auto r1 = iota(0.0, 4.0, 0.03); // ok auto r2 = iota(0.0, 3.0, 0.03); // [email protected](4001): Assertion failure } I want a range in steps of 0.03, beginning at 0.0 and ending at the closest point to 3.0. Line 4001 is: assert(start + count * step >= end); There's no documentation, and no custom assert message, so I don't know what was the intention of the author. It doesn't help that this fails in debug mode but works fine in release mode.
Comment #1 by yao.gomez — 2012-02-06T12:48:21Z
With DMD 2.058HEAD the assertion is now at line 4136. Doing a little bit of debugging, it seems that the line at 4135: --- if (pastEnd < end) ++count; --- is the culprit. With the r1 range, the count variable is correctly updated, as both pastEnd and end are different. But with the failling range (r2), pastEnd and end have the same value!, and thus the count is not correctly updated. Changing the previous code to: --- if (pastEnd <= end) ++count; --- seems to do the trick, and passes all the std.range unit tests. But I don't know if that's the correct fix.
Comment #2 by yao.gomez — 2012-02-06T12:51:13Z
I have a patch with this solution, but I haven't pulled it yet, because I need more input in whether this is the right fix or not.
Comment #3 by code — 2012-02-06T20:27:53Z
The correct fix is to narrow the lhs of the comparison. assert(cast(Value)(start + count * step) >= end) It is a common source of bugs that the intermediate 80-bit result is used for temporaries.
Comment #4 by andrej.mitrovich — 2013-04-05T10:34:42Z
(In reply to comment #3) > The correct fix is to narrow the lhs of the comparison. > assert(cast(Value)(start + count * step) >= end) That doesn't fix the issue. Yao Gomez'es solution does, but I don't know if it's a proper solution. Yao, you could try making a pull request and see if you get more comments on github. Issue 9877 might be a duplicate too.
Comment #5 by code — 2013-04-06T01:47:07Z
> if (pastEnd <= end) ++count; This is definitely wrong. There are only 100 steps in [0, 0.03 ..., 3.0). The problem was that assert(0.0 + 100 * 0.03 >= 3.0) fails because of floating-point excess precision.
Comment #6 by drug2004 — 2013-04-11T07:13:10Z
I propose the following: auto pastEnd = start + count * step; if (step > 0) { if (pastEnd < end) { ++count; pastEnd = start + count * step; } assert(pastEnd >= end); } else { if (pastEnd > end) { ++count; pastEnd = start + count * step; } assert(pastEnd <= end); } Martin Nowak's fix is the better (simpler and more clear) but I just don't like cast.
Comment #7 by andrej.mitrovich — 2013-04-11T08:13:26Z
(In reply to comment #6) > Martin Nowak's fix is the better (simpler and more clear) but I just don't like > cast. I've tried his fix and it didn't work for me.
Comment #8 by drug2004 — 2013-04-11T08:21:21Z
(In reply to comment #7) > (In reply to comment #6) > > Martin Nowak's fix is the better (simpler and more clear) but I just don't like > > cast. > > I've tried his fix and it didn't work for me. Try mine. It works for me (with your example).
Comment #9 by safety0ff.bugz — 2013-04-11T09:34:33Z
Indeed, casting does not seem sufficient to force correct rounding of intermediate results. Seems like the solution is to either assign and force rounding, or use approxEqual with appropriate constants for the error terms.
Comment #10 by code — 2013-10-30T11:15:24Z
*** Issue 9877 has been marked as a duplicate of this issue. ***
Comment #11 by code — 2013-10-30T11:24:42Z
> Indeed, casting does not seem sufficient to force correct rounding of > intermediate results. > Seems like the solution is to either assign and force rounding, or use > approxEqual with appropriate constants for the error terms. Yes, casting is optimized away by dmd. Walter suggested to use an opaque function or inline asm to enforce rounding to lower precision. It seems like C99 addresses this by specifying that casts and assignments need to be rounded to lower precision. http://stackoverflow.com/questions/503436/how-to-deal-with-excess-precision-in-floating-point-computations/503523#503523
Comment #12 by code — 2013-10-30T11:49:21Z