Necesito tomar un size_t volume y calcular este resultado en un size_t :
size_t next = (volume * 8 + 3) / 5 Si este resultado desbordara un size_t , entonces next debería ser cero. El problema es, por supuesto, que el volume * 8 + 3 puede desbordarse mientras que el resultado completo cabe en un size_t .
En este momento estoy dividiendo los últimos 4 bits de volume y realizando la multiplicación, suma y división por separado. Mi pregunta es: ¿Puedo hacerlo mejor que lo que tengo hasta ahora si no hay un tipo más grande que size_t ?
size_t next_volume(size_t volume) { // check if the numerator will overflow size_t if (volume > (SIZE_MAX - 3) / 8) { size_t lower, upper; // multiply lower 4 bits by 8 and add 3 lower = ((volume & 0xF) * 8) + 3; // downshift the rest and multiply by 8 upper = (volume >> 4) * 8; // divide upper remainder and lower by 5 lower = ((upper % 5 << 4) + lower) / 5; // divide upper by 5 upper = upper / 5; // ensure the sum will not overflow size_t if (upper + (lower >> 4) > SIZE_MAX >> 4) return 0; return (upper << 4) + lower; } else return (volume * 8 + 3) / 5; }Puede haber algunos errores en ese código. Todavía no lo he sometido a pruebas exhaustivas, pero creo que todas las ideas principales están ahí.
Sea vol1 = volume % 5 , vol2 = volume - vol1 . vol2 es divisible por 5, por lo tanto matemáticamente (vol2 * 8) / 5 = (vol2 / 5) * 8, por lo que obtienes el resultado correcto como
size_t vol1 = volume % 5; size_t vol2 = volume - vol1; size_t result = (vol2 / 5) * 8 + (vol1 * 8 + 3) / 5Obviamente, obtendrá un desbordamiento si el resultado no se ajusta a size_t, pero no si hay un desbordamiento en cualquier parte del cálculo. Dado que multiplicas por 8/5, en caso de desbordamiento, el resultado será aproximadamente 0,6 * volumen < volumen, por lo que puedes volver
return result < volume ? (size_t) -1 : result;que seguramente es mejor que devolver 0.