summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorOscar NihlgÄrd <oscarnihlgard@gmail.com>2018-05-30 18:14:21 +0200
committerDmitry Atamanov <data-man@users.noreply.github.com>2018-05-30 19:14:21 +0300
commit65070a6936bd6954f3bc95015af49b3ba3b007b7 (patch)
tree2be9bce9a3c8cf9237f62705592d144703333984 /lib
parent2107c81d6d53af538eae2f1bec7d77b02f5a80af (diff)
downloadNim-65070a6936bd6954f3bc95015af49b3ba3b007b7.tar.gz
Use truncation division in mod for floats (#7118)
* Use truncation division in mod for floats

* Add changelog entry

* Add floorDiv/floorMod to math.nim

* Update changelog
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/math.nim51
1 files changed, 40 insertions, 11 deletions
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index fc49008b9..e07e9a200 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -363,13 +363,16 @@ when not defined(JS): # C
       ## .. code-block:: nim
       ##  echo trunc(PI) # 3.0
 
-  proc fmod*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
-  proc fmod*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".}
+  proc fmod*(x, y: float32): float32 {.deprecated, importc: "fmodf", header: "<math.h>".}
+  proc fmod*(x, y: float64): float64 {.deprecated, importc: "fmod", header: "<math.h>".}
     ## Computes the remainder of `x` divided by `y`
     ##
     ## .. code-block:: nim
     ##  echo fmod(-2.5, 0.3) ## -0.1
 
+  proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
+  proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".}
+    ## Computes the modulo operation for float operators. 
 else: # JS
   proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y)
   proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.}
@@ -382,6 +385,10 @@ else: # JS
   proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.}
   proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.}
 
+  proc `mod`*(x, y: float32): float32 {.importcpp: "# % #".}
+  proc `mod`*(x, y: float64): float64 {.importcpp: "# % #".}
+  ## Computes the modulo operation for float operators.
+
 proc round*[T: float32|float64](x: T, places: int = 0): T =
   ## Round a floating point number.
   ##
@@ -397,6 +404,21 @@ proc round*[T: float32|float64](x: T, places: int = 0): T =
     var mult = pow(10.0, places.T)
     result = round0(x*mult)/mult
 
+proc floorDiv*[T: SomeInteger](x, y: T): T =
+  ## Floor division is conceptually defined as ``floor(x / y)``.
+  ## This is different from the ``div`` operator, which is defined
+  ## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv``
+  ## rounds down.
+  result = x div y
+  let r = x mod y
+  if (r > 0 and y < 0) or (r < 0 and y > 0): result.dec 1
+
+proc floorMod*[T: SomeNumber](x, y: T): T =
+  ## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y).
+  ## This proc behaves the same as the ``%`` operator in python.
+  result = x mod y
+  if (result > 0 and y < 0) or (result < 0 and y > 0): result += y
+
 when not defined(JS):
   proc c_frexp*(x: float32, exponent: var int32): float32 {.
     importc: "frexp", header: "<math.h>".}
@@ -461,15 +483,6 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} =
   ## `NaN`.
   ord(T(0) < x) - ord(x < T(0))
 
-proc `mod`*[T: float32|float64](x, y: T): T =
-  ## Computes the modulo operation for float operators. Equivalent
-  ## to ``x - y * floor(x/y)``. Note that the remainder will always
-  ## have the same sign as the divisor.
-  ##
-  ## .. code-block:: nim
-  ##  echo (4.0 mod -3.1) # -2.2
-  result = if y == 0.0: x else: x - y * (x/y).floor
-
 {.pop.}
 {.pop.}
 
@@ -634,3 +647,19 @@ when isMainModule:
     doAssert fac(2) == 2
     doAssert fac(3) == 6
     doAssert fac(4) == 24
+
+  block: # floorMod/floorDiv
+    doAssert floorDiv(8, 3) == 2
+    doAssert floorMod(8, 3) == 2
+    
+    doAssert floorDiv(8, -3) == -3
+    doAssert floorMod(8, -3) == -1
+
+    doAssert floorDiv(-8, 3) == -3
+    doAssert floorMod(-8, 3) == 1
+
+    doAssert floorDiv(-8, -3) == 2
+    doAssert floorMod(-8, -3) == -2
+
+    doAssert floorMod(8.0, -3.0) ==~ -1.0
+    doAssert floorMod(-8.5, 3.0) ==~ 0.5