-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Infinities as floating point range endpoints #68
Conversation
src/System/Random/Internal.hs
Outdated
@@ -806,6 +809,9 @@ uniformDoublePositive01M g = (+ d) <$> uniformDouble01M g | |||
instance UniformRange Float where | |||
uniformRM (l, h) g | |||
| l == h = return l | |||
| isInfinite l && isInfinite h = return (0/0) -- NaN |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was previously the case too, tested here:
random/src/System/Random/Stateful.hs
Lines 608 to 611 in 11464aa
-- * If \(a\) is @-Infinity@ and \(b\) is @Infinity@, the result is @NaN@. | |
-- | |
-- >>> let (a, b, x) = (-inf, inf, 0.5) in x * a + (1 - x) * b | |
-- NaN |
But we need this guard now so we can distinguish "both endpoints are unequal infinities" (this case) from "one of the endpoints is an infinity" (the next two cases)..
=> Proxy a -> TestTree | ||
floatingSpec px = | ||
testGroup ("(" ++ showsType px ")") | ||
[ SC.testProperty "uniformR" $ seeded $ Range.uniformRangeWithin px | ||
, testCase "r = +inf, x = 0" $ positiveInf @?= fst (uniformR (0, positiveInf) (ConstGen 0)) | ||
, testCase "r = +inf, x = 1" $ positiveInf @?= fst (uniformR (0, positiveInf) (ConstGen 1)) | ||
, testCase "l = -inf, x = 0" $ negativeInf @?= fst (uniformR (negativeInf, 0) (ConstGen 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails without the additional guards in uniformRM
, and passes when they are added.
The benchmarks show an unacceptable decline in performance. I'll turn this back into a draft until I've figured this out. Before (11464aa):
Currently (ef2ee10):
Edit: see #68 (comment) for updated benchmarks. |
64c52a5 makes Before (master / 11464aa):
After 67e12bf:
|
Performance issues fixed. |
I've observed such behavior when I implemented |
67e12bf
to
2e80584
Compare
Ready for review. |
-- TODO: Add more tests | ||
] | ||
where | ||
positiveInf, negativeInf :: a | ||
positiveInf = read "Infinity" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would write positiveInf = 1 / 0 then you don't need the Read constraint but I don't know if this is good practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is certainly much faster. Probably even constant-folded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering that it is in a test suite, I don't think performance matters much. Using read
makes it a bit more obvious I think.
I think this is ready to be merged. @Shimuuar @idontgetoutmuch if either of you has a strong opinion about changing |
Sorry, I forgot about this PR. Why NaN is returned when both endpoints are infinities? Following definition (in pseudocode) seems more sensible: uniformRM (+∞,+∞) _ = return +∞
uniformRM (-∞,-∞) _ = return -∞
uniformRM (-∞,+∞) _ = oneOf [-∞,+∞] Also number of ifs on happy path (neither endpoint is ∞) worry me a bit. |
This has been discussed back in #54. I agree with @idontgetoutmuch that returning NaN is more reasonable than alternation between infinities. |
From my point of view the issue in #54 is not that much about returning NaNs per se, but about returning NaNs inconsistently, only in a tiny fraction of cases, so small, that it would easily pass a test suite of 100 random values. It is worth to change this behaviour a) to be consistent with the majority of cases, b) to match our own documentation: http://hackage.haskell.org/package/random-1.2.0/docs/System-Random-Stateful.html#g:14 On the other hand, |
I just found this behavior mildly surprising which is fine for floating point. What isn't when it comes to floating point. BTW I came up with optimization which exploits quirks of floating point arithmetics: | isInfinite l || isInfinite h = return $! h + l If only one of Otherwise LGTM |
@curiousleo what do you think about @Shimuuar's suggestion? Also, could you please rebase? |
Thanks for the reminder. The suggestion looks neat! I'll come back to this PR as soon as free time permits. |
2cbfe1b
to
ef90a0f
Compare
Fixes #54. Brings the behaviour in line with the docs, which state that if exactly one endpoint is infinite, then
uniformRM
returns that endpoint.