2013-04-14 3 views
7

La rottura del tiepido numero di round standard segue la convenzione IEEE 754, per arrotondare la metà verso il numero pari più vicino. C'è un modo per specificare un diverso comportamento di arrotondamento, ad es. giro verso lo zero o verso -inf? Non sto parlando di ceil o floor, ho solo bisogno di un tie break diverso.Rottura del round con numpy

+3

Per curiosità, come possono queste regole tie-breaking diventare rilevante in pratica? Dopo tutto, la differenza è nello stesso ordine di grandezza dell'errore di quantizzazione. – maxy

+1

Sto ricostruendo alcuni calcoli complessi da MATLAB in python. La rottura del legame è diversa, quindi rovina alcuni casi di test, quando confronto i risultati. – Michael

risposta

9

NumPy non dà alcun controllo sulla modalità di arrotondamento interno. Ecco due alternative:

  1. Utilizzare gmpy2, come indicato nella this answer. Questo ti dà il pieno controllo sulla modalità di arrotondamento, ma l'utilizzo di gmpy2 per la matematica a virgola mobile è probabilmente più lento di NumPy.
  2. Utilizzare fesetround tramite ctypes per impostare manualmente la modalità di arrotondamento. Questo è specifico del sistema perché le costanti possono variare a seconda della piattaforma; controlla fenv.h per i valori costanti sulla tua piattaforma. Sulla mia macchina (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 
    
+5

Grazie per la tua risposta! Per completezza: usando linux, ho trovato fesetround non in libc, ma in libm, quindi la linea di caricamento era 'libm = ctypes.CDLL ('libm.so.6')'. Le costanti sono le stesse. – Michael

0

Con il software open-source SWIG

Per completare la risposta nneonneo, se non si desidera scaricare un pacchetto grande come gmpy2 né l'uso un codice specifico per il sistema con ctypes, è possibile utilizzare un'associazione da C con SWIG (presumendo che sia già presente sul computer).

Ecco cosa dovete fare (in quattro fasi):

1) prima un file denominato 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) Quindi, un 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) Nel vostro terminale (se si utilizza un'altra versione di python2.7, sostituire python2.7 alla seconda riga):

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) importare il _rounding.so libreria appena creata con nastro adesivo all'inizio del file python:

from your_path_to_rounding.so import rounding