12

Standard numpy round tie breaking is following IEEE 754 convention, to round half towards the nearest even number. Is there a way to specify different rounding behavior, e.g. round towards zero or towards -inf? I'm not talking about ceil or floor, I just need different tie breaking.

Michael
  • 7,316
  • 1
  • 37
  • 63
  • 3
    Out of curiosity, how can those tie-breaking rules become relevant in practice? After all, the difference is in the same order of magnitude as the quantization error. – maxy Apr 14 '13 at 17:56
  • 1
    I'm rebuilding some complex calculations from matlab in python. The tie breaking there is different, so it ruins some test cases, when I compare the results. – Michael Apr 15 '13 at 09:50

2 Answers2

12

NumPy doesn't give any control over the internal rounding mode. Here's two alternatives:

  1. Use gmpy2, as outlined in this answer. This gives you full control over the rounding mode, but using gmpy2 for simple float math is likely to be slower than NumPy.
  2. Use fesetround via ctypes to manually set the rounding mode. This is system-specific because the constants may vary by platform; check fenv.h for the constant values on your platform. On my machine (Mac OS X):

    import numpy as np
    import ctypes
    FE_TONEAREST = 0x0000
    FE_DOWNWARD = 0x0400
    FE_UPWARD = 0x0800
    FE_TOWARDZERO = 0x0c00
    libc = ctypes.CDLL('libc.dylib')
    
    v = 1. / (1<<23)
    print repr(np.float32(1+v) - np.float32(v/2)) # prints 1.0
    libc.fesetround(FE_UPWARD)
    print repr(np.float32(1+v) - np.float32(v/2)) # prints 1.0000002
    
Community
  • 1
  • 1
nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • 7
    Thanks for your answer! For completeness: Using linux, I found fesetround not in libc, but in libm, so the load line was `libm = ctypes.CDLL('libm.so.6')`. The constants are the same. – Michael Apr 15 '13 at 10:50
0

With the open-source software SWIG

To complete nneonneo answer, if you don't want to download a big package like gmpy2 neither use a system-specific code with ctypes, you can use a binding from C with SWIG (assuming that you already have it on your computer).

Here is what you need to do (in four steps):

1) Write first a file named rounding.i :

%module rounding
%{
/* Put header files here or function declarations like below */

 void rnd_arr();
 void rnd_zero();
 void rnd_plinf();
 void rnd_moinf();
 void rnd_switch();
%}

extern  void rnd_arr();
extern  void rnd_zero();
extern  void rnd_plinf();
extern  void rnd_moinf();
extern  void rnd_switch();

2) Then, a file rnd_C.cpp

#include <stdio.h>
#include <stdlib.h>
#include <fenv.h>


void rnd_arr()
{
  fesetround(FE_TONEAREST);
}


void rnd_zero()
{
  fesetround(FE_TOWARDZERO);
}

void rnd_plinf()
{
  fesetround(FE_UPWARD);
}

void rnd_moinf()
{
  fesetround(FE_DOWNWARD);
}

void rnd_switch()
{
  int r=fegetround();

  if (r==FE_UPWARD)
    r=FE_DOWNWARD;
  else 
    if (r==FE_DOWNWARD)
      r=FE_UPWARD;
    else fprintf(stderr,"ERROR ROUDING MODE \n");
  fesetround(r);
}

3) In your terminal (if you use another version than python2.7, replace python2.7 at the second line ):

swig -c++ -python -o rounding_wrap.cpp rounding.i
g++ -fPIC -c rounding_wrap.cpp rnd_C.cpp -I/usr/include/python2.7
g++ -shared rounding_wrap.o rnd_C.o -o _rounding.so

4) import the library _rounding.so that you just created by taping at the beginning of your python file :

from your_path_to_rounding.so import rounding