From 65070a6936bd6954f3bc95015af49b3ba3b007b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Wed, 30 May 2018 18:14:21 +0200 Subject: [PATCH] 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 --- changelog.md | 4 ++++ lib/pure/math.nim | 51 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index 316d5f9eae..2bbfd01f63 100644 --- a/changelog.md +++ b/changelog.md @@ -39,6 +39,9 @@ The proc is no longer deprecated. - ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``. +- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers + (previously it used floor division like Python). Use ``math.floorMod`` for the old behavior. + #### Breaking changes in the compiler - The undocumented ``#? braces`` parsing mode was removed. @@ -60,6 +63,7 @@ - Added the proc ``algorithm.upperBound``. - Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs. - Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs. +- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division. ### Library changes diff --git a/lib/pure/math.nim b/lib/pure/math.nim index fc49008b98..e07e9a2008 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: "".} - proc fmod*(x, y: float64): float64 {.importc: "fmod", header: "".} + proc fmod*(x, y: float32): float32 {.deprecated, importc: "fmodf", header: "".} + proc fmod*(x, y: float64): float64 {.deprecated, importc: "fmod", header: "".} ## 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: "".} + proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "".} + ## 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: "".} @@ -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