/** Bruch: Zaehler und Nenner sind je 16 Bit breit */ typedef struct frac16_s { int16_t z; int16_t n; } frac16_t; /** Bruch: Zaehler und Nenner sind je 32 Bit breit */ typedef struct frac32_s { int32_t z; int32_t n; } frac32_t; /** 32-Bit-Bruch durch 16-Bit-Bruch approximieren (Vollstaendige Enumeration) * * Der Bruch muss weder reduziert noch normalisiert sein. * * @param[in] In Z/N * @return out, normalisiert, gekuerzt */ frac16_t frac32approx16(frac32_t In) { int64_t err, errOpt = INT64_MAX; uint32_t Z, N, z=0, n=0; uint32_t zOpt = 1, nOpt = 1; /* Unsigned und Vorzeichen */ if( In.z >= 0 ) Z = In.z; else if( In.z == INT32_MIN ) Z = (uint32_t) INT32_MAX + 1; else Z = -In.z; if( In.n >= 0 ) N = In.n; else if( In.n == INT32_MIN ) N = (uint32_t) INT32_MAX + 1; else N = -In.n; /* Alle erlaubten Nenner durchprobieren INT16_MIN ist als Ergebnis fuer den Nenner nicht erlaubt, da Zaehler bei normalisierten Bruechen immer positiv. INT16_MAX+1 muss daher nicht geprueft werden. */ for( uint32_t ii = 2; ii <= 2*INT16_MAX-1; ii++ ) { if( ii%2 == 0 ) { n = ii/2; /* Zaehler bestimmen */ uint64_t zTemp = (uint64_t) Z * n/N; if( zTemp > INT16_MAX ) z = INT16_MAX; /* Egal wie gut diese Naeherung ist: Sie ist eher schlecht */ else z = zTemp; } else { /* Nochmal fuer gleichen Nenner testen mit z+1 */ if( z >= INT16_MAX ) continue; z += 1; } /* Naeherung testen */ uint64_t f0 = Z*n; uint64_t f1 = z*N; if( f0 >= f1 ) err = f0 - f1; else err = f1 - f0; if( err < errOpt ) { errOpt = err; zOpt = z; nOpt = n; } if( err == 0 ) { break; } } bool isnegative = ((In.z ^ In.n) & INT32_MIN); if( isnegative ) { zOpt = -zOpt; } return (frac16_t) {zOpt, nOpt}; }