arithm.nim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # simple integer arithmetic with overflow checking
  10. proc raiseOverflow {.compilerproc, noinline.} =
  11. # a single proc to reduce code size to a minimum
  12. sysFatal(OverflowError, "over- or underflow")
  13. proc raiseDivByZero {.compilerproc, noinline.} =
  14. sysFatal(DivByZeroError, "division by zero")
  15. when defined(builtinOverflow):
  16. # Builtin compiler functions for improved performance
  17. when sizeof(clong) == 8:
  18. proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
  19. importc: "__builtin_saddl_overflow", nodecl, nosideeffect.}
  20. proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
  21. importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.}
  22. proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
  23. importc: "__builtin_smull_overflow", nodecl, nosideeffect.}
  24. elif sizeof(clonglong) == 8:
  25. proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
  26. importc: "__builtin_saddll_overflow", nodecl, nosideeffect.}
  27. proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
  28. importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.}
  29. proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
  30. importc: "__builtin_smulll_overflow", nodecl, nosideeffect.}
  31. when sizeof(int) == 8:
  32. proc addIntOverflow(a, b: int, c: var int): bool {.inline.} =
  33. addInt64Overflow(a, b, c)
  34. proc subIntOverflow(a, b: int, c: var int): bool {.inline.} =
  35. subInt64Overflow(a, b, c)
  36. proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} =
  37. mulInt64Overflow(a, b, c)
  38. elif sizeof(int) == 4 and sizeof(cint) == 4:
  39. proc addIntOverflow(a, b: int, c: var int): bool {.
  40. importc: "__builtin_sadd_overflow", nodecl, nosideeffect.}
  41. proc subIntOverflow(a, b: int, c: var int): bool {.
  42. importc: "__builtin_ssub_overflow", nodecl, nosideeffect.}
  43. proc mulIntOverflow(a, b: int, c: var int): bool {.
  44. importc: "__builtin_smul_overflow", nodecl, nosideeffect.}
  45. proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
  46. if addInt64Overflow(a, b, result):
  47. raiseOverflow()
  48. proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
  49. if subInt64Overflow(a, b, result):
  50. raiseOverflow()
  51. proc mulInt64(a, b: int64): int64 {.compilerproc, inline.} =
  52. if mulInt64Overflow(a, b, result):
  53. raiseOverflow()
  54. else:
  55. proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
  56. result = a +% b
  57. if (result xor a) >= int64(0) or (result xor b) >= int64(0):
  58. return result
  59. raiseOverflow()
  60. proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
  61. result = a -% b
  62. if (result xor a) >= int64(0) or (result xor not b) >= int64(0):
  63. return result
  64. raiseOverflow()
  65. #
  66. # This code has been inspired by Python's source code.
  67. # The native int product x*y is either exactly right or *way* off, being
  68. # just the last n bits of the true product, where n is the number of bits
  69. # in an int (the delivered product is the true product plus i*2**n for
  70. # some integer i).
  71. #
  72. # The native float64 product x*y is subject to three
  73. # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
  74. # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
  75. # But, unlike the native int product, it's not in *range* trouble: even
  76. # if sizeof(int)==32 (256-bit ints), the product easily fits in the
  77. # dynamic range of a float64. So the leading 50 (or so) bits of the float64
  78. # product are correct.
  79. #
  80. # We check these two ways against each other, and declare victory if they're
  81. # approximately the same. Else, because the native int product is the only
  82. # one that can lose catastrophic amounts of information, it's the native int
  83. # product that must have overflowed.
  84. #
  85. proc mulInt64(a, b: int64): int64 {.compilerproc.} =
  86. var
  87. resAsFloat, floatProd: float64
  88. result = a *% b
  89. floatProd = toBiggestFloat(a) # conversion
  90. floatProd = floatProd * toBiggestFloat(b)
  91. resAsFloat = toBiggestFloat(result)
  92. # Fast path for normal case: small multiplicands, and no info
  93. # is lost in either method.
  94. if resAsFloat == floatProd: return result
  95. # Somebody somewhere lost info. Close enough, or way off? Note
  96. # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
  97. # The difference either is or isn't significant compared to the
  98. # true value (of which floatProd is a good approximation).
  99. # abs(diff)/abs(prod) <= 1/32 iff
  100. # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
  101. if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
  102. return result
  103. raiseOverflow()
  104. proc negInt64(a: int64): int64 {.compilerProc, inline.} =
  105. if a != low(int64): return -a
  106. raiseOverflow()
  107. proc absInt64(a: int64): int64 {.compilerProc, inline.} =
  108. if a != low(int64):
  109. if a >= 0: return a
  110. else: return -a
  111. raiseOverflow()
  112. proc divInt64(a, b: int64): int64 {.compilerProc, inline.} =
  113. if b == int64(0):
  114. raiseDivByZero()
  115. if a == low(int64) and b == int64(-1):
  116. raiseOverflow()
  117. return a div b
  118. proc modInt64(a, b: int64): int64 {.compilerProc, inline.} =
  119. if b == int64(0):
  120. raiseDivByZero()
  121. return a mod b
  122. proc absInt(a: int): int {.compilerProc, inline.} =
  123. if a != low(int):
  124. if a >= 0: return a
  125. else: return -a
  126. raiseOverflow()
  127. const
  128. asmVersion = defined(I386) and (defined(vcc) or defined(wcc) or
  129. defined(dmc) or defined(gcc) or defined(llvm_gcc))
  130. # my Version of Borland C++Builder does not have
  131. # tasm32, which is needed for assembler blocks
  132. # this is why Borland is not included in the 'when'
  133. when asmVersion and not defined(gcc) and not defined(llvm_gcc):
  134. # assembler optimized versions for compilers that
  135. # have an intel syntax assembler:
  136. proc addInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
  137. # a in eax, and b in edx
  138. asm """
  139. mov eax, ecx
  140. add eax, edx
  141. jno theEnd
  142. call `raiseOverflow`
  143. theEnd:
  144. ret
  145. """
  146. proc subInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
  147. asm """
  148. mov eax, ecx
  149. sub eax, edx
  150. jno theEnd
  151. call `raiseOverflow`
  152. theEnd:
  153. ret
  154. """
  155. proc negInt(a: int): int {.compilerProc, asmNoStackFrame.} =
  156. asm """
  157. mov eax, ecx
  158. neg eax
  159. jno theEnd
  160. call `raiseOverflow`
  161. theEnd:
  162. ret
  163. """
  164. proc divInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
  165. asm """
  166. mov eax, ecx
  167. mov ecx, edx
  168. xor edx, edx
  169. idiv ecx
  170. jno theEnd
  171. call `raiseOverflow`
  172. theEnd:
  173. ret
  174. """
  175. proc modInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
  176. asm """
  177. mov eax, ecx
  178. mov ecx, edx
  179. xor edx, edx
  180. idiv ecx
  181. jno theEnd
  182. call `raiseOverflow`
  183. theEnd:
  184. mov eax, edx
  185. ret
  186. """
  187. proc mulInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
  188. asm """
  189. mov eax, ecx
  190. mov ecx, edx
  191. xor edx, edx
  192. imul ecx
  193. jno theEnd
  194. call `raiseOverflow`
  195. theEnd:
  196. ret
  197. """
  198. elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)):
  199. proc addInt(a, b: int): int {.compilerProc, inline.} =
  200. # don't use a pure proc here!
  201. asm """
  202. "addl %%ecx, %%eax\n"
  203. "jno 1\n"
  204. "call _raiseOverflow\n"
  205. "1: \n"
  206. :"=a"(`result`)
  207. :"a"(`a`), "c"(`b`)
  208. """
  209. #".intel_syntax noprefix"
  210. #/* Intel syntax here */
  211. #".att_syntax"
  212. proc subInt(a, b: int): int {.compilerProc, inline.} =
  213. asm """ "subl %%ecx,%%eax\n"
  214. "jno 1\n"
  215. "call _raiseOverflow\n"
  216. "1: \n"
  217. :"=a"(`result`)
  218. :"a"(`a`), "c"(`b`)
  219. """
  220. proc mulInt(a, b: int): int {.compilerProc, inline.} =
  221. asm """ "xorl %%edx, %%edx\n"
  222. "imull %%ecx\n"
  223. "jno 1\n"
  224. "call _raiseOverflow\n"
  225. "1: \n"
  226. :"=a"(`result`)
  227. :"a"(`a`), "c"(`b`)
  228. :"%edx"
  229. """
  230. proc negInt(a: int): int {.compilerProc, inline.} =
  231. asm """ "negl %%eax\n"
  232. "jno 1\n"
  233. "call _raiseOverflow\n"
  234. "1: \n"
  235. :"=a"(`result`)
  236. :"a"(`a`)
  237. """
  238. proc divInt(a, b: int): int {.compilerProc, inline.} =
  239. asm """ "xorl %%edx, %%edx\n"
  240. "idivl %%ecx\n"
  241. "jno 1\n"
  242. "call _raiseOverflow\n"
  243. "1: \n"
  244. :"=a"(`result`)
  245. :"a"(`a`), "c"(`b`)
  246. :"%edx"
  247. """
  248. proc modInt(a, b: int): int {.compilerProc, inline.} =
  249. asm """ "xorl %%edx, %%edx\n"
  250. "idivl %%ecx\n"
  251. "jno 1\n"
  252. "call _raiseOverflow\n"
  253. "1: \n"
  254. "movl %%edx, %%eax"
  255. :"=a"(`result`)
  256. :"a"(`a`), "c"(`b`)
  257. :"%edx"
  258. """
  259. when not declared(addInt) and defined(builtinOverflow):
  260. proc addInt(a, b: int): int {.compilerProc, inline.} =
  261. if addIntOverflow(a, b, result):
  262. raiseOverflow()
  263. when not declared(subInt) and defined(builtinOverflow):
  264. proc subInt(a, b: int): int {.compilerProc, inline.} =
  265. if subIntOverflow(a, b, result):
  266. raiseOverflow()
  267. when not declared(mulInt) and defined(builtinOverflow):
  268. proc mulInt(a, b: int): int {.compilerProc, inline.} =
  269. if mulIntOverflow(a, b, result):
  270. raiseOverflow()
  271. # Platform independent versions of the above (slower!)
  272. when not declared(addInt):
  273. proc addInt(a, b: int): int {.compilerProc, inline.} =
  274. result = a +% b
  275. if (result xor a) >= 0 or (result xor b) >= 0:
  276. return result
  277. raiseOverflow()
  278. when not declared(subInt):
  279. proc subInt(a, b: int): int {.compilerProc, inline.} =
  280. result = a -% b
  281. if (result xor a) >= 0 or (result xor not b) >= 0:
  282. return result
  283. raiseOverflow()
  284. when not declared(negInt):
  285. proc negInt(a: int): int {.compilerProc, inline.} =
  286. if a != low(int): return -a
  287. raiseOverflow()
  288. when not declared(divInt):
  289. proc divInt(a, b: int): int {.compilerProc, inline.} =
  290. if b == 0:
  291. raiseDivByZero()
  292. if a == low(int) and b == -1:
  293. raiseOverflow()
  294. return a div b
  295. when not declared(modInt):
  296. proc modInt(a, b: int): int {.compilerProc, inline.} =
  297. if b == 0:
  298. raiseDivByZero()
  299. return a mod b
  300. when not declared(mulInt):
  301. #
  302. # This code has been inspired by Python's source code.
  303. # The native int product x*y is either exactly right or *way* off, being
  304. # just the last n bits of the true product, where n is the number of bits
  305. # in an int (the delivered product is the true product plus i*2**n for
  306. # some integer i).
  307. #
  308. # The native float64 product x*y is subject to three
  309. # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
  310. # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
  311. # But, unlike the native int product, it's not in *range* trouble: even
  312. # if sizeof(int)==32 (256-bit ints), the product easily fits in the
  313. # dynamic range of a float64. So the leading 50 (or so) bits of the float64
  314. # product are correct.
  315. #
  316. # We check these two ways against each other, and declare victory if
  317. # they're approximately the same. Else, because the native int product is
  318. # the only one that can lose catastrophic amounts of information, it's the
  319. # native int product that must have overflowed.
  320. #
  321. proc mulInt(a, b: int): int {.compilerProc.} =
  322. var
  323. resAsFloat, floatProd: float
  324. result = a *% b
  325. floatProd = toFloat(a) * toFloat(b)
  326. resAsFloat = toFloat(result)
  327. # Fast path for normal case: small multiplicands, and no info
  328. # is lost in either method.
  329. if resAsFloat == floatProd: return result
  330. # Somebody somewhere lost info. Close enough, or way off? Note
  331. # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
  332. # The difference either is or isn't significant compared to the
  333. # true value (of which floatProd is a good approximation).
  334. # abs(diff)/abs(prod) <= 1/32 iff
  335. # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
  336. if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
  337. return result
  338. raiseOverflow()
  339. # We avoid setting the FPU control word here for compatibility with libraries
  340. # written in other languages.
  341. proc raiseFloatInvalidOp {.noinline.} =
  342. sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result")
  343. proc nanCheck(x: float64) {.compilerProc, inline.} =
  344. if x != x: raiseFloatInvalidOp()
  345. proc raiseFloatOverflow(x: float64) {.noinline.} =
  346. if x > 0.0:
  347. sysFatal(FloatOverflowError, "FPU operation caused an overflow")
  348. else:
  349. sysFatal(FloatUnderflowError, "FPU operations caused an underflow")
  350. proc infCheck(x: float64) {.compilerProc, inline.} =
  351. if x != 0.0 and x*0.5 == x: raiseFloatOverflow(x)