PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Monday, October 17, 2022

[FIXED] How can I compute a * b / c when both a and b are smaller than c, but a * b overflows?

 October 17, 2022     c, integer, integer-arithmetic, integer-overflow     No comments   

Issue

Assuming that uint is the largest integral type on my fixed-point platform, I have:

uint func(uint a, uint b, uint c);

Which needs to return a good approximation of a * b / c.

The value of c is greater than both the value of a and the value of b.

So we know for sure that the value of a * b / c would fit in a uint.

However, the value of a * b itself overflows the size of a uint.

So one way to compute the value of a * b / c would be:

return a / c * b;

Or even:

if (a > b)
    return a / c * b;
return b / c * a;

However, the value of c is greater than both the value of a and the value of b.

So the suggestion above would simply return zero.

I need to reduce a * b and c proportionally, but again - the problem is that a * b overflows.

Ideally, I would be able to:

  • Replace a * b with uint(-1)
  • Replace c with uint(-1) / a / b * c.

But no matter how I order the expression uint(-1) / a / b * c, I encounter a problem:

  • uint(-1) / a / b * c is truncated to zero because of uint(-1) / a / b
  • uint(-1) / a * c / b overflows because of uint(-1) / a * c
  • uint(-1) * c / a / b overflows because of uint(-1) * c

How can I tackle this scenario in order to find a good approximation of a * b / c?


Edit 1

I do not have things such as _umul128 on my platform, when the largest integral type is uint64. My largest type is uint, and I have no support for anything larger than that (neither on the HW level, nor in some pre-existing standard library).

My largest type is uint.

Edit 2

In response to numerous duplicate suggestions and comments:

I do not have some "larger type" at hand, which I can use for solving this problem. That is why the opening statement of the question is:

Assuming that uint is the largest integral type on my fixed-point platform

I am assuming that no other type exists, neither on the SW layer (via some built-in standard library) nor on the HW layer.


Solution

I've established a solution which work in O(1) complexity (no loops):

typedef unsigned long long uint;

typedef struct
{
    uint n;
    uint d;
}
fraction;

uint func(uint a, uint b, uint c);
fraction reducedRatio(uint n, uint d, uint max);
fraction normalizedRatio(uint a, uint b, uint scale);
fraction accurateRatio(uint a, uint b, uint scale);
fraction toFraction(uint n, uint d);
uint roundDiv(uint n, uint d);

uint func(uint a, uint b, uint c)
{
    uint hi = a > b ? a : b;
    uint lo = a < b ? a : b;
    fraction f = reducedRatio(hi, c, (uint)(-1) / lo);
    return f.n * lo / f.d;
}

fraction reducedRatio(uint n, uint d, uint max)
{
    fraction f = toFraction(n, d);
    if (n > max || d > max)
        f = normalizedRatio(n, d, max);
    if (f.n != f.d)
        return f;
    return toFraction(1, 1);
}

fraction normalizedRatio(uint a, uint b, uint scale)
{
    if (a <= b)
        return accurateRatio(a, b, scale);
    fraction f = accurateRatio(b, a, scale);
    return toFraction(f.d, f.n);
}

fraction accurateRatio(uint a, uint b, uint scale)
{
    uint maxVal = (uint)(-1) / scale;
    if (a > maxVal)
    {
        uint c = a / (maxVal + 1) + 1;
        a /= c; // we can now safely compute `a * scale`
        b /= c;
    }
    if (a != b)
    {
        uint n = a * scale;
        uint d = a + b; // can overflow
        if (d >= a) // no overflow in `a + b`
        {
            uint x = roundDiv(n, d); // we can now safely compute `scale - x`
            uint y = scale - x;
            return toFraction(x, y);
        }
        if (n < b - (b - a) / 2)
        {
            return toFraction(0, scale); // `a * scale < (a + b) / 2 < MAXUINT256 < a + b`
        }
        return toFraction(1, scale - 1); // `(a + b) / 2 < a * scale < MAXUINT256 < a + b`
    }
    return toFraction(scale / 2, scale / 2); // allow reduction to `(1, 1)` in the calling function
}

fraction toFraction(uint n, uint d)
{
    fraction f = {n, d};
    return f;
}

uint roundDiv(uint n, uint d)
{
    return n / d + n % d / (d - d / 2);
}

Here is my test:

#include <stdio.h>

int main()
{
    uint a = (uint)(-1) / 3;            // 0x5555555555555555
    uint b = (uint)(-1) / 2;            // 0x7fffffffffffffff
    uint c = (uint)(-1) / 1;            // 0xffffffffffffffff
    printf("0x%llx", func(a, b, c));    // 0x2aaaaaaaaaaaaaaa
    return 0;
}


Answered By - goodvibration
Answer Checked By - David Marino (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing