Skip to content

Commit 5b28e65

Browse files
authored
Merge pull request #350 from jmid/float-dist-fix
Fix QCheck{,2}.Gen.float distribution
2 parents e4268fe + 5e65bfe commit 5b28e65

13 files changed

+954
-21
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## NEXT RELEASE
44

5+
- Fix `QCheck{,2}.Gen.float` generator which would only generate numbers with an
6+
exponent between 2^{-21} and 2^22
57
- Elaborate on `QCheck`/`QCheck2` situation in README
68
- Add a missing `description` field to the *.opam files
79
- Document `Shrink` invariants in the `QCheck` module

src/core/QCheck.ml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ let string_fold_right f s acc =
9898
else loop (i-1) (f s.[i] acc) in
9999
loop (len-1) acc
100100

101+
let cut_exp_zero s =
102+
match String.split_on_char 'e' s with
103+
| [signif;exponent] ->
104+
(match exponent.[0] with (* int_of_string removes a leading '0' *)
105+
| '+' -> Printf.sprintf "%se+%i" signif (int_of_string exponent) (* keep a leading '+' *)
106+
| _ -> Printf.sprintf "%se%i" signif (int_of_string exponent)) (* keep a leading '-' *)
107+
| _ -> s
108+
101109
exception No_example_found of string
102110
(* raised if an example failed to be found *)
103111

@@ -165,9 +173,16 @@ module Gen = struct
165173

166174
let bool st = RS.bool st
167175

168-
let float st =
169-
exp (RS.float st 15. *. (if RS.float st 1. < 0.5 then 1. else -1.))
170-
*. (if RS.float st 1. < 0.5 then 1. else -1.)
176+
let float st = (* switch to [bits64] once lower bound reaches 4.14 *)
177+
(* Technically we could write [15] but this is clearer *)
178+
let four_bits_mask = 0b1111 in
179+
(* Top 4 bits *)
180+
let left = Int64.(shift_left (of_int (RS.bits st land four_bits_mask)) 60) in
181+
(* Middle 30 bits *)
182+
let middle = Int64.(shift_left (of_int (RS.bits st)) 30) in
183+
(* Bottom 30 bits *)
184+
let right = Int64.of_int (RS.bits st) in
185+
Int64.(float_of_bits (logor left (logor middle right)))
171186

172187
let pfloat st = abs_float (float st)
173188
let nfloat st = -.(pfloat st)
@@ -495,7 +510,10 @@ module Print = struct
495510
let int32 i = Int32.to_string i ^ "l"
496511
let int64 i = Int64.to_string i ^ "L"
497512
let bool = string_of_bool
498-
let float = string_of_float
513+
let float f = (* Windows workaround to avoid leading exponent zero such as "-1.00001604579e-010" *)
514+
if Sys.win32
515+
then string_of_float f |> cut_exp_zero
516+
else string_of_float f
499517
let string s = Printf.sprintf "%S" s
500518
let bytes b = string (Bytes.to_string b)
501519
let char c = Printf.sprintf "%C" c

src/core/QCheck2.ml

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ let rec list_split l len acc = match len,l with
6060
| 0,_ -> List.rev acc, l
6161
| _,x::xs -> list_split xs (len-1) (x::acc)
6262

63+
let cut_exp_zero s =
64+
match String.split_on_char 'e' s with
65+
| [signif;exponent] ->
66+
(match exponent.[0] with (* int_of_string removes a leading '0' *)
67+
| '+' -> Printf.sprintf "%se+%i" signif (int_of_string exponent) (* keep a leading '+' *)
68+
| _ -> Printf.sprintf "%se%i" signif (int_of_string exponent)) (* keep a leading '-' *)
69+
| _ -> s
70+
6371
exception Failed_precondition
6472
(* raised if precondition is false *)
6573

@@ -397,10 +405,16 @@ module Gen = struct
397405
then Tree.Tree (true, Seq.return false_gen)
398406
else false_gen
399407

400-
let float : float t = fun st ->
401-
let x = exp (RS.float st 15. *. (if RS.bool st then 1. else -1.))
402-
*. (if RS.bool st then 1. else -1.)
403-
in
408+
let float : float t = fun st -> (* switch to [bits64] once lower bound reaches 4.14 *)
409+
(* Technically we could write [15] but this is clearer *)
410+
let four_bits_mask = 0b1111 in
411+
(* Top 4 bits *)
412+
let left = Int64.(shift_left (of_int (RS.bits st land four_bits_mask)) 60) in
413+
(* Middle 30 bits *)
414+
let middle = Int64.(shift_left (of_int (RS.bits st)) 30) in
415+
(* Bottom 30 bits *)
416+
let right = Int64.of_int (RS.bits st) in
417+
let x = Int64.(float_of_bits (logor left (logor middle right))) in
404418
let shrink a = fun () -> Shrink.float_towards 0. a () in
405419
Tree.make_primitive shrink x
406420

@@ -918,7 +932,10 @@ module Print = struct
918932

