integerops.nim 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2020 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # Integer arithmetic with overflow checking. Uses
  10. # intrinsics or inline assembler.
  11. proc raiseOverflow {.compilerproc, noinline.} =
  12. # a single proc to reduce code size to a minimum
  13. sysFatal(OverflowDefect, "over- or underflow")
  14. proc raiseDivByZero {.compilerproc, noinline.} =
  15. sysFatal(DivByZeroDefect, "division by zero")
  16. {.pragma: nimbaseH, importc, nodecl, noSideEffect, compilerproc.}
  17. when not defined(nimEmulateOverflowChecks):
  18. # take the #define from nimbase.h
  19. proc nimAddInt(a, b: int, res: ptr int): bool {.nimbaseH.}
  20. proc nimSubInt(a, b: int, res: ptr int): bool {.nimbaseH.}
  21. proc nimMulInt(a, b: int, res: ptr int): bool {.nimbaseH.}
  22. proc nimAddInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
  23. proc nimSubInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
  24. proc nimMulInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
  25. # unary minus and 'abs' not required here anymore and are directly handled
  26. # in the code generator.
  27. # 'nimModInt' does exist in nimbase.h without check as we moved the
  28. # check for 0 to the codgen.
  29. proc nimModInt(a, b: int; res: ptr int): bool {.nimbaseH.}
  30. proc nimModInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
  31. # Platform independent versions.
  32. template addImplFallback(name, T, U) {.dirty.} =
  33. when not declared(name):
  34. proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
  35. let r = cast[T](cast[U](a) + cast[U](b))
  36. if (r xor a) >= T(0) or (r xor b) >= T(0):
  37. res[] = r
  38. else:
  39. result = true
  40. addImplFallback(nimAddInt, int, uint)
  41. addImplFallback(nimAddInt64, int64, uint64)
  42. template subImplFallback(name, T, U) {.dirty.} =
  43. when not declared(name):
  44. proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
  45. let r = cast[T](cast[U](a) - cast[U](b))
  46. if (r xor a) >= 0 or (r xor not b) >= 0:
  47. res[] = r
  48. else:
  49. result = true
  50. subImplFallback(nimSubInt, int, uint)
  51. subImplFallback(nimSubInt64, int64, uint64)
  52. template mulImplFallback(name, T, U, conv) {.dirty.} =
  53. #
  54. # This code has been inspired by Python's source code.
  55. # The native int product x*y is either exactly right or *way* off, being
  56. # just the last n bits of the true product, where n is the number of bits
  57. # in an int (the delivered product is the true product plus i*2**n for
  58. # some integer i).
  59. #
  60. # The native float64 product x*y is subject to three
  61. # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
  62. # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
  63. # But, unlike the native int product, it's not in *range* trouble: even
  64. # if sizeof(int)==32 (256-bit ints), the product easily fits in the
  65. # dynamic range of a float64. So the leading 50 (or so) bits of the float64
  66. # product are correct.
  67. #
  68. # We check these two ways against each other, and declare victory if
  69. # they're approximately the same. Else, because the native int product is
  70. # the only one that can lose catastrophic amounts of information, it's the
  71. # native int product that must have overflowed.
  72. #
  73. when not declared(name):
  74. proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
  75. let r = cast[T](cast[U](a) * cast[U](b))
  76. let floatProd = conv(a) * conv(b)
  77. let resAsFloat = conv(r)
  78. # Fast path for normal case: small multiplicands, and no info
  79. # is lost in either method.
  80. if resAsFloat == floatProd:
  81. res[] = r
  82. else:
  83. # Somebody somewhere lost info. Close enough, or way off? Note
  84. # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
  85. # The difference either is or isn't significant compared to the
  86. # true value (of which floatProd is a good approximation).
  87. # abs(diff)/abs(prod) <= 1/32 iff
  88. # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
  89. if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
  90. res[] = r
  91. else:
  92. result = true
  93. mulImplFallback(nimMulInt, int, uint, toFloat)
  94. mulImplFallback(nimMulInt64, int64, uint64, toBiggestFloat)
  95. template divImplFallback(name, T) {.dirty.} =
  96. proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
  97. # we moved the b == 0 case out into the codegen.
  98. if a == low(T) and b == T(-1):
  99. result = true
  100. else:
  101. res[] = a div b
  102. divImplFallback(nimDivInt, int)
  103. divImplFallback(nimDivInt64, int64)
  104. proc raiseFloatInvalidOp {.compilerproc, noinline.} =
  105. sysFatal(FloatInvalidOpDefect, "FPU operation caused a NaN result")
  106. proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} =
  107. if x > 0.0:
  108. sysFatal(FloatOverflowDefect, "FPU operation caused an overflow")
  109. else:
  110. sysFatal(FloatUnderflowDefect, "FPU operations caused an underflow")