5

The GNU C Library has the function drem (alias remainder).

How can I simulate this function just using the modules supported by Google App Engine Python 2.7 runtime?

From the GNU manual for drem:

These functions are like fmod except that they round the internal quotient n to the nearest integer instead of towards zero to an integer. For example, drem (6.5, 2.3) returns -0.4, which is 6.5 minus 6.9.

From the GNU manual for fmod:

These functions compute the remainder from the division of numerator by denominator. Specifically, the return value is numerator - n * denominator, where n is the quotient of numerator divided by denominator, rounded towards zero to an integer. Thus, fmod (6.5, 2.3) returns 1.9, which is 6.5 minus 4.6.

Reading the documentation the following Python code should work:

def drem(x, y):
    n = round(x / y)
    return x - n * y

However with Python, drem(1.0, 2.0) == -1.0 and with C drem(1.0, 2.0) == 1.0. Note Python returns negative one and C returns positive one. This is almost certainly an internal difference in rounding floats. As far as I can tell both functions perform the same otherwise where parameters 2 * x != y.

How can I make my Python drem function work the same as its C equivalent?

Dan
  • 5,013
  • 5
  • 33
  • 59
  • I should add that I can only use the [supported Python modules](https://developers.google.com/appengine/kb/libraries) available in Google App Engine. – Dan Jul 03 '13 at 22:20
  • `numpy.round()` instead of `round()` seems to work for my example but I would be grateful to know if there are any edge cases with this. – Dan Jul 03 '13 at 22:50
  • I would **love** not to have dependence on Numpy just for its `round` function so the question still stands. – Dan Jul 05 '13 at 09:41
  • I tried implementing [this .net](http://stackoverflow.com/questions/1971645/is-math-ieeeremainderx-y-equivalent-to-xy) version but it fails to handle `drem(1.0, 2.0)`. – Dan Jul 06 '13 at 10:45

2 Answers2

5

The key to solving this problem is to realise that the drem/remainder function specification requires the internal rounding calculation to round to half even.

Therefore we cannot use the built-in round function in Python 2.x as it rounds away from 0. However the round function in Python 3.x has changed to round to half even. So the following Python 3.x code will be equivalent to the GNU C Library drem function but will not work in Python 2.x:

def drem(x, y):
    n = round(x / y)
    return x - n * y

To achieve the same with Python 2.x we can use the decimal module and its remainder_near function:

import decimal

def drem(x, y):
    xd = decimal.Decimal(x)
    yd = decimal.Decimal(y)

    return float(xd.remainder_near(yd))
Dan
  • 5,013
  • 5
  • 33
  • 59
1

EDIT: I just read your first comment and see that you cannot use the ctypes module. Anyways, I learned a lot today by trying to find an answer to your problem.

Considering that numpy.round() rounds values exactly halfway between rounded decimal values to the next even integer, using numpy is not a good solution.

Also, drem internally calls this MONSTER function, which should be hard to implement in Python.

Inspired by this article, I would recommend you to call the drem function from the math library directly. Something along these lines should do the trick:

from ctypes import CDLL
# Use the C math library directly from Python
# This works for Linux, but the version might differ for some systems
libm = CDLL('libm.so.6') 

# For Windows, try this instead: 
# from ctypes import cdll
# libm = cdll.libc

# Make sure the return value is handled as double instead of the default int
libm.drem.restype = c_double
# Make sure the arguments are double by putting them inside c_double()
# Call your function and have fun!
print libm.drem(c_double(1.0), c_double(2.0))
nedim
  • 1,767
  • 1
  • 18
  • 20
  • Thank you very much for the investigation and for letting me know `numpy.round` is not viable. I agree that drem internal function is monster and am not even going to attempt to port it! I have updated the question so that only using the App Engine runtime modules is more prominent. The limitations of the App Engine runtime only occurred to me after I wrote the question and tried to do exactly what you suggested above with ctypes. – Dan Jul 05 '13 at 21:40