919933
let bool = string_of_bool
920934

921-
let float = string_of_float
935+
let float f = (* Windows workaround to avoid leading exponent zero such as "-1.00001604579e-010" *)
936+
if Sys.win32
937+
then string_of_float f |> cut_exp_zero
938+
else string_of_float f
922939

923940
let string s = Printf.sprintf "%S" s
924941

@@ -2112,10 +2129,6 @@ module Test = struct
21122129
(* print entries of the table, sorted by increasing index *)
21132130
let out = Buffer.create 128 in
21142131
(* Windows workaround to avoid annoying exponent zero such as "1.859e+018" *)
2115-
let cut_exp_zero s =
2116-
match String.split_on_char '+' s with
2117-
| [signif;exponent] -> Printf.sprintf "%s+%i" signif (int_of_string exponent)
2118-
| _ -> failwith "cut_exp_zero failed to parse scientific notation " ^ s in
21192132
let fmt_float f =
21202133
if f > 1e7 || f < -1e7 then cut_exp_zero (Printf.sprintf "%.3e" f) else Printf.sprintf "%.2f" f in
21212134
Printf.bprintf out "stats %s:\n" name;

test/core/QCheck2_expect_test.expected.ocaml4.32

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,48 @@ Test nat < 5001 failed (7 shrink steps):
305305

306306
--- Failure --------------------------------------------------------------------
307307

308+
Test float <= 1e10 failed (594 shrink steps):
309+
310+
10000521515.6
311+
312+
--- Failure --------------------------------------------------------------------
313+
314+
Test float < Float.pi failed (622 shrink steps):
315+
316+
3.14167007397
317+
318+
--- Failure --------------------------------------------------------------------
319+
320+
Test float <= 1.0 failed (627 shrink steps):
321+
322+
1.00001262726
323+
324+
--- Failure --------------------------------------------------------------------
325+
326+
Test float < 1.0 failed (627 shrink steps):
327+
328+
1.00001262726
329+
330+
--- Failure --------------------------------------------------------------------
331+
332+
Test float <= 1e-10 failed (662 shrink steps):
333+
334+
1.00006020937e-10
335+
336+
--- Failure --------------------------------------------------------------------
337+
338+
Test float >= -1e-10 failed (791 shrink steps):
339+
340+
-1.00003797158e-10
341+
342+
--- Failure --------------------------------------------------------------------
343+
344+
Test float >= -1e10 failed (722 shrink steps):
345+
346+
-10000376498.9
347+
348+
--- Failure --------------------------------------------------------------------
349+
308350
Test char never produces 'abcdef' failed (1 shrink steps):
309351

310352
'a'
@@ -1686,9 +1728,67 @@ stats dist:
16861728
-4.. 0: ####################################################### 1934
16871729
1.. 5: 0
16881730
6.. 10: 0
1731+
1732+
+++ Stats for float exponent ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1733+
1734+
stats exponent:
1735+
num: 5000, avg: 17.00, stddev: 588.29, median 26, min -1025, max 1024
1736+
-1025.. -923: ############################################# 225
1737+
-922.. -820: ############################################### 235
1738+
-819.. -717: #################################################### 263
1739+
-716.. -614: ############################################## 231
1740+
-613.. -511: ################################################### 256
1741+
-510.. -408: ############################################### 237
1742+
-407.. -305: ################################################ 241
1743+
-304.. -202: ################################################## 251
1744+
-201.. -99: #################################################### 260
1745+
-98.. 4: ################################################# 247
1746+
5.. 107: ###################################################### 274
1747+
108.. 210: ################################################# 249
1748+
211.. 313: ############################################## 230
1749+
314.. 416: ################################################### 257
1750+
417.. 519: ##################################################### 268
1751+
520.. 622: ###################################################### 270
1752+
623.. 725: ################################################# 249
1753+
726.. 828: #################################################### 262
1754+
829.. 931: ####################################################### 275
1755+
932.. 1034: ############################################ 220
1756+
1757+
+++ Stats for float significant ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1758+
1759+
stats significant:
1760+
num: 5000, avg: 18327.51, stddev: 767363.84, median 512418, min -999183, max 999745
1761+
-999183..-899237: #################################################### 509
1762+
-899236..-799290: ##################################################### 514
1763+
-799289..-699343: ################################################# 479
1764+
-699342..-599396: ############################################### 462
1765+
-599395..-499449: ################################################# 474
1766+
-499448..-399502: 0
1767+
-399501..-299555: 0
1768+
-299554..-199608: 0
1769+
-199607.. -99661: 0
1770+
-99660.. 286: 2
1771+
287.. 100233: 0
1772+
100234.. 200180: 0
1773+
200181.. 300127: 0
1774+
300128.. 400074: 0
1775+
400075.. 500021: 0
1776+
500022.. 599968: ################################################## 483
1777+
599969.. 699915: ##################################################### 512
1778+
699916.. 799862: #################################################### 509
1779+
799863.. 899809: ####################################################### 531
1780+
899810.. 999756: ###################################################### 525
1781+
1782+
+++ Collect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1783+
1784+
Collect results for test float classify:
1785+
1786+
FP_normal: 4994 cases
1787+
FP_nan: 2 cases
1788+
FP_subnormal: 4 cases
16891789
================================================================================
16901790
1 warning(s)
1691-
failure (76 tests failed, 3 tests errored, ran 168 tests)
1791+
failure (83 tests failed, 3 tests errored, ran 178 tests)
16921792
random seed: 153870556
16931793

