Coverage for pycommons / math / int_math.py: 100%

197 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-11 03:04 +0000

1""" 

2Mathematics routines combining integers and floats. 

3 

4These routines try to return results with the highest possible precision, 

5ideally as integers. 

6If floating point values need to be converted to integers, then we round 

7towards the nearest integer numbers, whereas `0.5` is always rounded up and 

8`-0.5` is always rounded down. 

9Thus, `1.5` becomes `2` and `-1.5` becomes `-2`. 

10""" 

11 

12from contextlib import suppress 

13from math import gcd, isfinite, isqrt, sqrt 

14from sys import float_info 

15from typing import Final 

16 

17from pycommons.types import type_error 

18 

19#: The positive limit for doubles that can be represented exactly as ints. 

20#: We cannot represent any number `z` with `|z| >= 2 ** 53` as float without 

21#: losing some digits, because floats have only 52 bits. 

22#: `float(9007199254740992) == 9007199254740992.0` 

23#: `float(9007199254740991) == 9007199254740991.0` 

24#: But: 

25#: #: `float(9007199254740993) == 9007199254740992.0`. 

26__DBL_INT_LIMIT_P_I: Final[int] = 2 ** float_info.mant_dig 

27#: The positive limit for doubles that can be represented exactly as ints. 

28__DBL_INT_LIMIT_P_F: Final[float] = float(__DBL_INT_LIMIT_P_I) # = 1 << 53 

29#: The negative limit for doubles that can be represented exactly as ints. 

30__DBL_INT_LIMIT_N_I: Final[int] = -__DBL_INT_LIMIT_P_I 

31#: The negative limit for doubles that can be represented exactly as ints. 

32__DBL_INT_LIMIT_N_F: Final[float] = __DBL_INT_LIMIT_N_I 

33 

34 

35def __try_int(val: float) -> int | float: 

36 """ 

37 Convert a float to an int without any fancy checks. 

38 

39 :param val: the flot 

40 :returns: the float or int 

41 

42 >>> from math import inf, nan, nextafter 

43 >>> type(__try_int(0.0)) 

44 <class 'int'> 

45 >>> type(__try_int(0.5)) 

46 <class 'float'> 

47 >>> type(__try_int(inf)) 

48 <class 'float'> 

49 >>> type(__try_int(-inf)) 

50 <class 'float'> 

51 >>> type(__try_int(nan)) 

52 <class 'float'> 

53 >>> 1 << 53 

54 9007199254740992 

55 >>> type(__try_int(9007199254740992.0)) 

56 <class 'int'> 

57 >>> __try_int(9007199254740992.0) 

58 9007199254740992 

59 >>> too_big = nextafter(9007199254740992.0, inf) 

60 >>> print(too_big) 

61 9007199254740994.0 

62 >>> type(__try_int(too_big)) 

63 <class 'float'> 

64 >>> type(__try_int(-9007199254740992.0)) 

65 <class 'int'> 

66 >>> __try_int(-9007199254740992.0) 

67 -9007199254740992 

68 >>> type(__try_int(-too_big)) 

69 <class 'float'> 

70 """ 

71 if __DBL_INT_LIMIT_N_F <= val <= __DBL_INT_LIMIT_P_F: 

72 a = int(val) 

73 if a == val: 

74 return a 

75 return val 

76 

77 

78def try_int(value: int | float) -> int | float: 

79 """ 

80 Attempt to convert a float to an integer. 

81 

82 This method will convert a floating point number to an integer if the 

83 floating point number was representing an exact integer. This is the 

84 case if it has a) no fractional part and b) is in the range 

85 `-9007199254740992...9007199254740992`, i.e., the range where `+1` and 

86 `-1` work without loss of precision. 

87 

88 :param value: the input value, which must either be `int` or `float` 

89 :returns: an `int` if `value` can be represented as `int` without loss of 

90 precision, `val` otherwise 

91 :raises TypeError: if `value` is neither an instance of `int` nor of 

92 `float` 

93 :raises ValueError: if `value` is a `float`, but not finite 

94 

95 >>> print(type(try_int(10.5))) 

96 <class 'float'> 

97 >>> print(type(try_int(10))) 

98 <class 'int'> 

99 

100 >>> from math import inf, nan, nextafter 

101 >>> type(try_int(0.0)) 

102 <class 'int'> 

103 >>> type(try_int(0.5)) 

104 <class 'float'> 

105 

106 >>> try: 

107 ... try_int(inf) 

108 ... except ValueError as ve: 

109 ... print(ve) 

110 Value must be finite, but is inf. 

111 

112 >>> try: 

113 ... try_int(-inf) 

114 ... except ValueError as ve: 

115 ... print(ve) 

116 Value must be finite, but is -inf. 

117 

118 >>> try: 

119 ... try_int(nan) 

120 ... except ValueError as ve: 

121 ... print(ve) 

122 Value must be finite, but is nan. 

123 

124 >>> try: 

125 ... try_int("blab") # noqa # type: off 

126 ... except TypeError as te: 

127 ... print(te) 

128 value should be an instance of any in {float, int} but is str, namely \ 

129'blab'. 

130 

131 >>> type(try_int(9007199254740992.0)) 

132 <class 'int'> 

133 >>> try_int(9007199254740992.0) 

134 9007199254740992 

135 >>> too_big = nextafter(9007199254740992.0, inf) 

136 >>> print(too_big) 

137 9007199254740994.0 

138 >>> type(try_int(too_big)) 

139 <class 'float'> 

140 >>> type(try_int(-9007199254740992.0)) 

141 <class 'int'> 

142 >>> try_int(-9007199254740992.0) 

143 -9007199254740992 

144 >>> type(try_int(-too_big)) 

145 <class 'float'> 

146 """ 

147 if isinstance(value, int): 

148 return value 

149 if isinstance(value, float): 

150 if not isfinite(value): 

151 raise ValueError(f"Value must be finite, but is {value}.") 

152 if __DBL_INT_LIMIT_N_F <= value <= __DBL_INT_LIMIT_P_F: 

153 a = int(value) 

154 if a == value: 

155 return a 

156 return value 

157 raise type_error(value, "value", (int, float)) 

158 

159 

160def __choose_frac(num1: int, denom1: int, num2: int, denom2: int) \ 

161 -> tuple[int, int]: 

162 """ 

163 Choose the more compact one of two fractions. 

164 

165 :param num1: the first numerator 

166 :param denom1: the first denominator 

167 :param num2: the second numerator 

168 :param denom2: the second denominator 

169 :return: the numerator, denominator 

170 

171 >>> __choose_frac(10, 100, 1, 10) 

172 (1, 10) 

173 >>> __choose_frac(1, 10, 10, 100) 

174 (1, 10) 

175 >>> __choose_frac(1, 10, 1, 11) 

176 (1, 10) 

177 >>> __choose_frac(1, 11, 1, 10) 

178 (1, 10) 

179 >>> __choose_frac(1, 11, 2, 10) 

180 (2, 10) 

181 >>> __choose_frac(2, 10, 1, 11) 

182 (2, 10) 

183 >>> __choose_frac(2, 10, 1, 10) 

184 (1, 10) 

185 >>> __choose_frac(1, 10, 2, 10) 

186 (1, 10) 

187 """ 

188 return (num1, denom1) if (denom1 < denom2) or ( 

189 (num1 + denom1) < (num2 + denom2)) else (num2, denom2) 

190 

191 

192def __minimize_frac(value: float, num: int, denom: int) -> tuple[int, int]: 

193 """ 

194 Try to find a more compact equivalent representation of a given fraction. 

195 

196 If you convert `1/7` directly from the `float` format to an integer 

197 fraction via `(1/7).as_integer_ratio()`, you get 

198 `(2573485501354569, 18014398509481984)`. This is a precise conversion, 

199 as `2573485501354569/18014398509481984 - 1/7 == 0` 

200 However, `2573485501354569 * 7 = 18014398509481983`. Thus, the chosen 

201 denominator is just 1 off the perfect one, because 

202 `18014398509481983` divided by `2573485501354569` would give us exactly 

203 `7`. Therefore, in this function, we try to check some adjacent 

204 denominators for a given fraction. If they have a :func:`gcd` greater than 

205 `1` with the numerator, then we choose them instead. 

206 

207 :param value: the floating point value 

208 :param num: the numerator 

209 :param denom: the denominator 

210 :return: the fraction 

211 

212 >>> (1/7).as_integer_ratio() 

213 (2573485501354569, 18014398509481984) 

214 >>> 2573485501354569/18014398509481984 - 1/7 

215 0.0 

216 

217 >>> __minimize_frac(1/7, 2573485501354569, 18014398509481984) 

218 (1, 7) 

219 

220 >>> __minimize_frac(1/3, 6004799503160661, 18014398509481984) 

221 (1, 3) 

222 

223 >>> __minimize_frac(1/3, 1, 3) 

224 (1, 3) 

225 

226 >>> __minimize_frac(1/7, 1, 7) 

227 (1, 7) 

228 

229 >>> __minimize_frac(1/7, 10, 70) 

230 (1, 7) 

231 

232 >>> __minimize_frac(1e-20, 1, 1_0000_0000_0000_0000_0000) 

233 (1, 100000000000000000000) 

234 >>> __minimize_frac(1e-20, 1, 1_0000_0000_0000_0000_0001) 

235 (1, 100000000000000000001) 

236 >>> __minimize_frac(1e-20, 12, 12_0000_0000_0000_0000_0001) 

237 (1, 100000000000000000000) 

238 """ 

239 # First, we reduce with the GCD to simplify the fraction 

240 g: int = gcd(num, denom) # the greatest common divisor 

241 num //= g # simplify the fraction 

242 denom //= g 

243 best_num = num 

244 best_denom = denom 

245 min_gcd: int = 1 

246 

247 for num_add in range(-5, 5): 

248 use_num = num + num_add 

249 if use_num <= 0: 

250 continue 

251 for denom_add in range(-5, 5): 

252 use_denom = denom + denom_add 

253 if use_denom <= 0: 

254 continue 

255 if (use_num / use_denom) != value: 

256 continue 

257 g = gcd(use_num, use_denom) 

258 if g > min_gcd: 

259 min_gcd = g 

