cases.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. """The reviewed test design: one case group per public dmath function.
  2. Hexadecimal inputs name exact binary64 values, not approximate decimal inputs.
  3. Every boundary expansion retains its purpose in the emitted case name.
  4. These groups were designed afresh; no legacy vectors or fingerprints are used.
  5. """
  6. from dataclasses import dataclass
  7. from oracle import bits, value, SIGN, INF
  8. @dataclass(frozen=True)
  9. class Case:
  10. name: str
  11. x: int
  12. y: int = 0
  13. def word(x):
  14. return bits(float.fromhex(x)) if isinstance(x, str) else bits(float(x))
  15. def c(name, x, y=0):
  16. return Case(name, word(x), word(y))
  17. def edge(name, x, both_signs=False):
  18. u = word(x)
  19. assert 0 < u < INF
  20. rows = [Case(name + suffix, u + offset) for suffix, offset in
  21. [('_below', -1), ('_at', 0), ('_above', 1)]]
  22. if both_signs:
  23. rows += [Case('negative_' + r.name, r.x | SIGN) for r in rows]
  24. return rows
  25. def special():
  26. return [
  27. Case('positive_zero', 0), Case('negative_zero', SIGN),
  28. Case('least_subnormal', 1), Case('negative_least_subnormal', SIGN | 1),
  29. Case('third_subnormal', 3), Case('negative_third_subnormal', SIGN | 3),
  30. Case('largest_subnormal', 0x000FFFFFFFFFFFFF),
  31. Case('negative_largest_subnormal', 0x800FFFFFFFFFFFFF),
  32. Case('least_normal', 0x0010000000000000),
  33. Case('negative_least_normal', 0x8010000000000000),
  34. Case('largest_finite', 0x7FEFFFFFFFFFFFFF),
  35. Case('negative_largest_finite', 0xFFEFFFFFFFFFFFFF),
  36. Case('positive_infinity', INF), Case('negative_infinity', INF | SIGN),
  37. Case('quiet_nan_payload', 0x7FF8ABCDEF135790),
  38. Case('negative_quiet_nan_payload', 0xFFF8ABCDEF135790),
  39. Case('signaling_nan_payload', 0x7FF0000000010248),
  40. Case('negative_signaling_nan_payload', 0xFFF0000000010248),
  41. ]
  42. def cases_isfinite():
  43. return special() + [c('ordinary_positive', '0x1.abc123p+37'),
  44. c('ordinary_negative', '-0x1.2468p-137')]
  45. def cases_isinf():
  46. return special() + [Case('exponent_below_infinity', 0x7FE0000000000000),
  47. Case('first_nan_encoding', INF + 1)]
  48. def cases_isnan():
  49. return special() + [Case('all_fraction_bits_set', 0x7FFFFFFFFFFFFFFF),
  50. Case('negative_all_bits_set', 0xFFFFFFFFFFFFFFFF)]
  51. def cases_isnormal():
  52. return special() + edge('normal_transition', '0x1p-1022', True)
  53. def cases_fabs():
  54. return special() + [c('negative_fraction', '-0x1.96p-4'),
  55. c('negative_large_integral', '-0x1.23456789abcdep+89')]
  56. def cases_copysign():
  57. rows = []
  58. for r in special() + [c('ordinary', '0x1.b38p+6')]:
  59. rows += [Case(r.name + '_positive_sign', r.x, word('0x1p-81')),
  60. Case(r.name + '_negative_sign', r.x, word('-0x1p+81'))]
  61. rows += [Case('sign_from_negative_nan', word(7.625), 0xFFF0000000010248),
  62. Case('sign_from_negative_zero', word(7.625), SIGN),
  63. Case('sign_from_positive_nan', word(-7.625), 0x7FF8ABCDEF135790)]
  64. return rows
  65. def ordering_cases():
  66. pairs = [(0, SIGN), (SIGN, 0), (SIGN, SIGN), (0, 0),
  67. (word(-7.625), word(3.1875)), (1, 3),
  68. (INF, word(11.125)), (INF | SIGN, word(-11.125))]
  69. rows = [Case('ordered_pair_' + str(i), a, b) for i, (a, b) in enumerate(pairs)]
  70. for nan in special()[-4:]:
  71. rows += [Case(nan.name + '_left', nan.x, word(13.25)),
  72. Case(nan.name + '_right', word(13.25), nan.x),
  73. Case(nan.name + '_both', nan.x, 0xFFF8ABCDEF135790)]
  74. return rows
  75. def cases_fmin():
  76. return ordering_cases() + [c('equal_negative_numbers', -19.875, -19.875)]
  77. def cases_fmax():
  78. return ordering_cases() + [c('equal_positive_numbers', 19.875, 19.875)]
  79. def rounding_boundaries():
  80. rows = special()
  81. for label, x in [('unit', '0x1p+0'), ('half', '0x1p-1'),
  82. ('word_split', '0x1p+20'), ('signed32', '0x1p+31'),
  83. ('last_fractional_binade', '0x1p+51'),
  84. ('integral_binade', '0x1p+52'), ('signed64', '0x1p+63')]:
  85. rows += edge(label, x, True)
  86. return rows
  87. def cases_ceil():
  88. return rounding_boundaries() + [c('up_from_positive_fraction', 23.0625),
  89. c('up_from_negative_fraction', -23.0625)]
  90. def cases_floor():
  91. return rounding_boundaries() + [c('down_from_positive_fraction', 23.9375),
  92. c('down_from_negative_fraction', -23.9375)]
  93. def cases_trunc():
  94. return rounding_boundaries() + [c('drop_positive_fraction', 37.6875),
  95. c('drop_negative_fraction', -37.6875)]
  96. def cases_modf():
  97. return rounding_boundaries() + [c('split_positive', 129.8125),
  98. c('split_negative', -129.8125),
  99. c('exact_negative_integer', -129)]
  100. def cases_fmod():
  101. rows = [c('positive_remainder', 53.75, 7.5), c('negative_remainder', -53.75, 7.5),
  102. c('negative_divisor', 53.75, -7.5), c('both_negative', -53.75, -7.5),
  103. c('positive_exact_multiple', 52.5, 7.5),
  104. c('negative_exact_multiple', -52.5, 7.5),
  105. c('quotient_exceeds_double', '0x1.abcdefp+900', '0x1.7p-901'),
  106. c('subnormal_remainder', '0x1.0000000000001p-1022', '0x1p-1022'),
  107. Case('subnormal_division', 29, 7), Case('negative_subnormal_division', SIGN | 29, 7)]
  108. for r in special():
  109. rows += [Case(r.name + '_dividend', r.x, word(7.5)),
  110. Case(r.name + '_divisor', word(-53.75), r.x)]
  111. return rows
  112. def cases_sqrt():
  113. return (special() + [c('exact_square', 206.640625), c('irrational', 13.625),
  114. c('wide_significand', '0x1.a5c739b18426fp+409')]
  115. + edge('square_neighbor', '0x1.9d48p+7')
  116. + edge('normal_subnormal_transition', '0x1p-1022'))
  117. def cases_cbrt():
  118. rows = special() + [c('exact_positive_cube', 155.287109375),
  119. c('exact_negative_cube', -155.287109375),
  120. c('irrational_positive', 19.375), c('irrational_negative', -19.375)]
  121. for rem in range(3):
  122. rows += edge('exponent_class_' + str(rem), '0x1.73p+' + str(600 + rem), True)
  123. return rows
  124. def cases_exp():
  125. rows = special() + [c('moderate_positive', 6.3125), c('moderate_negative', -6.3125),
  126. c('large_finite_result', 708.375), c('subnormal_result', -738.625)]
  127. for name, x in [('tiny_shortcut', '0x1p-28'), ('first_reduction', '0x1.62e43p-2'),
  128. ('second_reduction', '0x1.0a2b2p+0'),
  129. ('overflow_boundary', '0x1.62e42fefa39efp+9')]:
  130. rows += edge(name, x, True)
  131. # The last nonzero result lies at this negative magnitude.
  132. rows += [Case('underflow_' + r.name, r.x | SIGN)
  133. for r in edge('threshold', '0x1.74910d52d3051p+9')]
  134. return rows
  135. def cases_exp2():
  136. rows = special() + [c('fractional_positive', 12.3125), c('fractional_negative', -12.3125),
  137. c('exact_normal_power', -847), c('subnormal_power', -1053)]
  138. for n in (9, 67, 143, 219):
  139. rows += edge('table_midpoint_' + str(n), value(word((n + 0.5) / 256)))
  140. rows += edge('overflow', '0x1p+10')
  141. rows += [Case('underflow_' + r.name, r.x | SIGN)
  142. for r in edge('tie', 1075)]
  143. return rows
  144. def cases_exp10():
  145. return (special() + [c('exact_positive_power', 17), c('negative_power', -17),
  146. c('fractional_positive', 4.1875), c('fractional_negative', -4.1875),
  147. c('subnormal_result', -319.75)]
  148. + edge('overflow', '0x1.34413509f79ffp+8'))
  149. def log_boundaries():
  150. return (special() + edge('near_one', 1) + edge('normalize', '0x1.6a09ep+0')
  151. + [c('less_than_one', 0.171875), c('greater_than_one', 23.5625)])
  152. def cases_log():
  153. return log_boundaries() + [c('wide_exponent', '0x1.a9bcdef012345p+723')]
  154. def cases_log2():
  155. return log_boundaries() + [c('exact_binary_power', '0x1p-817'),
  156. c('exact_subnormal_power', '0x1p-1053')]
  157. def cases_log10():
  158. return log_boundaries() + [c('decimal_power', 10000000),
  159. c('small_decimal', 0.00000003125)]
  160. def cases_log_base():
  161. rows = [c('base_above_one', 19.375, 3.25), c('base_below_one', 19.375, 0.3125),
  162. c('argument_below_one', 0.3125, 3.25), c('equal_base_and_argument', 3.25, 3.25),
  163. c('zero_result_sign', 1, 0.3125), c('base_one_positive', 19.375, 1),
  164. c('base_one_negative', 0.3125, 1), c('both_one', 1, 1),
  165. c('near_one_pair', '0x1.0000000000003p+0', '0x1.0000000000007p+0')]
  166. for r in special():
  167. rows += [Case(r.name + '_argument', r.x, word(3.25)),
  168. Case(r.name + '_base', word(19.375), r.x)]
  169. rows += [Case('both_infinite', INF, INF), Case('both_zero', 0, 0)]
  170. return rows
  171. def cases_pow():
  172. rows = [c('positive_fractional', 3.3125, 2.1875),
  173. c('negative_odd_integer', -3.3125, 7), c('negative_even_integer', -3.3125, 6),
  174. c('negative_reciprocal', -3.3125, -7), c('negative_fractional_domain', -3.3125, 2.1875),
  175. c('square_shortcut', '0x1.abcdef1234567p+5', 2),
  176. c('cube_shortcut', '0x1.abcdef1234567p+5', 3),
  177. c('fourth_power_shortcut', '0x1.abcdef1234567p+5', 4),
  178. c('sqrt_shortcut', 23.5625, 0.5), c('reciprocal_shortcut', 23.5625, -1),
  179. c('near_one_large_positive', '0x1.0000000000003p+0', '0x1p+49'),
  180. c('near_one_large_negative', '0x1.ffffffffffffbp-1', '-0x1p+49'),
  181. c('finite_overflow', 7.625, 4096), c('finite_underflow', 7.625, -4096),
  182. c('negative_odd_overflow', -7.625, 4097), c('negative_odd_underflow', -7.625, -4097),
  183. c('largest_odd_exponent', -1, '0x1.fffffffffffffp+52'),
  184. c('first_even_only_binade', -1, '0x1p+53')]
  185. for r in special():
  186. rows += [Case(r.name + '_odd_power', r.x, word(7)),
  187. Case(r.name + '_negative_odd_power', r.x, word(-7)),
  188. Case(r.name + '_zero_power', r.x, 0),
  189. Case(r.name + '_exponent', word(3.3125), r.x)]
  190. rows += [Case('one_to_nan', word(1), 0xFFF0000000010248),
  191. Case('negative_one_to_infinity', word(-1), INF),
  192. Case('negative_zero_fractional_pole', SIGN, word(-0.25))]
  193. return rows
  194. def angle_cases():
  195. rows = special() + [c('small_positive_angle', 0.34375), c('small_negative_angle', -0.34375),
  196. c('moderate_positive_angle', 43.8125), c('moderate_negative_angle', -43.8125),
  197. c('large_reduction', '0x1.a5c739b18426fp+41'),
  198. c('very_large_reduction', '0x1.73b4a82cf19dep+811')]
  199. rows += edge('quarter_turn_kernel', '0x1.921fb54442d18p-1', True)
  200. rows += edge('medium_reduction_cutoff', '0x1.921fbp+20', True)
  201. return rows
  202. def cases_sin():
  203. return angle_cases() + edge('tiny_cutoff', '0x1p-26', True)
  204. def cases_cos():
  205. return angle_cases() + edge('tiny_cutoff', '0x1.6a09ep-27', True)
  206. def cases_tan():
  207. return (angle_cases() + edge('reciprocal_pole', '0x1.2d97c7f3321d2p+2', True)
  208. + edge('kernel_transform', '0x1.59428p-1', True))
  209. def cases_sincos():
  210. return angle_cases() + [c('second_quadrant', 2.3125), c('third_quadrant', 4.1875),
  211. c('fourth_quadrant', 5.9375)]
  212. def cases_asin():
  213. return (special() + edge('domain_endpoint', 1, True) + edge('half_interval', 0.5, True)
  214. + edge('near_endpoint_formula', '0x1.f3333p-1', True)
  215. + [c('interior_positive', 0.71875), c('interior_negative', -0.71875)])
  216. def cases_acos():
  217. return (special() + edge('domain_endpoint', 1, True) + edge('half_interval', 0.5, True)
  218. + edge('tiny_cutoff', '0x1p-57', True)
  219. + [c('interior_positive', 0.28125), c('interior_negative', -0.28125)])
  220. def cases_atan():
  221. rows = special() + [c('ordinary_positive', 9.3125), c('ordinary_negative', -9.3125)]
  222. for label, x in [('tiny_cutoff', '0x1p-27'), ('first_interval', 0.4375),
  223. ('second_interval', 0.6875), ('third_interval', 1.1875),
  224. ('reciprocal_interval', 2.4375), ('asymptote', '0x1p+66')]:
  225. rows += edge(label, x, True)
  226. return rows
  227. def cases_atan2():
  228. rows = []
  229. for i, (y, x) in enumerate([(5.8125, 0.21875), (5.8125, -0.21875),
  230. (-5.8125, 0.21875), (-5.8125, -0.21875)]):
  231. rows.append(c('quadrant_' + str(i + 1), y, x))
  232. for y in [0, SIGN, INF, INF | SIGN]:
  233. for x in [0, SIGN, INF, INF | SIGN]:
  234. rows.append(Case('axes_%016x_%016x' % (y, x), y, x))
  235. for r in special():
  236. rows += [Case(r.name + '_ordinate', r.x, word(-5.8125)),
  237. Case(r.name + '_abscissa', word(-5.8125), r.x)]
  238. rows += [c('tiny_ratio', '0x1p-1000', '0x1p+900'),
  239. c('huge_ratio', '0x1p+900', '0x1p-1000'),
  240. c('tiny_ratio_negative_x', '0x1p-1000', '-0x1p+900'),
  241. c('unit_abscissa_shortcut', 1.8125, 1)]
  242. return rows
  243. # Declaration order is the category order in the test reports.
  244. GROUPS = {
  245. 'classification': ['isfinite', 'isinf', 'isnan', 'isnormal'],
  246. 'sign_and_order': ['fabs', 'copysign', 'fmin', 'fmax'],
  247. 'rounding_and_remainder': ['ceil', 'floor', 'trunc', 'modf', 'fmod'],
  248. 'roots': ['sqrt', 'cbrt'],
  249. 'exponentials': ['exp', 'exp2', 'exp10', 'pow'],
  250. 'logarithms': ['log', 'log2', 'log10', 'log_base'],
  251. 'trigonometry': ['sin', 'cos', 'tan', 'sincos'],
  252. 'inverse_trigonometry': ['asin', 'acos', 'atan', 'atan2'],
  253. }
  254. # Tolerances apply only to the independent accuracy oracle, never to the frozen
  255. # implementation results. They are case-suite budgets, not universal bounds.
  256. ULPS = {name: 0 for name in sum(list(GROUPS.values())[:4], [])}
  257. ULPS.update(cbrt=1, exp=1, exp2=1, exp10=2, pow=2, log=1, log2=1, log10=1,
  258. log_base=3, sin=1, cos=1, tan=1, sincos=1, asin=1, acos=1, atan=1, atan2=2)