16941794
+++ Stats for int_dist_empty_bucket ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

test/core/QCheck2_expect_test.expected.ocaml4.64

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,48 @@ Test nat < 5001 failed (7 shrink steps):
367367

368368
--- Failure --------------------------------------------------------------------
369369

370+
Test float <= 1e10 failed (594 shrink steps):
371+
372+
10000521515.6
373+
374+
--- Failure --------------------------------------------------------------------
375+
376+
Test float < Float.pi failed (622 shrink steps):
377+
378+
3.14167007397
379+
380+
--- Failure --------------------------------------------------------------------
381+
382+
Test float <= 1.0 failed (627 shrink steps):
383+
384+
1.00001262726
385+
386+
--- Failure --------------------------------------------------------------------
387+
388+
Test float < 1.0 failed (627 shrink steps):
389+
390+
1.00001262726
391+
392+
--- Failure --------------------------------------------------------------------
393+
394+
Test float <= 1e-10 failed (662 shrink steps):
395+
396+
1.00006020937e-10
397+
398+
--- Failure --------------------------------------------------------------------
399+
400+
Test float >= -1e-10 failed (791 shrink steps):
401+
402+
-1.00003797158e-10
403+
404+
--- Failure --------------------------------------------------------------------
405+
406+
Test float >= -1e10 failed (722 shrink steps):
407+
408+
-10000376498.9
409+
410+
--- Failure --------------------------------------------------------------------
411+
370412
Test char never produces 'abcdef' failed (1 shrink steps):
371413

372414
'a'
@@ -1748,9 +1790,67 @@ stats dist:
17481790
-4.. 0: ####################################################### 1934
17491791
1.. 5: 0
17501792
6.. 10: 0
1793+
1794+
+++ Stats for float exponent ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1795+
1796+
stats exponent:
1797+
num: 5000, avg: 17.00, stddev: 588.29, median 26, min -1025, max 1024
1798+
-1025.. -923: ############################################# 225
1799+
-922.. -820: ############################################### 235
1800+
-819.. -717: #################################################### 263
1801+
-716.. -614: ############################################## 231
1802+
-613.. -511: ################################################### 256
1803+
-510.. -408: ############################################### 237
1804+
-407.. -305: ################################################ 241
1805+
-304.. -202: ################################################## 251
1806+
-201.. -99: #################################################### 260
1807+
-98.. 4: ################################################# 247
1808+
5.. 107: ###################################################### 274
1809+
108.. 210: ################################################# 249
1810+
211.. 313: ############################################## 230
1811+
314.. 416: ################################################### 257
1812+
417.. 519: ##################################################### 268
1813+
520.. 622: ###################################################### 270
1814+
623.. 725: ################################################# 249
1815+
726.. 828: #################################################### 262
1816+
829.. 931: ####################################################### 275
1817+
932.. 1034: ############################################ 220
1818+
1819+
+++ Stats for float significant ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1820+
1821+
stats significant:
1822+
num: 5000, avg: 18327.51, stddev: 767363.84, median 512418, min -999183, max 999745
1823+
-999183..-899237: #################################################### 509
1824+
-899236..-799290: ##################################################### 514
1825+
-799289..-699343: ################################################# 479
1826+
-699342..-599396: ############################################### 462
1827+
-599395..-499449: ################################################# 474
1828+
-499448..-399502: 0
1829+
-399501..-299555: 0
1830+
-299554..-199608: 0
1831+
-199607.. -99661: 0
1832+
-99660.. 286: 2
1833+
287.. 100233: 0
1834+
100234.. 200180: 0
1835+
200181.. 300127: 0
1836+
300128.. 400074: 0
1837+
400075.. 500021: 0
1838+
500022.. 599968: ################################################## 483
1839+
599969.. 699915: ##################################################### 512
1840+
699916.. 799862: #################################################### 509
1841+
799863.. 899809: ####################################################### 531
1842+
899810.. 999756: ###################################################### 525
1843+
1844+
+++ Collect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1845+
1846+
Collect results for test float classify:
1847+
1848+
FP_normal: 4994 cases
1849+
FP_nan: 2 cases
1850+
FP_subnormal: 4 cases
17511851
================================================================================
17521852
1 warning(s)
1753-
failure (76 tests failed, 3 tests errored, ran 168 tests)
1853+
failure (83 tests failed, 3 tests errored, ran 178 tests)
17541854
random seed: 153870556
17551855

17561856
+++ Stats for int_dist_empty_bucket ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

0 commit comments

Comments
 (0)