260 best_num, best_denom = __choose_frac( 

261 best_num, best_denom, use_num // g, use_denom // g) 

262 return best_num, best_denom 

263 

264 

265def float_to_frac(value: int | float) -> tuple[int, int]: 

266 """ 

267 Turn a floating point number into an integer fraction. 

268 

269 If we want to translate a floating point number to an integer fraction, 

270 then we have several possible ways to go about this. The reason is that, 

271 due to the loss in precision when representing fractions as floating point 

272 numbers, several different integer fractions can produce the exactly same 

273 floating point number. 

274 

275 One choice would be to use :meth:`float.as_integer_ratio`, which turns the 

276 binary representation of the floating point number to an integer fraction. 

277 This is the canonical way that, without losing any precision, will return 

278 an integer fraction that fits exactly to the floating point value. 

279 

280 However, as said, there may be multiple such fractions. And some of them 

281 may be more "compact" than others. 

282 

283 A second approach would be to first represent the floating point value as 

284 a string. The string that this produces also represents exactly this 

285 floating point value, obviously. Now we can translate the string to a 

286 fraction - and this can give us a different result. 

287 

288 Which of the two is right? 

289 

290 Both of them are. Kind of. So I'd figure we test both and stick with the 

291 :meth:`float.as_integer_ratio` default result - unless the string path 

292 provides a more compact representation. As simple yard stick, in such 

293 cases, we use the fraction with the smaller denominator. If the 

294 demoninators are the same, then we use the one with the smaller absolute 

295 numerator. 

296 

297 :param value: the floating point value 

298 :returns: the integer fraction 

299 :raises TypeError: if value is neither an integer nor a float 

300 :raises ValueError: if value is not finite 

301 

302 >>> float_to_frac(0.1) 

303 (1, 10) 

304 

305 >>> float_to_frac(1e-1) 

306 (1, 10) 

307 

308 >>> float_to_frac(1e-20) 

309 (1, 100000000000000000000) 

310 

311 >>> 1e-20.as_integer_ratio() 

312 (6646139978924579, 664613997892457936451903530140172288) 

313 

314 >>> float_to_frac(1e-30) 

315 (1, 1000000000000000000000000000000) 

316 

317 >>> float_to_frac(1e30) 

318 (1000000000000000000000000000000, 1) 

319 

320 >>> float_to_frac(1000) 

321 (1000, 1) 

322 

323 >>> float_to_frac(1000.567) 

324 (1000567, 1000) 

325 

326 >>> float_to_frac(1.234e-5) 

327 (617, 50000000) 

328 

329 >>> float_to_frac(1.234e5) 

330 (123400, 1) 

331 

332 >>> float_to_frac(0) 

333 (0, 1) 

334 >>> 0 / 1 

335 0.0 

336 

337 >>> float_to_frac(-5376935265607590) 

338 (-5376935265607590, 1) 

339 >>> -5376935265607590 / 1 

340 -5376935265607590.0 

341 

342 >>> float_to_frac(4) 

343 (4, 1) 

344 >>> 4 / 1 

345 4.0 

346 

347 >>> float_to_frac(1 / 7) 

348 (1, 7) 

349 

350 >>> float_to_frac(1 / 11) 

351 (1, 11) 

352 

353 >>> float_to_frac(1 / 10000) 

354 (1, 10000) 

355 

356 >>> float_to_frac(-21844.45693689149) 

357 (-2184445693689149, 100000000000) 

358 >>> -2184445693689149 / 100000000000 

359 -21844.45693689149 

360 

361 >>> float_to_frac(-3010907.436657168) 

362 (-188181714791073, 62500000) 

363 >>> -188181714791073 / 62500000 

364 -3010907.436657168 

365 

366 >>> float_to_frac(13660.023207762431) 

367 (26679732827661, 1953125000) 

368 >>> 26679732827661 / 1953125000 

369 13660.023207762431 

370 

371 >>> float_to_frac(438027.68586526066) 

372 (58791080797933, 134217728) 

373 >>> 58791080797933 / 134217728 

374 438027.68586526066 

375 

376 >>> float_to_frac(-8338355882.478134) 

377 (-546462491114087, 65536) 

378 >>> -546462491114087 / 65536 

379 -8338355882.478134 

380 

381 >>> float_to_frac(-32835294.95774138) 

382 (-275442417964869, 8388608) 

383 >>> -275442417964869 / 8388608 

384 -32835294.95774138 

385 

386 >>> float_to_frac(-0.8436071305882418) 

387 (-1054508913235303, 1250000000000001) 

388 >>> -1054508913235303 / 1250000000000001 

389 -0.8436071305882418 

390 

391 >>> float_to_frac(-971533.786640197) 

392 (-32599264379521, 33554432) 

393 >>> -32599264379521 / 33554432 

394 -971533.786640197 

395 

396 >>> float_to_frac(187487280836.01147) 

397 (767947902304303, 4096) 

398 >>> 767947902304303 / 4096 

399 187487280836.01147 

400 

401 >>> float_to_frac(24214223389953.125) 

402 (193713787119625, 8) 

403 >>> 193713787119625 / 8 

404 24214223389953.125 

405 

406 >>> float_to_frac(2645.112807929305) 

407 (363541536137187, 137438953472) 

408 >>> 363541536137187 / 137438953472 

409 2645.112807929305 

410 

411 >>> float_to_frac(92129361.64291245) 

412 (1545674200225257, 16777216) 

413 >>> 1545674200225257 / 16777216 

414 92129361.64291245 

415 

416 >>> float_to_frac(7218177.564653773) 

417 (1937614786056805, 268435456) 

418 >>> 1937614786056805 / 268435456 

419 7218177.564653773 

420 

421 >>> float_to_frac(-1.4589908563595052e+16) 

422 (-14589908563595052, 1) 

423 >>> -14589908563595052 / 1 

424 -1.4589908563595052e+16 

425 

426 >>> float_to_frac(-1.607745417434216e+16) 

427 (-16077454174342160, 1) 

428 >>> -16077454174342160 / 1 

429 -1.607745417434216e+16 

430 

431 >>> float_to_frac(-952261813.8291152) 

432 (-595163633643197, 625000) 

433 >>> -595163633643197 / 625000 

434 -952261813.8291152 

435 

436 >>> float_to_frac(124.69515801820336) 

437 (779344737613771, 6250000000000) 

438 >>> 779344737613771 / 6250000000000 

439 124.69515801820336 

440 

441 >>> float_to_frac(1.041491959175676e+16) 

442 (10414919591756760, 1) 

443 >>> 10414919591756760 / 1 

444 1.041491959175676e+16 

445 

446 >>> float_to_frac(1.4933667846659504e+16) 

447 (14933667846659504, 1) 

448 >>> 14933667846659504 / 1 

449 1.4933667846659504e+16 

450 

451 >>> float_to_frac(-6.034817133993009e-05) 

452 (-271784001959, 4503599627370496) 

453 >>> -271784001959 / 4503599627370496 

454 -6.034817133993009e-05 

455 

456 >>> float_to_frac(-2.682658826813622e-05) 

457 (-1887753327, 70368744177664) 

458 >>> -1887753327 / 70368744177664 

459 -2.682658826813622e-05 

460 

461 >>> float_to_frac(6.342974725370709e-05) 

462 (17853886631, 281474976710656) 

463 >>> 17853886631 / 281474976710656 

464 6.342974725370709e-05 

465 

466 >>> float_to_frac(8.759559844406795e-05) 

467 (3081996129, 35184372088832) 

468 >>> 3081996129 / 35184372088832 

469 8.759559844406795e-05 

470 

471 >>> float_to_frac(-9.6e-09) 

472 (-3, 312500000) 

473 >>> -3 / 312500000 

474 -9.6e-09 

475 

476 >>> float_to_frac(-0.4) 

477 (-2, 5) 

478 >>> -2 / 5 

479 -0.4 

480 

481 >>> float_to_frac(2e-10) 

482 (1, 5000000000) 

483 >>> 1 / 5000000000 

484 2e-10 

485 

486 >>> float_to_frac(0.3) 

487 (3, 10) 

488 >>> 3 / 10 

489 0.3 

490 

491 >>> float_to_frac(-3e-09) 

492 (-3, 1000000000) 

493 >>> -3 / 1000000000 

494 -3e-09 

495 

496 >>> float_to_frac(1e-07) 

497 (1, 10000000) 

498 >>> 1 / 10000000 

499 1e-07 

500 

501 >>> float_to_frac(-8e-08) 

502 (-1, 12500000) 

503 >>> -1 / 12500000 

504 -8e-08 

505 

506 >>> float_to_frac(-0.01) 

507 (-1, 100) 

508 >>> -1 / 100 

509 -0.01 

510 

511 >>> float_to_frac(1e-08) 

512 (1, 100000000) 

513 >>> 1 / 100000000 

514 1e-08 

515 

516 >>> float_to_frac(0.01) 

517 (1, 100) 

518 >>> 1 / 100 

519 0.01 

520 

521 >>> float_to_frac(-2e-06) 

522 (-1, 500000) 

523 >>> -1 / 500000 

524 -2e-06 

525 

526 >>> float_to_frac(-6e-08) 

527 (-3, 50000000) 

528 >>> -3 / 50000000 

529 -6e-08 

530 

531 >>> float_to_frac(7e-05) 

532 (7, 100000) 

533 >>> 7 / 100000 

534 7e-05 

535 

536 >>> float_to_frac(-1e+40) 

537 (-10000000000000000000000000000000000000000, 1) 

538 >>> -10000000000000000000000000000000000000000 / 1 

539 -1e+40 

540 

541 >>> float_to_frac(1e+40) 

542 (10000000000000000000000000000000000000000, 1) 

543 >>> 10000000000000000000000000000000000000000 / 1 

544 1e+40 

545 

546 >>> float_to_frac(1.6152587208080178e+161) 

547 (16152587208080177786162779887253130808505244217290853753086392891829620\ 

5481092892491941055809815064259587161746312711311289043311353137951968371237523\ 

549928335253504000, 1) 

550 """ 

551 value = try_int(value) 

552 if isinstance(value, int): 

553 return value, 1 

554 

555 minus: Final[bool] = value < 0.0 

556 if minus: 

557 value = -value 

558 

559 # Get the minimized fraction. 

560 num1, denom1 = __minimize_frac(value, *value.as_integer_ratio()) 

561 

562 # First, we convert the floating point number to a string, which 

563 # necessarily exactly represents the value. Then we will go and turn 

564 # the string into a fraction. 

565 value_str: Final[str] = float.__repr__(value) 

566 

567 end_idx: int = str.__len__(value_str) 

568 dot_idx: Final[int] = str.find(value_str, ".") 

569 exp_idx: Final[int] = str.find(value_str, "e") 

570 

571 denom2: int = 1 

572 multiplier: int = 1 

573 if exp_idx > 0: 

574 int_exp = int(value_str[exp_idx + 1:end_idx]) 

575 if int_exp < 0: 

576 denom2 = 10 ** (-int_exp) 

577 else: 

578 multiplier = 10 ** int_exp 

579 end_idx = exp_idx 

580 

581 num2: int = 0 

582 if dot_idx >= 0: 

583 int_denom_2 = 10 ** (end_idx - dot_idx - 1) 

584 num2 = ((int(value_str[:dot_idx]) * int_denom_2) 

585 + int(value_str[dot_idx + 1:end_idx])) 

586 denom2 *= int_denom_2 

587 else: 

588 num2 = int(value_str[:end_idx]) 

589 

590 num2 *= multiplier 

591 if num2 / denom2 == value: 

592 num1, denom1 = __choose_frac(*__minimize_frac( 

593 value, num2, denom2), num1, denom1) 

594 return (-num1 if minus else num1), denom1 

595 

596 

597def try_int_div(a: int, b: int) -> int | float: 

598 """ 

599 Try to divide two integers at best precision. 

600 

601 Floating point divisions can incur some loss of precision. We try 

602 to avoid this here as much as possible. First, we check if `a` is 

603 divisible by `b` without any fractional part. If this is true, then 

604 we can do a pure integer division without loss of any precision. 

605 In other words, if `a % b == 0`, then `a / b` is itself an integer, 

606 i.e., can be represented exactly. 

607 

608 Otherwise, it will have a fractional part, so it would ideally be a 

609 `float`. 

610 Well. 

611 

612 What if the integer `a` is really large? Converting it to floating 

613 point number may then incur a loss of precision. And why do we convert 

614 it to a `float` in the first place? To properly represent the fractional 

615 part. Because the integer part is `a // b` is an integer. The fractional 

616 part `f` is then by definition `f = (a % b) // b == (a - b*(a // b)) / b`. 

617 Obviously, `0<f<1`. It may be entirely possible that we lose several full 

618 integer *digits* by converting the integer `a` to a `float` ... just to 

619 then be able to add a fraction `f`. So this loss of precision may be much 

620 larger than the little fractional part that we would add (which will 

621 always be in `(0, 1)`. In such a case, it may be much better to stay in 

622 the realm of integers and instead lose the fractional part. Thus, we also 

623 test whether multiplying the result of the floating point computation with 

624 `b` is closer to `a` than integer results rounded in either direction. 

625 

626 During this procedure, we try to pick the result closest to the original 

627 value. This, however, may only be possible if we can actually compute the 

628 difference. If we deal with floating point numbers and integers, some 

629 integers may simply be too large for being ever converted to a float. In 

630 this case, we remain entirely in the integer realm and only round if need 

631 be. 

632 

633 :param a: the first integer 

634 :param b: the second integer 

635 :returns: a/b, either as `int` or as `float` but always a finite value 

636 :raises ZeroDivisionError: if `b==0` 

637 :raises TypeError: if `a` or `b` are not integers 

638 

639 >>> print(try_int_div(10, 2)) 

640 5 

641 >>> print(try_int_div(10, -2)) 

642 -5 

643 >>> print(try_int_div(-10, 2)) 

644 -5 

645 >>> print(try_int_div(-10, -2)) 

646 5 

647 >>> print(type(try_int_div(10, 2))) 

648 <class 'int'> 

649 >>> print(try_int_div(10, 3)) 

650 3.3333333333333335 

651 >>> print(try_int_div(-10, 3)) 

652 -3.3333333333333335 

653 >>> print(try_int_div(10, -3)) 

654 -3.3333333333333335 

655 >>> print(try_int_div(-10, -3)) 

656 3.3333333333333335 

657 >>> print(type(try_int_div(10, 3))) 

658 <class 'float'> 

659 >>> print(try_int_div(9007199254740992, 1)) 

660 9007199254740992 

661 >>> print(try_int_div(2109792310235001520128, 234234)) 

662 9007199254740992 

663 >>> print(try_int_div(2109792310235001520128, 234235)) 

664 9007160801054503 

665 >>> print(try_int_div(2109792310235001520128, 234233)) 

666 9007237708755818 

667 >>> large = 123456789012345678901234567890123456789012345678901234567\ 

66889012345678901234567890123456789012345678901234567890123456789012345678901234\ 

66956789012345678901234567890123456789012345678901234567890123456789012345678901\ 

67023456789012345678901234567890123456789012345678901234567890123456789012345678\ 

67190123456789012345678901234567890123456789012345678901234567890123456789012345\ 

67267890123456789012345678901234567890123456789012345678901234567890123456789012\ 

6733456789012345678901234567890123456789012345678901234567890123456789012345678\ 

67490123456789012345678901234567890123456789012345678901234567890123456789012345\ 

675678901234567890123456789012345678901234567890 

676 

677 >>> try: 

678 ... large / 1 

679 ... except OverflowError as oe: 

680 ... print(oe) 

681 integer division result too large for a float 

682 >>> try_int_div(large, 1) 

683 123456789012345678901234567890123456789012345678901234567\ 

68489012345678901234567890123456789012345678901234567890123456789012345678901234\ 

68556789012345678901234567890123456789012345678901234567890123456789012345678901\ 

68623456789012345678901234567890123456789012345678901234567890123456789012345678\ 

68790123456789012345678901234567890123456789012345678901234567890123456789012345\ 

68867890123456789012345678901234567890123456789012345678901234567890123456789012\ 

6893456789012345678901234567890123456789012345678901234567890123456789012345678\ 

69090123456789012345678901234567890123456789012345678901234567890123456789012345\ 

691678901234567890123456789012345678901234567890 

692 

693 >>> try_int_div(large * 7, 1 * 7) 

694 123456789012345678901234567890123456789012345678901234567\ 

69589012345678901234567890123456789012345678901234567890123456789012345678901234\ 

69656789012345678901234567890123456789012345678901234567890123456789012345678901\ 

69723456789012345678901234567890123456789012345678901234567890123456789012345678\ 

69890123456789012345678901234567890123456789012345678901234567890123456789012345\ 

69967890123456789012345678901234567890123456789012345678901234567890123456789012\ 

7003456789012345678901234567890123456789012345678901234567890123456789012345678\ 

70190123456789012345678901234567890123456789012345678901234567890123456789012345\ 

702678901234567890123456789012345678901234567890 

703 

704 >>> res = try_int_div(large, 7) 

705 >>> print(res) 

706 1763668414462081127160493827001763668414462081127160493827001763668414462\ 

70708112716049382700176366841446208112716049382700176366841446208112716049382700\ 

70817636684144620811271604938270017636684144620811271604938270017636684144620811\ 

70927160493827001763668414462081127160493827001763668414462081127160493827001763\ 

71066841446208112716049382700176366841446208112716049382700176366841446208112716\ 

71104938270017636684144620811271604938270017636684144620811271604938270017636684\ 

71214462081127160493827001763668414462081127160493827001763668414462081127160493\ 

71382700176366841446208112716049382700176366841446208112716049382700176366841446\ 

714208112716049382700176366841 

715 >>> large - (res * 7) 

716 3 

717 

718 >>> res = try_int_div(large - 1, 7) 

719 >>> print(res) 

720 1763668414462081127160493827001763668414462081127160493827001763668414462\ 

72108112716049382700176366841446208112716049382700176366841446208112716049382700\ 

72217636684144620811271604938270017636684144620811271604938270017636684144620811\ 

72327160493827001763668414462081127160493827001763668414462081127160493827001763\ 

72466841446208112716049382700176366841446208112716049382700176366841446208112716\ 

72504938270017636684144620811271604938270017636684144620811271604938270017636684\ 

72614462081127160493827001763668414462081127160493827001763668414462081127160493\ 

72782700176366841446208112716049382700176366841446208112716049382700176366841446\ 

728208112716049382700176366841 

729 >>> (large - 1) - (res * 7) 

730 2 

731 

732 >>> res = try_int_div(large + 1, 7) 

733 >>> print(res) 

734 1763668414462081127160493827001763668414462081127160493827001763668414462\ 

73508112716049382700176366841446208112716049382700176366841446208112716049382700\ 

73617636684144620811271604938270017636684144620811271604938270017636684144620811\ 

73727160493827001763668414462081127160493827001763668414462081127160493827001763\ 

73866841446208112716049382700176366841446208112716049382700176366841446208112716\ 

73904938270017636684144620811271604938270017636684144620811271604938270017636684\ 

74014462081127160493827001763668414462081127160493827001763668414462081127160493\ 

74182700176366841446208112716049382700176366841446208112716049382700176366841446\ 

742208112716049382700176366842 

743 >>> (large + 1) - (res * 7) 

744 -3 

745 

746 >>> try: 

747 ... try_int_div(0, 0) 

748 ... except ZeroDivisionError as zde: 

749 ... print(zde) 

750 integer division or modulo by zero 

751 

752 >>> try: 

753 ... try_int_div(1, 0) 

754 ... except ZeroDivisionError as zde: 

755 ... print(zde) 

756 integer division or modulo by zero 

757 

758 >>> try: 

759 ... try_int_div(-1, 0) 

760 ... except ZeroDivisionError as zde: 

761 ... print(zde) 

762 integer division or modulo by zero 

763 

764 >>> try_int_div(153, 17) 

765 9 

766 >>> try_int_div(-153, 17) 

767 -9 

768 >>> try_int_div(626240198453350272215815210991, 180) 

769 3479112213629723734532306728 

770 >>> try_int_div(-626240198453350272215815210991, 180) 

771 -3479112213629723734532306728 

772 >>> try_int_div(312641328808813509022862142116, 184) 

773 1699137656569638635993815990 

774 >>> try_int_div(-312641328808813509022862142116, 184) 

775 -1699137656569638635993815990 

776 >>> try_int_div(300228563787891776398328530521, 6) 

777 50038093964648629399721421754 

778 >>> try_int_div(300228563787891776398328530520, 6) 

779 50038093964648629399721421753 

780 >>> try_int_div(-300228563787891776398328530521, 6) 

781 -50038093964648629399721421754 

782 

783 >>> try_int_div(153, -17) 

784 -9 

785 >>> try_int_div(-153, -17) 

786 9 

787 >>> try_int_div(626240198453350272215815210991, -180) 

788 -3479112213629723734532306728 

789 >>> try_int_div(-626240198453350272215815210991, -180) 

790 3479112213629723734532306728 

791 >>> try_int_div(312641328808813509022862142116, -184) 

792 -1699137656569638635993815990 

793 >>> try_int_div(-312641328808813509022862142116, -184) 

794 1699137656569638635993815990 

795 >>> try_int_div(300228563787891776398328530521, -6) 

796 -50038093964648629399721421754 

797 >>> try_int_div(-300228563787891776398328530521, -6) 

798 50038093964648629399721421754 

799 

800 >>> try_int_div(471560594207063922064065980174, 160) 

801 2947253713794149512900412376 

802 

803 >>> try_int_div(7995687632, 605623302520652727304862084393) 

804 1.3202410803417417e-20 

805 

806 >>> try_int_div(308201705551808339041017943851, 23) 

807 13400074154426449523522519298 

808 

809 >>> try_int_div(899348944156468188933109403939, 54) 

810 16654610076971633128390914888 

811 

812 >>> try_int_div(494818043590514116668712249977, 42) 

813 11781381990250336111159815476 

814 

815 >>> try_int_div(738070379515233920, 205) 

816 3600343314708458 

817 

818 >>> try_int_div(3502315235185234554036893628, 2914324106703) 

819 1201759003787480 

820 

821 >>> try_int_div(7410628973168661103, 3869) 

822 1915386139356076.8 

823 

824 >>> try_int_div(1230161216449799063065724370370, 4689247521470) 

825 262336592559346126 

826 

827 >>> try_int_div(1052870426843577701006624798274, 28) 

828 37602515244413489321665171367 

829 

830 >>> try_int_div(218235816140080518429116, 65180391) 

831 3348182065064330.5 

832 

833 >>> try_int_div(542681063252950460111634072, 1417) 

834 382978873149576894927053 

835 

836 >>> try_int_div(6347580784084238615827, 9508617) 

837 667560885466754.9 

838 

839 >>> try_int_div(25864142832167873073008, 8621014) 

840 3000127691727199.5 

841 

842 >>> try_int_div(35377667634669293542601414338, 8678403583) 

843 4076517909811212054 

844 

845 >>> try_int_div(1423204593957046760175, 6) 

846 237200765659507793363 

847 

848 >>> try_int_div(1959151753859121847452742, 155502) 

849 12598884605079817928 

850 

851 >>> try_int_div(153429321515534965993379305, 15165212220) 

852 10117189215010864 

853 

854 >>> try_int_div(638685779810794590594721888599, 6355644831674) 

855 100491106209686119 

856 

857 >>> try_int_div(14634805, 3163458943542033136) 

858 4.626203551614245e-12 

859 

860 >>> try_int_div(2728490692514068837390, 1134) 

861 2406076448425104795 

862 

863 >>> try_int_div(52133244, 2145832361321597595907) 

864 2.4295115005112346e-14 

865 

866 >>> try_int_div(989732710522254024, 870) 

867 1137623805197993.2 

868 

869 >>> try_int_div(1015, 4100715151904) 

870 2.475178017494646e-10 

871 

872 >>> try_int_div(750731, 60649291746) 

873 1.2378231936228884e-05 

874 

875 >>> try_int_div(7972413701754221571302566, 1660418690) 

876 4801447821425222 

877 

878 >>> try_int_div(356135676699525125944, 46208) 

879 7707229845471025 

880 

881 >>> try_int_div(10448177882855961291672, 1739414) 

882 6006722886475538 

883 

884 >>> try_int_div(739391142068058031063862, 8456) 

885 87439822855730609161 

886 

887 >>> try_int_div(316514845935646909034735039673, 32172) 

888 9838208564455020173900753 

889 

890 >>> try_int_div(4158458869534984918534998087, 30) 

891 138615295651166163951166603 

892 

893 >>> try_int_div(102306108211747181839762853503, 29118) 

894 3513500522417308257427119 

895 

896 >>> all(try_int_div(1, y) == (1 / y) for y in range(2, 10)) 

897 True 

898 

899 >>> all(try_int_div(2, y) == (2 / y) for y in range(3, 10)) 

900 True 

901 

902 >>> try_int_div(820432337843942760, 85) 

903 9652145151105209 

904 

905 >>> try_int_div(84050617, 3577089862) 

906 0.023496926340286613 

907 

908 >>> try_int_div(812060021745358856, 996816531) 

909 814653445.7356013 

910 

911 >>> try_int_div(38029336, 472982612237) 

912 8.040324319775297e-05 

913 

914 >>> try_int_div(50229909719349513, 9) 

915 5581101079927724 

916 

917 >>> try_int_div(61320503685013026, 2728161164469337) 

918 22.476862614874392 

919 

920 >>> try_int_div(23134400382350491, 8) 

921 2891800047793811.5 

922 

923 >>> try_int_div(12510965, 67561917605841203) 

924 1.8517776645993746e-10 

925 

926 >>> try_int_div(27246707584980173, 4) 

927 6811676896245043 

928 

929 >>> try_int_div(135385235231741420, 6) 

930 22564205871956903 

931 

932 >>> try_int_div(90, 153429501803) 

933 5.865886217603572e-10 

934 

935 >>> try_int_div(734553401849288248, 951111) 

936 772310909924.5916 

937 

938 >>> try_int_div(9820998979656, 4239082999146) 

939 2.3167744018304255 

940 

941 >>> try_int_div(133105116441194557, 17) 

942 7829712731834974 

943 

944 >>> try_int_div(1004604250960040176, 14) 

945 71757446497145727 

946 

947 >>> try_int_div(246148731190755584, 6) 

948 41024788531792597 

949 

950 >>> try_int_div(991564, 72057594037927936) 

951 1.3760714789867734e-11 

952 

953 >>> try_int_div(2623725286393384, 139634775865757) 

954 18.789912972079378 

955 

956 >>> try_int_div(63010439554808723, 9) 

957 7001159950534303 

958 

959 >>> try_int_div(2801452, 427673) 

960 6.550453266865105 

961 

962 >>> try_int_div(14177411351567, 349688426689780) 

963 0.04054298132132421 

964 

965 >>> try_int_div(126660394112336947, 368) 

966 344185853566133 

967 

968 >>> try_int_div(1031427640916897886, 7) 

969 147346805845271127 

970 

971 >>> try_int_div(33290935002573849, 2) 

972 16645467501286925 

973 

974 >>> try_int_div(209062743096233332, 64) 

975 3266605360878646 

976 

977 >>> try_int_div(253174817711179642, 57) 

978 4441663468617186.5 

979 

980 >>> try_int_div(29462133006911895, 24943246) 

981 1181166757.8033707 

982 

983 >>> try_int_div(93475849985676023, 60673699562) 

984 1540632.1134276118 

985 

986 >>> try_int_div(-16, -16) 

987 1 

988 

989 >>> try_int_div(242, 150) 

990 1.6133333333333333 

991 

992 >>> try_int_div(-547, -698) 

993 0.7836676217765043 

994 

995 >>> try_int_div(463, 105) 

996 4.40952380952381 

997 

998 >>> try_int_div(-148, -203) 

999 0.729064039408867 

1000 

1001 >>> try_int_div(0, -25) 

1002 0 

1003 

1004 >>> try_int_div(24, -177) 

1005 -0.13559322033898305 

1006 

1007 >>> try_int_div(-166, 186) 

1008 -0.8924731182795699 

1009 

1010 >>> try_int_div(-608143760099358008316, 16) 

1011 -38008985006209875520 

1012 

1013 >>> try_int_div(-6917198296130591233, 2932) 

1014 -2359208150112753 

1015 

1016 >>> try_int_div(-40068404846647758412, 2431) 

1017 -16482272664190769 

1018 

1019 >>> try_int_div(809884532216820092, -80) 

1020 -10123556652710251 

1021 

1022 >>> try_int_div(-9428902965475478968, -1946) 

1023 4845273877428304 

1024 

1025 >>> try_int_div(94881103250893722164, 174) 

1026 545293696844216794 

1027 

1028 >>> try_int_div(558275776531402194, 196) 

1029 2848345798629603 

1030 

1031 >>> try_int_div(-5470954588630039684, -1425) 

1032 3839266377985993 

1033 

1034 >>> x = 52051907638184435686872083537997907834107397007408814104887055550\ 

103562651162584515572343506336421788506498764827396236326664325857298113627143047\ 

103651960058116028166468699987611855171916670918181477368243402962224866666859483\ 

103727106641344727853102203836313167806475289935694133683049416723665601922267867\ 

103843423073756386687744959209264639678418438341653942959851578625489694262628828\ 

103901502680997461128779292890987864647476268814586685317933377503211673153129336\ 

104003 

1041 >>> y = -1390354761575126197823721816501267487365151253235077143453455350\ 

104242071715351921681111638693456863646113210365045493352 

1043 >>> try_int_div(x, y) 

1044 -374378605207314720094873468836285633819929467061778300817269205498029513\ 

104565402433513265745971266615921400317429877366670340545494622384105823993306348\ 

104652843139170606054848072439314573682429224161889047753873201343420620892557508\ 

104762700497112616274728625215898978448963212698759159253800300962780904741771804\ 

1048645167943738988195771748632862162 

1049 

1050 >>> x = -2371099749665044990658141554075074237566818135879925492767658755\ 

105191466431785344954996723447284617738104563308335316636469414432945133872626562\ 

105247514537471872530515191642703803616012611248118482218872482697827387761273565\ 

105386825000794528611072492997052827719254891404531142028847153355973782623685875\ 

105455388033455119839506838214696423221276537787120528956164822252461892023157114\ 

105502799038227958323905920667727058869625829951896827916647011550854954614174228\ 

10560327582498733595995697631187168710055335569609973997123439124471957303314220 

1057 >>> y = 23343578649740692313745114608806472033684574464287511781560680643\ 

105878701796266433578371331497 

1059 >>> try_int_div(x, y) 

1060 -101573961098350440775039372298606794268469908577045401233130406288914282\ 

106192918893248309802232281829255680862092165642164462698933290177430764947955661\ 

106287652857199541665161754173953190591131037518696771010612358486878465958542091\ 

106354914381056640460582835505879418330902968200425941036454902030259440742425865\ 

106493440206970165106076445287259870479244010983474198088053313334979762284802017\ 

10651153886834216445854191456742632611734684212485945595091 

1066 

1067 >>> x = 40410061884642793201602670149115414017394734761323545080847020296\ 

106825252534757283636685784303675489231680221820894136736092474359799408796002401\ 

106987921742390653841150636438854369236710256224057607718115525186887758631639670\ 

107082468402380054839668544662058030344964306015945683011983835531538788295592437\ 

107165716882229369219075665520432950975969718863463181388344946182200519006147179\ 

107264461315530742161850062785306859778524746068148875909170944464910610460508750\ 

1073707051996751159775959805908309 

1074 >>> try_int_div(x, 455824846955327759894405021890034373481341853483437732) 

1075 8865260890133771894279961299939580714408739728863480476996077470796169955\ 

107643085620471927592765951912973058793385603301586629745150439814928912960267883\ 

107766400412702824502182496145839556516762964408587815797906905800899307550385283\ 

107859797468484699310297417864757571840405529641545438878013277855865542975695833\ 

107988146919245416237227940140677498927052487483630425657258239092995434299050034\ 

1080021769275549143357256693312576946435784210430 

1081 

1082 >>> x = -6232936786190843094006005233017695847240511752635979691082519622\ 

108389671822451515008492890632740917254159586399372900046205300278719490624751881\ 

108476026655230475873682972201137642189143060727745274448518821915960488822897219\ 

108519105433159267999911968464110051361652323090653411336081715581855840751539611\ 

108644549510551090842769903146173949669899963195645511983189442245054559694895154\ 

108728690282113755080383328799009405959846487733552322199361433571441631699077621\ 

1088235779724223724145601506861527256455350316142 

1089 >>> y = -672227780379448967615099076221459579351218967417588220 

1090 >>> try_int_div(x, y) 

1091 9272060703401113879042492429626067042706252670698636022710487050821126676\ 

109243853746484009770979124008642489211907343513622775392767108564692587266167738\ 

109393885766234994122772733590708011238049750176667082969644614903930265971589853\ 

109426674807243894898200722561326294551914700598739493395761954564220400052036328\ 

109558909594484124974270252599224045683880122299500249240856446063861849302671425\ 

109605617327729864850535306650812894643758024556770793387750201 

1097 

1098 >>> try: 

1099 ... try_int_div(1.0, 2) 

1100 ... except TypeError as te: 

1101 ... print(te) 

1102 a should be an instance of int but is float, namely 1.0. 

1103 

1104 >>> try: 

1105 ... try_int_div(1, 2.0) 

1106 ... except TypeError as te: 

1107 ... print(te) 

1108 b should be an instance of int but is float, namely 2.0. 

1109 """ 

1110 if not isinstance(a, int): 

1111 raise type_error(a, "a", int) 

1112 if not isinstance(b, int): 

1113 raise type_error(b, "b", int) 

1114 minus: bool = False 

1115 if a < 0: 

1116 minus = True 

1117 a = -a 

1118 if b < 0: 

1119 minus = not minus 

1120 b = -b 

1121 

1122 # Let's say a = 762 and b = 204. 

1123 # We first compute the GCD to reduce both sides of the equation. 

1124 the_gcd: Final[int] = gcd(a, b) # == 6 in the example 

1125 a //= the_gcd # == 127 in the example 

1126 b //= the_gcd # == 34 in the example 

1127 

1128 # First, let's compute the result of the pure integer division. 

1129 int_res_1: Final[int] = a // b # == 3 in our example 

1130 int_mult_1: Final[int] = int_res_1 * b # == 102 in the example 

1131 if int_mult_1 == a: # if there is no rest, then we can stop here 

1132 return -int_res_1 if minus else int_res_1 

1133 

1134 int_frac_1: Final[int] = a - int_mult_1 # == 25 in the example 

1135 int_res_2: Final[int] = int_res_1 + 1 # rounding up, == 4 in the example 

1136 # Compute int_frac_2 == (int_res_2 * b - a, but simplified: 

1137 int_frac_2: Final[int] = b - int_frac_1 # == 9 in the example 

1138 

1139 # OK, there may be a loss of precision if we do the floating point 

1140 # computation. But we should try it now anyway. 

1141 # if `a` and `b` can exactly be represented as floats (by being not more 

1142 # than `__DBL_INT_LIMIT_P_I`, then we are OK and can directly use the 

1143 # result. Otherwise, if the result is between the lower and the upper 

1144 # limit, then we will also take it. This would mean to basically default 

1145 # to the normal division in Python in cases where it falls into the 

1146 # expected range of possible results. 

1147 with suppress(ArithmeticError): 

1148 float_res = __try_int(a / b) # == 3.5588235294117645 in the example 

1149 if ((a <= __DBL_INT_LIMIT_P_I) and (b <= __DBL_INT_LIMIT_P_I)) or ( 

1150 int_res_1 < float_res < int_res_2): 

1151 return -float_res if minus else float_res 

1152 

1153 best_result: Final[int] = \ 

1154 int_res_2 if int_frac_2 <= int_frac_1 else int_res_1 

1155 return -best_result if minus else best_result # fix sign of result 

1156 

1157 

1158def try_float_int_div(a: int | float, b: int) -> int | float: 

1159 """ 

1160 Try to divide a float by an int at best precision. 

1161 

1162 :param a: the first number, which is either a float or an int 

1163 :param b: the second number, which must be an int 

1164 :returns: `a/b`, but always finite 

1165 

1166 :raises ValueError: if either one of the arguments or the final result 

1167 would not be finite 

1168 :raises TypeError: if either one of `a` or `b` is neither an integer nor 

1169 a float 

1170 

1171 >>> try_float_int_div(10, 2) 

1172 5 

1173 

1174 >>> try_float_int_div(10.0, 2) 

1175 5 

1176 

1177 >>> try_float_int_div(10, 3) 

1178 3.3333333333333335 

1179 

1180 >>> try_float_int_div(-10, 2) 

1181 -5 

1182 

1183 >>> try_float_int_div(-10.2, 2) 

1184 -5.1 

1185 

1186 >>> try_float_int_div(-10.0, 2) 

1187 -5 

1188 

1189 >>> try_float_int_div(-10, 3) 

1190 -3.3333333333333335 

1191 

1192 >>> print(type(try_float_int_div(10.0, 2))) 

1193 <class 'int'> 

1194 

1195 >>> print(type(try_float_int_div(10.0, 3))) 

1196 <class 'float'> 

1197 

1198 >>> try: 

1199 ... try_float_int_div(10, 0.5) 

1200 ... except TypeError as te: 

1201 ... print(te) 

1202 b should be an instance of int but is float, namely 0.5. 

1203 

1204 >>> from math import inf, nan 

1205 >>> try: 

1206 ... try_float_int_div(1.0, 0) 

1207 ... except ZeroDivisionError as zde: 

1208 ... print(zde) 

1209 integer division or modulo by zero 

1210 

1211 >>> try: 

1212 ... try_float_int_div(inf, 0) 

1213 ... except ValueError as ve: 

1214 ... print(ve) 

1215 Value must be finite, but is inf. 

1216 

1217 >>> try: 

1218 ... try_float_int_div(-inf, 0) 

1219 ... except ValueError as ve: 

1220 ... print(ve) 

1221 Value must be finite, but is -inf. 

1222 

1223 >>> try: 

1224 ... try_float_int_div(nan, 0) 

1225 ... except ValueError as ve: 

1226 ... print(ve) 

1227 Value must be finite, but is nan. 

1228 

1229 >>> try: 

1230 ... try_float_int_div(1, inf) 

1231 ... except TypeError as te: 

1232 ... print(te) 

1233 b should be an instance of int but is float, namely inf. 

1234 

1235 >>> try: 

1236 ... try_float_int_div("y", 1) 

1237 ... except TypeError as te: 

1238 ... print(te) 

1239 value should be an instance of any in {float, int} but is str, namely 'y'. 

1240 

1241 >>> try: 

1242 ... try_float_int_div(1, "x") 

1243 ... except TypeError as te: 

1244 ... print(te) 

1245 b should be an instance of int but is str, namely 'x'. 

1246 """ 

1247 if not isinstance(b, int): 

1248 raise type_error(b, "b", int) 

1249 a = try_int(a) 

1250 if isinstance(a, int): 

1251 return try_int_div(a, b) 

1252 return __try_int(a / b) 

1253 

1254 

1255#: the maximum value of a root that can be computed with floats exactly 

1256__MAX_I_ROOT: Final[int] = __DBL_INT_LIMIT_P_I * __DBL_INT_LIMIT_P_I 

1257 

1258 

1259def try_int_sqrt(value: int) -> int | float: 

1260 """ 

1261 Try to compute the square root of a potentially large integer. 

1262 

1263 :param value: the value 

1264 :returns: the square root 

1265 :raises ValueError: if `value` is negative 

1266 :raises TypeError: if `value` is not an integer 

1267 

1268 >>> try_int_sqrt(0) 

1269 0 

1270 

1271 >>> try_int_sqrt(1) 

1272 1 

1273 

1274 >>> try_int_sqrt(2) 

1275 1.4142135623730951 

1276 

1277 >>> try_int_sqrt(3) 

1278 1.7320508075688772 

1279 

1280 >>> try_int_sqrt(4) 

1281 2 

1282 

1283 >>> try_int_sqrt(5) 

1284 2.23606797749979 

1285 

1286 >>> try_int_sqrt(6) 

1287 2.449489742783178 

1288 

1289 >>> try_int_sqrt(7) 

1290 2.6457513110645907 

1291 

1292 >>> try_int_sqrt(8) 

1293 2.8284271247461903 

1294 

1295 >>> try_int_sqrt(9) 

1296 3 

1297 

1298 # exact result: 67108864.0000000074505805969238277 

1299 >>> try_int_sqrt(4503599627370497) 

1300 67108864 

1301 >>> 67108864 * 67108864 

1302 4503599627370496 

1303 >>> sqrt(4503599627370497) 

1304 67108864.0 

1305 >>> sqrt(4503599627370497) * sqrt(4503599627370497) 

1306 4503599627370496.0 

1307 

1308 # exact result: 1592262918131443.14115595358963 

1309 >>> try_int_sqrt(2535301200456458802993406410753) 

1310 1592262918131443.2 

1311 >>> sqrt(2535301200456458802993406410753) 

1312 1592262918131443.2 

1313 

1314 # exact result: 6369051672525772.564623814 

1315 >>> try_int_sqrt(40564819207303340847894502572033) 

1316 6369051672525773 

1317 >>> sqrt(40564819207303340847894502572033) 

1318 6369051672525773.0 

1319 

1320 # exact result: 50952413380206180.51699051486817387 

1321 >>> try_int_sqrt(2596148429267413814265248164610049) 

1322 50952413380206181 

1323 >>> sqrt(2596148429267413814265248164610049) 

1324 5.0952413380206184e+16 

1325 

1326 # exact result: 47695509376267.99690952215843525 

1327 >>> try_int_sqrt(2274861614661668407597778085) 

1328 47695509376267.99 

1329 >>> sqrt(2274861614661668407597778085) 

1330 47695509376267.99 

1331 

1332 # exact result: 9067560306493833.1123015448971368313360 

1333 >>> try_int_sqrt(82220649911902536690031728766315) 

1334 9067560306493833 

1335 >>> sqrt(82220649911902536690031728766315) 

1336 9067560306493832.0 

1337 

1338 >>> try_int_sqrt(1156) 

1339 34 

1340 >>> 34 * 34 

1341 1156 

1342 >>> sqrt(1156) 

1343 34.0 

1344 >>> 34.0 * 34.0 

1345 1156.0 

1346 

1347 >>> try_int_sqrt(1005) 

1348 31.701734968294716 

1349 >>> 31.701734968294716 * 31.701734968294716 

1350 1005.0 

1351 >>> sqrt(1005) 

1352 31.701734968294716 

1353 >>> 31.701734968294716 * 31.701734968294716 

1354 1005.0 

1355 

1356 exact result: 1098367625620897554397104127853022914763109648022\ 

1357928865503114153469686909343624968339609542505832728796367409822636937\ 

135828593951807995466301001184452657840914432 

1359 >>> try_int_sqrt(int("1206411441012088169768424908631547135410050\ 

1360450349701156359323012992324468898745458674194715627653148741645085002\ 

1361880167432962708099995812635821183919553390204438671018341579206970136\ 

1362807811815836079357669821219116858017489215282754293788095448310134150\ 

13636291035205862448784848059094859987648259778470316291228729945882624")) 

1364 10983676256208975543971041278530229147631096480229288655031141534\ 

1365696869093436249683396095425058327287963674098226369372859395180799546\ 

13666301001184452657840914432 

1367 

1368 # exact result: 112519976.73369080909552361 

1369 >>> try_int_sqrt(12660745164150321) 

1370 112519976.73369081 

1371 >>> 112519976.73369081 * 112519976.73369081 

1372 1.2660745164150322e+16 

1373 >>> sqrt(12660745164150321) 

1374 112519976.7336908 

1375 >>> 112519976.7336908 * 112519976.7336908 

1376 1.2660745164150318e+16 

1377 

1378 >>> try_int_sqrt(12369445361672285) 

1379 111218008.26157734 

1380 >>> sqrt(12369445361672285) 

1381 111218008.26157734 

1382 

1383 # exact result: 94906265.624251558157461955425 

1384 >>> try_int_sqrt(9007199254740993) 

1385 94906265.62425156 

1386 >>> 94906265.62425156 * 94906265.62425156 

1387 9007199254740994.0 

1388 >>> sqrt(9007199254740993) 

1389 94906265.62425156 

1390 >>> 94906265.62425156 * 94906265.62425156 

1391 9007199254740994.0 

1392 

1393 # exact result: 126969687.206733737782866 

1394 >>> try_int_sqrt(16121301469375805) 

1395 126969687.20673373 

1396 >>> 126969687.20673373 * 126969687.20673373 

1397 1.6121301469375804e+16 

1398 >>> sqrt(16121301469375805) 

1399 126969687.20673373 

1400 >>> 126969687.20673373 * 126969687.20673373 

1401 1.6121301469375804e+16 

1402 

1403 # exact result: 94906265.6242515686941740831 

1404 # here we are off a bit! 

1405 >>> try_int_sqrt(9007199254740995) 

1406 94906265.62425156 

1407 >>> 94906265.62425156 * 94906265.62425156 

1408 9007199254740994.0 

1409 >>> sqrt(9007199254740995) 

1410 94906265.62425157 

1411 >>> 94906265.62425157 * 94906265.62425157 

1412 9007199254740996.0 

1413 

1414 # exact result: 102406758.28296330267545316 

1415 >>> try_int_sqrt(10487144142025273) 

1416 102406758.2829633 

1417 >>> 102406758.2829633 * 102406758.2829633 

1418 1.0487144142025274e+16 

1419 >>> sqrt(10487144142025273) 

1420 102406758.28296329 

1421 >>> 102406758.28296329 * 102406758.28296329 

1422 1.048714414202527e+16 

1423 

1424 # exact result: 101168874.5492688823358 

1425 >>> try_int_sqrt(10235141177565705) 

1426 101168874.54926889 

1427 >>> 101168874.54926889 * 101168874.54926889 

1428 1.0235141177565706e+16 

1429 >>> sqrt(10235141177565705) 

1430 101168874.54926887 

1431 >>> 101168874.54926887 * 101168874.54926887 

1432 1.0235141177565702e+16 

1433 

1434 # exact result: 123961449.976073299398431984 

1435 >>> try_int_sqrt(15366441080170523) 

1436 123961449.9760733 

1437 >>> 123961449.9760733 * 123961449.9760733 

1438 1.5366441080170522e+16 

1439 >>> sqrt(15366441080170523) 

1440 123961449.97607331 

1441 >>> 123961449.97607331 * 123961449.97607331 

1442 1.5366441080170526e+16 

1443 

1444 # exact result: 4760418939079673.01527272985 

1445 >>> try_int_sqrt(22661588475548439582669426672241) 

1446 4760418939079673 

1447 >>> 4760418939079673 * 4760418939079673 

1448 22661588475548439437260241786929 

1449 >>> sqrt(22661588475548439582669426672241) 

1450 4760418939079673.0 

1451 >>> 4760418939079673.0 * 4760418939079673.0 

1452 2.266158847554844e+31 

1453 

1454 # exact result: 5712179292532910.79362200453777547 

1455 >>> try_int_sqrt(32628992270041785263905793906381) 

1456 5712179292532911 

1457 >>> 5712179292532911 * 5712179292532911 

1458 32628992270041787621642018133921 

1459 >>> sqrt(32628992270041785263905793906381) 

1460 5712179292532911.0 

1461 >>> 5712179292532911.0 * 5712179292532911.0 

1462 3.2628992270041787e+31 

1463 

1464 >>> try: 

1465 ... try_int_sqrt(-1) 

1466 ... except ValueError as ve: 

1467 ... print(ve) 

1468 Compute the root of -1 ... really? 

1469 

1470 >>> try: 

1471 ... try_int_sqrt(1.0) 

1472 ... except TypeError as te: 

1473 ... print(te) 

1474 value should be an instance of int but is float, namely 1.0. 

1475 """ 

1476 if not isinstance(value, int): 

1477 raise type_error(value, "value", int) 

1478 if value < 0: 

1479 raise ValueError(f"Compute the root of {value} ... really?") 

1480 

1481 # First, let's compute the integer root. This is basically the 

1482 # rounded-down version of the actual root. The integer root (isqrt) is the 

1483 # lower limit for the result. 

1484 # In the odd chance that this is already the correct result, we can 

1485 # directly stop and return it. 

1486 result_low: Final[int] = isqrt(value) 

1487 diff_low: Final[int] = value - (result_low * result_low) 

1488 if diff_low <= 0: 

1489 # Notice: If we get here, then seemingly `isqrt(value) == sqrt(value)` 

1490 # in Python's implementation if `value` the result fits into the 

1491 # float range (I think). 

1492 return result_low 

1493 

1494 # First, we use the floating point sqrt for all numbers that can exactly 

1495 # be represented as floating point numbers. Of course we try to convert 

1496 # the result to integers. 

1497 if value <= __DBL_INT_LIMIT_P_I: # default to the normal Python sqrt. 

1498 return __try_int(sqrt(value)) # we can compute the exact square root 

1499 

1500 # `value` is bigger than 2 ** 53 and definitely does not fit into a float 

1501 # without losing precision. 

1502 # We cannot accurately compute the root of value, because transforming 

1503 # value to an int will already lead to a loss of precision. 

1504 # However, what if sqrt(value) < 2 ** 53? 

1505 # In this case, we *could* represent some fractional digits in the 

1506 # result. But we cannot get them using `sqrt`. So we do a trick: 

1507 # `root(a) = root(a * mul * mul) / mul`. 

1508 # We compute the integer square root of `value` times some value `mul`. 

1509 # We pick `mul` just large enough so that the result of 

1510 # `isqrt(mul * mul * value)` will still be `<= __DBL_INT_LIMIT_P_I` and 

1511 # thus fits into a `float` nicely. We then get the approximate fractional 

1512 # part by dividing by `mul`. 

1513 if result_low < __DBL_INT_LIMIT_P_I: 

1514 mul: int = __DBL_INT_LIMIT_P_I // result_low 

1515 if mul > 1: 

1516 # We can proceed like before, just do the integer root at a higher 

1517 # resolution. In this high resolution, we compute both the upper 

1518 # and the lower bound for the root. We then pick the one closer to 

1519 # the actual value, rounding up on draw situations. This value is 

1520 # then divided by the multiplier to give us the maximum precision. 

1521 new_value: Final[int] = value * mul * mul 

1522 new_low: Final[int] = isqrt(new_value) 

1523 new_diff_low: Final[int] = new_value - (new_low * new_low) 

1524 new_high: Final[int] = new_low + 1 

1525 new_diff_high: Final[int] = (new_high * new_high) - new_value 

1526 return try_int_div( 

1527 new_high if new_diff_high <= new_diff_low else new_low, mul) 

1528 

1529 #: If we get here, then there is just no way to get useful fractional 

1530 #: parts. We then just check if we should round up the result or return 

1531 #: the rounded-down result. 

1532 result_up: Final[int] = result_low + 1 

1533 diff_up: int = (result_up * result_up) - value 

1534 return result_up if diff_up <= diff_low else result_low 

1535 

1536 

1537def try_int_add(a: int, b: int | float) -> int | float: 

1538 """ 

1539 Try to add a floating point number to an integer. 

1540 

1541 :param a: the integer 

1542 :param b: the floating point number 

1543 :returns: `a + b` or the best possible approximation thereof 

1544 :raises TypeError: if `a` is not an integer or if `b` is neither a 

1545 float nor an integer 

1546 :raises ValueError: if `b` or the result is not finite 

1547 

1548 >>> try_int_add(5, 7) 

1549 12 

1550 

1551 >>> try_int_add(5, 7.0) 

1552 12 

1553 

1554 >>> try_int_add(0, -8670.320148166094) 

1555 -8670.320148166094 

1556 >>> 0 + -8670.320148166094 

1557 -8670.320148166094 

1558 

1559 >>> try_int_add(-63710, 100.96227261264141) 

1560 -63609.03772738736 

1561 >>> -63710 + 100.96227261264141 

1562 -63609.03772738736 

1563 

1564 >>> try_int_add(77, 12975.955050422272) 

1565 13052.955050422272 

1566 >>> 77 + 12975.955050422272 

1567 13052.955050422272 

1568 

1569 >>> try_int_add(-308129344193738, 62995516.01169562) 

1570 -308129281198222 

1571 >>> -308129344193738 + 62995516.01169562 

1572 -308129281198222.0 

1573 

1574 >>> try_int_add(-2158504468760619, -1.3773316665252534e+16) 

1575 -1.5931821134013152e+16 

1576 >>> -2158504468760619 + -1.3773316665252534e+16 

1577 -1.5931821134013152e+16 

1578 

1579 >>> try_int_add(-960433622582960, 1.491132239895968e+16) 

1580 1.395088877637672e+16 

1581 >>> -960433622582960 + 1.491132239895968e+16 

1582 1.395088877637672e+16 

1583 

1584 # exact result: 10796862382206072.70684135 

1585 >>> try_int_add(10796862236149287, 146056785.70684135) 

1586 10796862382206073 

1587 >>> 10796862236149287 + 146056785.70684135 

1588 1.0796862382206074e+16 

1589 

1590 # exact result: -11909678744561796.5206623 

1591 >>> try_int_add(-11909677351933537, -1392628259.5206623) 

1592 -11909678744561797 

1593 >>> -11909677351933537 + -1392628259.5206623 

1594 -1.1909678744561796e+16 

1595 

1596 # exact result: 8991519996993845.25 

1597 >>> try_int_add(9257476766666634, -265956769672788.75) 

1598 8991519996993845 

1599 >>> 9257476766666634 + -265956769672788.75 

1600 8991519996993845.0 

1601 

1602 >>> v = int("-9166650131241408540833319855375552663116961087945581\ 

16036489173691561634548053405237489064") 

1604 >>> try_int_add(v, 6.147962494740932e+217) 

1605 6.147962494740932e+217 

1606 >>> v + 6.147962494740932e+217 

1607 6.147962494740932e+217 

1608 

1609 exact result: 2060196266381720280000783609573994641953401509142\ 

16100431778715465940577471030.192914550695235 

1611 >>> v = int("2060196266381720280000783609573994641953401509142043\ 

16121778715465940577470980") 

1613 >>> try_int_add(v, 50.192914550695235) 

1614 20601962663817202800007836095739946419534015091420431778715465940\ 

1615577471030 

1616 >>> v + 50.192914550695235 

1617 2.0601962663817203e+73 

1618 

1619 >>> try: 

1620 ... try_int_add(2.0, 1) 

1621 ... except TypeError as te: 

1622 ... print(te) 

1623 a should be an instance of int but is float, namely 2.0. 

1624 

1625 >>> try: 

1626 ... try_int_add(2, "1") 

1627 ... except TypeError as te: 

1628 ... print(te) 

1629 b should be an instance of any in {float, int} but is str, namely '1'. 

1630 

1631 >>> from math import inf 

1632 >>> try: 

1633 ... try_int_add(2, inf) 

1634 ... except ValueError as ve: 

1635 ... print(ve) 

1636 b=inf is not finite 

1637 """ 

1638 if not isinstance(a, int): 

1639 raise type_error(a, "a", int) 

1640 if isinstance(b, int): 

1641 return a + b 

1642 if not isinstance(b, float): 

1643 raise type_error(b, "b", (int, float)) 

1644 if not isfinite(b): 

1645 raise ValueError(f"{b=} is not finite") 

1646 

1647 # First we attempt to turn b into an integer, because that would solve all 

1648 # of our problems. 

1649 b = __try_int(b) 

1650 if isinstance(b, int): 

1651 return a + b # We are lucky, the result is an integer 

1652 

1653 b_num, b_denom = float_to_frac(b) 

1654 int_num: Final[int] = b_num // b_denom 

1655 int_res: Final[int] = a + int_num 

1656 

1657 a_exact: Final[bool] = __DBL_INT_LIMIT_N_I < a < __DBL_INT_LIMIT_P_I 

1658 b_exact: Final[bool] = __DBL_INT_LIMIT_N_I < b < __DBL_INT_LIMIT_P_I 

1659 res_exact: Final[bool] = \ 

1660 __DBL_INT_LIMIT_N_I < int_res < __DBL_INT_LIMIT_P_I 

1661 if a_exact and b_exact and res_exact: 

1662 # We know that the result should fit well into the float range. 

1663 # So we can just compute it normally 

1664 return __try_int(a + b) 

1665 

1666 if not b_exact: 

1667 # Now if we get here, we are in a strange territory. 

1668 # The floating point character of `b` will definitely pollute the 

1669 # result. Regardless of what we do, we will not just have a rounding 

1670 # error that costs us a fractional part, but it will cost decimals. 

1671 # The right thing to do may be to return a float here, because we do 

1672 # know that floats have a limited resolution and the returned value 

1673 # may be biased. 

1674 float_res: Final[float] = a + b 

1675 if isfinite(float_res): 

1676 return __try_int(float_res) 

1677 

1678 # If we get here, then b is either an exactly representable float or the 

1679 # result of adding a to b would no longer be finite. 

1680 # If `b` is an exactly represented float, this means that the result does 

1681 # not fit into a float. So we just try to round the result. 

1682 # We will lose a fractional part, but the integer part will be exact. 

1683 # `a` is an integer, so it is exact anyway. The integer part of `b` 

1684 # can be represented as exact integer as well. So this means that we 

1685 # will lose the fractional part only. 

1686 # We can do the same thing if the result of the computation would not be 

1687 # finite. Although it would be a bit pretentious to round in such a 

1688 # situation ... well ... why not. 

1689 b_num -= int_num * b_denom 

1690 round_up: Final[bool] = abs(b_num + b_num) >= b_denom 

1691 return (int_res - 1) if (round_up and (b_num < 0)) else ( 

1692 (int_res + 1) if round_up and (b_num > 0) else int_res) 

1693 

1694 

1695def try_int_mul(a: int, b: int | float) -> int | float: 

1696 """ 

1697 Try to multiply an integer with an int or float as exactly as possible. 

1698 

1699 :param a: the integer 

1700 :param b: the int or float to multiply `a` with 

1701 :returns: `a * b` 

1702 :raises ValueError: if `b` or the result is not finite 

1703 :raises TypeError: if `a` is not an integer or if `b` is neither an 

1704 integer nor a float 

1705 

1706 >>> try_int_mul(6, 5) 

1707 30 

1708 

1709 # exact result: -111038109230780524.216538356 

1710 >>> try_int_mul(197262324754, -562895.673916714) 

1711 -111038109230780524 

1712 >>> 197262324754 * -562895.673916714 

1713 -1.1103810923078053e+17 

1714 

1715 >>> try_int_mul(4, -2493374.0) 

1716 -9973496 

1717 >>> 4 * -2493374.0 

1718 -9973496.0 

1719 

1720 # exact result: -805144077682.7549712841791 

1721 >>> try_int_mul(609329061, -1321.3616897926931) 

1722 -805144077682.755 

1723 >>> 609329061 * -1321.3616897926931 

1724 -805144077682.755 

1725 

1726 # exact result: -88939650274621002534.99 

1727 >>> try_int_mul(-6548165, 13582377700412.406) 

1728 -88939650274621004172 

1729 >>> -6548165 * 13582377700412.406 

1730 -8.8939650274621e+19 

1731 

1732 >>> try_int_mul(4, 0.687279486538305) 

1733 2.74911794615322 

1734 >>> 4 * 0.687279486538305 

1735 2.74911794615322 

1736 

1737 # exact result: -2236563847561524626.733 

1738 >>> try_int_mul(21396228, -104530754091.86725) 

1739 -2236563847561524627 

1740 >>> 21396228 * -104530754091.86725 

1741 -2.2365638475615245e+18 

1742 

1743 # exact result: -92649832027598387270282.5408 

1744 >>> try_int_mul(29187432758, -3174305626527.0176) 

1745 -92649832027598386631807 

1746 >>> 29187432758 * -3174305626527.0176 

1747 -9.264983202759838e+22 

1748 

1749 # exact result: 47954872443652456553018463.12996 

1750 >>> try_int_mul(-317420410641789, -151076839534.96564) 

1751 47954872443652455666473176 

1752 >>> -317420410641789 * -151076839534.96564 

1753 4.795487244365246e+25 

1754 

1755 # exact result: 369200712310299349798.80066193866 

1756 >>> try_int_mul(8136505182920565, 45375.834465796564) 

1757 369200712310299353646 

1758 >>> 8136505182920565 * 45375.834465796564 

1759 3.6920071231029936e+20 

1760 

1761 # exact result: 431520767093145743090.73845486 

1762 >>> try_int_mul(40196153594795, 10735374.619252708) 

1763 431520767093145743091 

1764 >>> 40196153594795 * 10735374.619252708 

1765 4.315207670931457e+20 

1766 

1767 # exact result: -250242005217172713.52783326 

1768 >>> try_int_mul(27941562579, -8955905.90217194) 

1769 -250242005217172703 

1770 >>> 27941562579 * -8955905.90217194 

1771 -2.502420052171727e+17 

1772 

1773 # exact result: -6563728914129924.848948421 

1774 >>> try_int_mul(-672426819, 9761253.906991959) 

1775 -6563728914129925 

1776 >>> -672426819 * 9761253.906991959 

1777 -6563728914129925.0 

1778 

1779 >>> try_int_mul(14059, 1.0673811010650016e+16) 

1780 1.5006310899872858e+20 

1781 >>> 14059 * 1.0673811010650016e+16 

1782 1.5006310899872858e+20 

1783 

1784 # exact result: 14493050353163113.126430160675 

1785 >>> try_int_mul(240712887635, 60208.867483403505) 

1786 14493050353163113 

1787 >>> 240712887635 * 60208.867483403505 

1788 1.4493050353163114e+16 

1789 

1790 # exact result: 805460953505875910367.5205722154 

1791 >>> try_int_mul(1812115257906061, 444486.6020479314) 

1792 805460953505875915662 

1793 >>> 1812115257906061 * 444486.6020479314 

1794 8.054609535058759e+20 

1795 

1796 # exact result: -1384354228892504466.5554728510606 

1797 >>> try_int_mul(6815245310862468, -203.12610416033795) 

1798 -1384354228892504435 

1799 >>> 6815245310862468 * -203.12610416033795 

1800 -1.3843542288925043e+18 

1801 

1802 # exact result: -572028608656496.423924280629596728 

1803 >>> try_int_mul(11587431214834713, -0.049366300265425656) 

1804 -572028608656496.4 

1805 >>> 11587431214834713 * -0.049366300265425656 

1806 -572028608656496.4 

1807 

1808 # exact result: 1128618866534760.28918431873755142 

1809 >>> try_int_mul(16354919666787217, 0.06900791257487526) 

1810 1128618866534760.2 

1811 >>> 16354919666787217 * 0.06900791257487526 

1812 1128618866534760.2 

1813 

1814 # exact result: -2507326755278071.50624700782133248 

1815 >>> try_int_mul(13217245192529664, -0.18970116077556032) 

1816 -2507326755278071.5 

1817 >>> 13217245192529664 * -0.18970116077556032 

1818 -2507326755278071.5 

1819 

1820 # exact result: 696151526057376.88027486041356184 

1821 >>> try_int_mul(-10333677547666606, -0.06736725844658964) 

1822 696151526057376.9 

1823 >>> -10333677547666606 * -0.06736725844658964 

1824 696151526057376.9 

1825 

1826 # exact result: -958450150333374.5128889837837098 

1827 >>> try_int_mul(12016909016999122, -0.0797584594322509) 

1828 -958450150333374.6 

1829 >>> 12016909016999122 * -0.0797584594322509 

1830 -958450150333374.6 

1831 

1832 >>> aa = int("1318537368301039863303586092319665276843530233302383387022\ 

18338761465225501763768872549741384158750496877681759291226540877199284501122993\ 

18340897105528797412214008383330709731057075605034370259681835287681493225337651\ 

18353905721656778533145739528500419884652958325779506781860934858448618309985340\ 

18362653730863759125601710698375950989559971436924737005754922330642277477754688\ 

18374919382044527420457991975491785609852030831998308070776211565814942350933642\ 

1838672902063132158594646597242361650005228312919254855") 

1839 >>> try_int_mul(aa, -2.6624992899981142e+135) 

1840 -351060480693750064453835292428076534998065626248205859394224131928297807\ 

184137557248636560809666436142921728329271829636053027677161961750893317035607449\ 

184288120284425121093171262696954695038088240282639132134536157970777415620680544\ 

184377787260819949647198753734696904695444416894546646340086090596255528370630342\ 

184439912680252313956743223994856279867123453950294756395973723697089090287742438\ 

184542755397630769057958666859505455598999506335122025009809406354300546655548757\ 

184603308334026066509069233835834019214049819194441000000000000000000000000000000\ 

184700000000000000000000000000000000000000000000000000000000000000000000000000000\ 

18480000000000000 

1849 

1850 # exact result: 5115993211447460900.43715653698 

1851 >>> try_int_mul(45247701671134, 113066.36630145647) 

1852 5115993211447460734 

1853 >>> 45247701671134 * 113066.36630145647 

1854 5.115993211447461e+18 

1855 

1856 # exact result: -125197981872321984234 

1857 >>> try_int_mul(-15606149727, 8022349142.0) 

1858 -125197981872321984234 

1859 >>> -15606149727 * 8022349142.0 

1860 -1.2519798187232199e+20 

1861 

1862 # exact result: -348481045.61578014504 

1863 >>> try_int_mul(6636, -52513.71995415614) 

1864 -348481045.6157802 

1865 >>> 6636 * -52513.71995415614 

1866 -348481045.6157802 

1867 

1868 # exact result: -339789407482572717.3787168852228 

1869 >>> try_int_mul(6921658507965838, -49.0907500119406) 

1870 -339789407482572714 

1871 >>> 6921658507965838 * -49.0907500119406 

1872 -3.3978940748257274e+17 

1873 

1874 >>> try_int_mul(2366231432701, 9061910680864392.0) 

1875 2.1442577893390244e+28 

1876 >>> 2366231432701 * 9061910680864392.0 

1877 2.1442577893390244e+28 

1878 

1879 >>> try_int_mul(11382697409900285, 7338977711.446167) 

1880 8.35373625873942e+25 

1881 >>> 11382697409900285 * 7338977711.446167 

1882 8.35373625873942e+25 

1883 

1884 >>> try_int_mul(34207518885, -6554.28955920917) 

1885 -224205983874406 

1886 >>> 34207518885 * -6554.28955920917 

1887 -224205983874406.0 

1888 

1889 >>> try_int_mul(35107, -165228482913.08173) 

1890 -5800676349629560 

1891 >>> 35107 * -165228482913.08173 

1892 -5800676349629560.0 

1893 

1894 >>> try_int_mul(0, 2.4281702332336544e+16) 

1895 0 

1896 >>> 0 * 2.4281702332336544e+16 

1897 0.0 

1898 

1899 >>> try_int_mul(12299117359251193, 9482167930204820.0) 

1900 1.1662229619371705e+32 

1901 >>> 12299117359251193 * 9482167930204820.0 

1902 1.1662229619371705e+32 

1903 

1904 >>> try_int_mul(-11025104822925196, 0.20926918490209712) 

1905 -2307214699753735.5 

1906 >>> -11025104822925196 * 0.20926918490209712 

1907 -2307214699753735.5 

1908 

1909 >>> try_int_mul(9772540954912922, -0.46316069211643107) 

1910 -4526256832413637 

1911 >>> 9772540954912922 * -0.46316069211643107 

1912 -4526256832413637.0 

1913 

1914 >>> a = -5687274133508874816611305835797802819969961090128107452007652532\ 

191573368270677846755602944621609056451583453036433989268229052391280449456056415\ 

191635305434613932448168719851117464179317780697744165312772461358166814824851088\ 

191755666554914988361150741132171265507858468795070096289596042533878836544303330\ 

191851629205606376256678385083639086123757879299418359388159156263956657201968562\ 

191931234444965574888174813926197455099511160764637390234826490840919289969902451\ 

192019032766058028744 

1921 >>> try_int_mul(a, 1.6152587208080178e+161) 

1922 -91864191417760728566594708387860610684587465634907240089126522260899489\ 

19234868393273335550289848260675410562832758047321131061871920910825594187776263\ 

19243813438746416917351911213760538943029477664003307403545581300547688099127531\ 

19255980188627547208265662112980855054467534738906286741579697000621904552812416\ 

19269446067183418151530080010856381487158544646128107795979443668739119314808361\ 

19277661789396847122441264057280246286323595226375142154153566223973380730500186\ 

19280346809116255685129263413549101637215712126487779567515862772796494902477027\ 

19294283909310368263635531679756438464790102625606997105530169121933171318181638\ 

1930719123552835758718976000 

1931 

1932 >>> a = 3765608478313035700785399638168771339557519363527174997097642640\ 

19336101495713891805694367423527904175961734525627539554843115375392781571623329\ 

19342447297269149929791925360073769054144096521088071580437368293552550840688369\ 

19352608265449974834262318405099511472680054858496169995743896513251354009716226\ 

19362514585220536136586004979988630733951340879593101821106230203914627052267837\ 

19371572821101974821796182780210158355368107881592427373376547394885537235348770\ 

1938037983728077456443610490231815763426207048140659081123096064113083255321 

1939 >>> try_int_mul(a, 3.4397561272117625e+192) 

1940 129527748359578257806492201438402698334484205908662618646067106845154998\ 

19412115342535199421721007878815369298874224123455721441548991298088516366970587\ 

19426954884520363675490158067710598514210872880348223211816340777337079830796450\ 

19431538806420032828931583145759690145309074737296451653410762104587461282286330\ 

19445613446806304563553562786109972723062095583351926917494446975708601932868972\ 

19458706300360490387733148782540598727225279861244661365412091714003810616131531\ 

19464481842956460108582107814498584521025354908493431977057047306288665587544613\ 

19476391274648488448957696316691763417067525251280127682030399365602278708061765\ 

19481082290234901228437827481292269177791129383042667337751797004896760132865057\ 

194919242926784445427888800399360 

1950 

1951 >>> a = 129139500057625933922412781529753661193714022177289159193286727\ 

1952341869510538391068183373334814894358463049932698275602586001788553855337370\ 

1953614701810798567624656761308982494637834371899037091828942757328272879402878\ 

1954026516716840890321410558877108818475215456025987413249018189775618174448780\ 

1955086119164868545066265500186250891155310591180702358002 

1956 >>> try_int_mul(a, 1107055232.6334295) 

1957 14296455927845986412272147350433876910362542595277171600320805129204174\ 

1958701824227341993224688128568101787206140720134279287421051283535763162532066\ 

19591082072819079131257940844247255813910836282210665242651249270543058782206016\ 

19600195030665164058974557661094866963676618764040698636345181586979396240926480\ 

196138619836809751492028454165243512720617990761805723389 

1962 

1963 >>> try_int_mul(1202489289292969, 2.1583639585424923e+306) 

1964 259540954254332074038246669681477283939748496620440148998643367300751188\ 

19652740876153253237092741716795754997976766921219000512012754932543147420651232\ 

19665520218655906535605653525109031074378131051185629676951714011087271116671446\ 

19670899827404105545452888405653019460560445425746060708059494631661799648373661\ 

19685341276121474415984640 

1969 

1970 # exact result: -7952338024951495584.4756757 

1971 >>> try_int_mul(-131722798246, 60371766.54947795) 

1972 -7952338024951495584 

1973 >>> -131722798246 * 60371766.54947795 

1974 -7.952338024951496e+18 

1975 

1976 # exact result: 374987726685442496656857448.375 

1977 >>> try_int_mul(-197846874313873, -1895343194002.375) 

1978 374987726685442496656857448 

1979 >>> -197846874313873 * -1895343194002.375 

1980 3.749877266854425e+26 

1981 

1982 # exact result: 10775411722410520324.9 

1983 >>> try_int_mul(187295, 57531763914736.22) 

1984 10775411722410520091 

1985 >>> 187295 * 57531763914736.22 

1986 1.077541172241052e+19 

1987 

1988 >>> try: 

1989 ... try_int_mul(5.0, 1) 

1990 ... except TypeError as te: 

1991 ... print(te) 

1992 a should be an instance of int but is float, namely 5.0. 

1993 

1994 >>> try: 

1995 ... try_int_mul(5, "x") 

1996 ... except TypeError as te: 

1997 ... print(te) 

1998 b should be an instance of any in {float, int} but is str, namely 'x'. 

1999 

2000 >>> from math import inf 

2001 >>> try: 

2002 ... try_int_mul(5, inf) 

2003 ... except ValueError as ve: 

2004 ... print(ve) 

2005 b=inf is not finite 

2006 """ 

2007 if not isinstance(a, int): 

2008 raise type_error(a, "a", int) 

2009 if isinstance(b, int): 

2010 return a * b 

2011 if not isinstance(b, float): 

2012 raise type_error(b, "b", (int, float)) 

2013 if not isfinite(b): 

2014 raise ValueError(f"{b=} is not finite") 

2015 

2016 # First we attempt to turn b into an integer, because that would solve all 

2017 # of our problems. 

2018 b = __try_int(b) 

2019 if isinstance(b, int): 

2020 return a * b # We are lucky, the result is an integer 

2021 

2022 minus: bool = False 

2023 if a < 0: 

2024 a = -a 

2025 minus = True 

2026 if b < 0: 

2027 b = -b 

2028 minus = not minus 

2029 

2030 # Try to get the result as floating point number 

2031 float_res: int | float | None = None 

2032 with suppress(ArithmeticError): 

2033 float_res = a * b 

2034 float_res = __try_int(float_res) if isfinite(float_res) else None 

2035 

2036 # pylint: disable=R0916 

2037 if (float_res is not None) and (isinstance(float_res, int) or ( 

2038 a >= __DBL_INT_LIMIT_P_I) or (b >= __DBL_INT_LIMIT_P_I) or ( 

2039 (a <= __DBL_INT_LIMIT_P_I) and (b <= __DBL_INT_LIMIT_P_I) and ( 

2040 float_res <= __DBL_INT_LIMIT_P_F))): 

2041 # If float_res could be transformed to an int, then we are good. 

2042 # If either a or b are outside of the range where we can represent 

2043 # digits exactly, then there is nothing that we can do and we may 

2044 # as well return the result of the floating point computation. 

2045 # Trying to use integers would suggest a precision that we cannot 

2046 # offer. 

2047 # Alternatively, if everything falls into the range where we do not 

2048 # have a loss of precision, then trying anything would be odd. 

2049 # Using integer precision would be pretentious. 

2050 return -float_res if minus else float_res # pylint: disable=E1130 

2051 

2052 num, denom = float_to_frac(b) 

2053 result = try_int_div(a * num, denom) 

2054 return -result if minus else result 

2055 

2056 

2057def ceil_div(a: int, b: int) -> int: 

2058 """ 

2059 Compute a ceiling division of two integers. 

2060 

2061 :param a: the number to be divided by `b` 

2062 :param b: the number dividing `a` 

2063 :returns: the rounded-up result of the division 

2064 

2065 >>> ceil_div(1, 1) 

2066 1 

2067 >>> ceil_div(-1, 1) 

2068 -1 

2069 >>> ceil_div(-1, -1) 

2070 1 

2071 >>> ceil_div(1, -1) 

2072 -1 

2073 >>> ceil_div(98, 98) 

2074 1 

2075 >>> ceil_div(98, 99) 

2076 1 

2077 >>> ceil_div(98, 97) 

2078 2 

2079 >>> ceil_div(98, -97) 

2080 -1 

2081 >>> ceil_div(-98, -97) 

2082 2 

2083 >>> ceil_div(-98, 97) 

2084 -1 

2085 >>> ceil_div(3, 1) 

2086 3 

2087 >>> ceil_div(3, -1) 

2088 -3 

2089 >>> ceil_div(-3, 1) 

2090 -3 

2091 >>> ceil_div(-3, -1) 

2092 3 

2093 >>> ceil_div(3, 2) 

2094 2 

2095 >>> ceil_div(3, -2) 

2096 -1 

2097 >>> ceil_div(-3, 2) 

2098 -1 

2099 >>> ceil_div(-3, -2) 

2100 2 

2101 >>> ceil_div(3, 3) 

2102 1 

2103 >>> ceil_div(3, 4) 

2104 1 

2105 >>> ceil_div(3, -4) 

2106 0 

2107 >>> ceil_div(-3, 4) 

2108 0 

2109 >>> ceil_div(-3, -4) 

2110 1 

2111 >>> ceil_div(4, 1) 

2112 4 

2113 >>> ceil_div(4, 2) 

2114 2 

2115 >>> ceil_div(4, 3) 

2116 2 

2117 >>> ceil_div(4, 4) 

2118 1 

2119 >>> ceil_div(4, 5) 

2120 1 

2121 >>> ceil_div(4, 23242398) 

2122 1 

2123 >>> ceil_div(4, -23242398) 

2124 0 

2125 >>> ceil_div(-4, 23242398) 

2126 0 

2127 >>> ceil_div(-4, -23242398) 

2128 1 

2129 >>> ceil_div(0, 1) 

2130 0 

2131 >>> ceil_div(0, -1) 

2132 0 

2133 >>> ceil_div(-0, 1) 

2134 0 

2135 >>> ceil_div(-0, -1) 

2136 0 

2137 >>> try: 

2138 ... ceil_div(1, 0) 

2139 ... except ZeroDivisionError as ze: 

2140 ... print(ze) 

2141 integer division or modulo by zero 

2142 >>> try: 

2143 ... ceil_div(-1, 0) 

2144 ... except ZeroDivisionError as ze: 

2145 ... print(ze) 

2146 integer division or modulo by zero 

2147 >>> try: 

2148 ... ceil_div(1, -0) 

2149 ... except ZeroDivisionError as ze: 

2150 ... print(ze) 

2151 integer division or modulo by zero 

2152 >>> try: 

2153 ... ceil_div(-1, -0) 

2154 ... except ZeroDivisionError as ze: 

2155 ... print(ze) 

2156 integer division or modulo by zero 

2157 """ 

2158 return -((-a) // b)