Path: csiph.com!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: How to avoid an overflow during multiplication? Date: Fri, 31 Dec 2021 10:28:21 -0800 Organization: A noiseless patient Spider Lines: 77 Message-ID: <86a6ggzl16.fsf@linuxsc.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="06b28be905d0d032491ccb4d10c11358"; logging-data="9317"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18OARlqSGWz/7CX2CeF5IQuj4wKeI3J55Q=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:SMxroslwmRGOvCwkLZ0Qv7sx3pU= sha1:vryVE9GqBR6FsI/lz+O4TisnKrA= Xref: csiph.com comp.lang.c:164167 Mateusz Viste writes: > I have this little function: > > /* converts an amount of ticks into human time (micro-seconds) > * timeunits: number of ticks to convert into actual time > * tempo : the time length (in microseconds) of grouplen ticks > */ > uint32_t ticks2time(uint32_t ticks, uint32_t grouplen, uint16_t tempo) > { > return ticks * tempo / grouplen; > } > > In practical situations the end result of this computation is > guaranteed to always fit inside an uint32_t, but the above formula > overflows easily in the (ticks * tempo) part. [...] I haven't followed all the extra information you have given, but here are some ideas for you to try. First let me restate the function, with minor lexical changes: uint32_t ticks2time( uint32_t ticks, uint32_t scale, uint16_t tempo ){ return ticks * tempo / scale; } Next you might not know the identity a*b/c === a*(b/c) + a*(b%c)/c Of course multiplication is commutative, so we can also write: a*b/c === b*(a/c) + b*(a%c)/c These identities suggest two alternate formulations: uint32_t ticks2time( uint32_t ticks, uint32_t scale, uint16_t tempo ){ return ticks*(tempo/scale) + ticks*(tempo%scale)/scale; } uint32_t ticks2time( uint32_t ticks, uint32_t scale, uint16_t tempo ){ return tempo*(ticks/scale) + tempo*(ticks%scale)/scale; } If one of these works for you (checking both performance and the lack of overflow) then great. If the first one works in terms of avoiding overflow, but is too slow, then if tempo doesn't change often, we can precompute the quantities tempo/scale and tempo%scale when tempo changes: uint16_t tempo_scale_q = tempo/scale; uint16_t tempo_scale_r = tempo%scale; void set_tempo( uint16_t tempo, uint16_t scale ){ tempo_scale_q = tempo / scale; tempo_scale_r = tempo % scale; } uint32_t ticks2time_2( uint32_t ticks, uint32_t scale ){ return ticks*tempo_scale_q + ticks*tempo_scale_r/scale; } which probably will be faster than the original. Incidentally, if 'scale' (aka grouplen) never changes, if you can make it be a compile-time constant then the compiler may be able to turn the division (and remainder) into more efficient computations, giving better performance. Please let us know if these techniques turn out to be helpful. Good luck!