31202Fermer31204
bearbecueLe 22/05/2017 à 09:08
0^2 ./31198>
oui c'est normal.
cette fonction 'IDivMulInv' calcule le resultat d'une division entiere entre deux nombres 32bits signes: une variable divisee par une constante, sans utiliser d'instruction idiv.

le principe c'est de la faire en fixed-point, tu multiplie ta variable 'x' par (1<<32)/constante et tu recupere les 32-bits hauts du resultat.
sauf qu'il faut offsetter le complement a deux pour avoir la bonne troncation (?frenglish ftw), et pour eviter de perdre de la precision et avoir des erreurs d'arrondis en fait c'est pas (1<<32)/constante, mais (1<<(32+n))/constante, de sorte a ce que la constante prenne le max de bits utiles dans les 32 bits du multiplicateur constant.
donc pour une division par une constante 'k' tu peux calculer un multiplicateur 'km' et un shifteur 'ks', et t'as un truc de la forme:

xdivk = mulhi(x, km) >> ks

en rajoutant les corrections de signe:

offset = (km < 0 && kDiv > 0) ? +x : (km > 0 && kDiv < 0) ? -x : 0;
a = mulhi(x, km);
b = (a + offset ) >> ks;
xdivk = b + sign(b)

en virant les branches du calcul de 'offset' (qui est aussi constant vu qu'il depend que des constantes calculees depuis le diviseur constant) ca donne le code du copy/paste d'au dessus ^^
la version sans bugs:

static HH_FORCEINLINE hh_i32	_MulHiS32(hh_i32 x, hh_i32 y)
{
	const hh_u64	mulloS64 = hh_i64(x) * hh_i64(y);
	return mulloS64 >> 32;
}

//----------------------------------------------------------------------------

static HH_FORCEINLINE hh_i32	_IDivMulInv(hh_i32 x, hh_i32 kMul, hh_i32 kSra, hh_i32 kAddMask, hh_i32 kSubMask)
{
	const hh_i32	offset = (x & kAddMask) - (x & kSubMask);
	const hh_i32	mulHi = _MulHiS32(x, kMul) + offset;
	const hh_i32	mulHiS = mulHi >> kSra;
	const hh_i32	sign = hh_u32(mulHiS) >> 31;	// rounding offset for negative values
	const hh_i32	d = mulHiS + sign;
	return d;
}

//----------------------------------------------------------------------------

static HH_FORCEINLINE hh_i32	_IDivMulInv(hh_i32 x, hh_i32 kDiv, hh_i32 kMul, hh_i32 kSra)
{
#if 1
	const hh_i32	kDivSign = (kDiv >> 31);
	const hh_i32	kMulSign = (kMul >> 31);
	const hh_i32	kAddMask = kMulSign & ~kDivSign;
	const hh_i32	kSubMask = kDivSign & ~kMulSign;
	return _IDivMulInv(x, kMul, kSra, kAddMask, kSubMask);
#else
	return (kDiv != 0) ? x / kDiv : 0;
#endif
}

C'est au passage ce qui est utilise par tous les compilateurs un minimum potables (ceci inclut msvc wink) lorsque tu divise (ou modulo) par une constante.
la division ou modulo non signes sont plus simples vu que t'as pas toute la partie correction de signe. (mais c'est pas les memes km et ks)