Skip to content

Close #1035 Fix :> / :< generators producing values that violate the schema - #1301

Open
vssekorin wants to merge 1 commit into
metosin:masterfrom
vssekorin:fix-1035
Open

Close #1035 Fix :> / :< generators producing values that violate the schema#1301
vssekorin wants to merge 1 commit into
metosin:masterfrom
vssekorin:fix-1035

Conversation

@vssekorin

Copy link
Copy Markdown

Fixes #1035.

Problem

[:> x] turned the exclusive bound into an inclusive one with (inc x) and handed it to gen/double* as :min. inc does not move a double once |x| >= 2^53, so the generator was asked for values >= x and produced x itself:

(= Double/MAX_VALUE
   (shrink [:> Double/MAX_VALUE])
   (shrink [:>= Double/MAX_VALUE]))
;=> true

(> Double/MAX_VALUE Double/MAX_VALUE)
;=> false

The reported Double/MAX_VALUE case is just the extreme end of it — the bug starts at 2^53:

(->> (mg/sample [:> 1e16] {:seed 1 :size 100}) (remove (m/validator [:> 1e16])) count)
;=> 2
(->> (mg/sample [:> 1e300] {:seed 1 :size 100}) (remove (m/validator [:> 1e300])) count)
;=> 2

:< had the mirror problem via (dec x), and additionally threw on [:< (- Double/MAX_VALUE)] (Couldn't satisfy such-that predicate after 10 tries).

Fix

src/malli/generator.cljc

  • -next-up — the next representable double above x. Math/nextUp on the JVM; in ClojureScript, where no such function exists, one arithmetic ulp step (overshooting a representable value or two is harmless for generation).
  • gen-double-above — generator of doubles strictly greater than x, covering the three edges:
    • nothing is greater than ##NaN or ##Inf → unreachable generator (-never-gen);
    • everything finite is greater than ##-Inf → the plain double generator;
    • above Double/MAX_VALUE only ##Inf is left → (gen/return ##Inf).
  • :> now delegates to gen-double-above; :< is :> negated, which removes the duplicated mirror logic and fixes [:< (- Double/MAX_VALUE)] along the way.
(mg/generate [:> Double/MAX_VALUE] {:seed 0})    ;=> ##Inf
(mg/generate [:> 1e16] {:seed 0})                ;=> 1.3510798882111488E16
(mg/generate [:< (- Double/MAX_VALUE)] {:seed 0}) ;=> ##-Inf
(mg/generate [:> 5] {:seed 0})                   ;=> 6.0   (unchanged)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[:> Double/MAX_VALUE] generates Double/MAX_VALUE

1 participant