F
Francois Grieu
Hello,
I want to devise a C89 preprocessor macro MULDIV(a,b,c) which, given positive integer constant expressions a b c, produce an integer constant expression evaluable by the preprocessor and equal to a*b/c even though a*b might grossly overflow the range of long. I can assume a, b, c, a*b/c are all in range [1..2000000000].
I came up with something that works well enough for my current application, by combining two techniques:
1) Basic arithmetic tells that
a*b/c == ((a/c)*c + a%c) * b) / c
== (a/c)*b + (a%c)*((b/c)*c + b%c))/c
== (a/c)*b + (b/c)*(a%c) + (a%c)*(b%c)/c
This is enough to solve the problem when c<46342:
#define MULDIV1(a,b,c) ( ((a)/(c))*(b) + (b)/(c)*((a)%(c)) + \
((a)%(c))*((b)%(c))/(c) )
#if MULDIV1(18536400,4634100,46341)!=1853640000
#error "MULDIV1(18536400,4634100,46341) fails"
#endif
#if MULDIV1(345437,268303627,46341)!=1999999999
#error "MULDIV1(345437,268303627,46341) fails"
#endif
note: at the expense of simplicity, we can reach c<92682 with:
#define MULDIV2(a,b,c) ( ((a)/(c))*(b)+(b)/(c)*((a)%(c))+\
MD__M( (c), (a)%(c), (b)%(c), (a)%(c)>(c)/2, (b)%(c)>(c)/2 ) )
#define MD__M(c,d,e,f,g) d*(g)+e*(f)-(f&&g)*c+\
(((f?c-d:d)*(g?c-e:e)-(f!=g))/c+(f!=g))*(d&&e?f!=g?-1:1:0)
#if MULDIV2(260756292,710863,92681)!=1999999999
#error "MULDIV2(260756292,710863,92681) fails"
#endif
#if MULDIV2(172572,1074113993,92681)!=1999999999
#error "MULDIV2(172572,1074113993,710863,92681) fails"
#endif
2) In my application, when c is big, it will share common primes factors with a and/or b and many of theses factors will be 2 3 or 5. Thus the fraction can be reduced. In particular, the powers of 2 can be removed relatively simply:
#define MULDIV3(a,b,c) MULDIV1( (a)/MD__A((a),(c)), (b)/MD__B((a),(b),(c)),\
(c)/MD__A((a),(c))/MD__B((a),(b),(c)) )
#define MD__A(a,c) (((a|c)^((a|c)-1))/2+1)
#define MD__B(a,b,c) MD__A(b,c/MD__A(a,c))
#if MULDIV3(1471048704,1032258064,759250944)!=1999999999
#error "MULDIV3(1471048704,1032258064,759250944) fails"
#endif
#if MULDIV3(944816128,1984188898,1518501888)!=1234567889
#error "MULDIV3(944816128,1984188898,1518501888) fails"
#endif
With slightly more math, and a preprocessor tolerant of huge macros, I managed to also remove enough powers of 3 and 5.
But I'm looking for something simpler and more general. Any idea? I could live with a slightly inaccurate result, provided the relative error is always at most 1/10000.
[Actually I want a*b/c by excess, that is (a*b-1)/c+1, and I have the problem that a and b can be 0, but these are minor details.]
TIA,
Francois Grieu
I want to devise a C89 preprocessor macro MULDIV(a,b,c) which, given positive integer constant expressions a b c, produce an integer constant expression evaluable by the preprocessor and equal to a*b/c even though a*b might grossly overflow the range of long. I can assume a, b, c, a*b/c are all in range [1..2000000000].
I came up with something that works well enough for my current application, by combining two techniques:
1) Basic arithmetic tells that
a*b/c == ((a/c)*c + a%c) * b) / c
== (a/c)*b + (a%c)*((b/c)*c + b%c))/c
== (a/c)*b + (b/c)*(a%c) + (a%c)*(b%c)/c
This is enough to solve the problem when c<46342:
#define MULDIV1(a,b,c) ( ((a)/(c))*(b) + (b)/(c)*((a)%(c)) + \
((a)%(c))*((b)%(c))/(c) )
#if MULDIV1(18536400,4634100,46341)!=1853640000
#error "MULDIV1(18536400,4634100,46341) fails"
#endif
#if MULDIV1(345437,268303627,46341)!=1999999999
#error "MULDIV1(345437,268303627,46341) fails"
#endif
note: at the expense of simplicity, we can reach c<92682 with:
#define MULDIV2(a,b,c) ( ((a)/(c))*(b)+(b)/(c)*((a)%(c))+\
MD__M( (c), (a)%(c), (b)%(c), (a)%(c)>(c)/2, (b)%(c)>(c)/2 ) )
#define MD__M(c,d,e,f,g) d*(g)+e*(f)-(f&&g)*c+\
(((f?c-d:d)*(g?c-e:e)-(f!=g))/c+(f!=g))*(d&&e?f!=g?-1:1:0)
#if MULDIV2(260756292,710863,92681)!=1999999999
#error "MULDIV2(260756292,710863,92681) fails"
#endif
#if MULDIV2(172572,1074113993,92681)!=1999999999
#error "MULDIV2(172572,1074113993,710863,92681) fails"
#endif
2) In my application, when c is big, it will share common primes factors with a and/or b and many of theses factors will be 2 3 or 5. Thus the fraction can be reduced. In particular, the powers of 2 can be removed relatively simply:
#define MULDIV3(a,b,c) MULDIV1( (a)/MD__A((a),(c)), (b)/MD__B((a),(b),(c)),\
(c)/MD__A((a),(c))/MD__B((a),(b),(c)) )
#define MD__A(a,c) (((a|c)^((a|c)-1))/2+1)
#define MD__B(a,b,c) MD__A(b,c/MD__A(a,c))
#if MULDIV3(1471048704,1032258064,759250944)!=1999999999
#error "MULDIV3(1471048704,1032258064,759250944) fails"
#endif
#if MULDIV3(944816128,1984188898,1518501888)!=1234567889
#error "MULDIV3(944816128,1984188898,1518501888) fails"
#endif
With slightly more math, and a preprocessor tolerant of huge macros, I managed to also remove enough powers of 3 and 5.
But I'm looking for something simpler and more general. Any idea? I could live with a slightly inaccurate result, provided the relative error is always at most 1/10000.
[Actually I want a*b/c by excess, that is (a*b-1)/c+1, and I have the problem that a and b can be 0, but these are minor details.]
TIA,
Francois Grieu