5

I need to write a function that returns true if the multiplication of two numbers is greater than ULONG_MAX limit. Otherwise returns false.

I tried the following method:

bool isGtThanULONG_MAX(double A, double B) {
    double result = A * B;
    if (result > ULONG_MAX)
        return true;
    else
    {
        //If this could be due to overflow, then again check:
        double temp = result / A;
        
        if (A != 0 && (temp != B)) {
        // overflow handling
        return true;
    }
    return false;
}

}

output: 1st and 4th line in below cout statements are giving OVERFLOW (which is obviously wrong output) and rest are giving NO-OVERFLOW (which is correct) in output. Why it is failing? am I missing anything? please help.

    cout << (isGtThanULONG_MAX(10, 0.0000000000000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;//OVERFLOW
    cout << (isGtThanULONG_MAX(10, 0.000000000000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.00000000000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.0000000000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;//OVERFLOW
    cout << (isGtThanULONG_MAX(10, 0.000000000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.00000000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.0000000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.000000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.00000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.0000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.000000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.00000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.0000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.000001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.00001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.0001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.001) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.01) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 0.1) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;
    cout << (isGtThanULONG_MAX(10, 1) == true ? "OVERFLOW" : "NO-OVERFLOW") << endl;

PS - I tried this program on MS Visual Studio professional 2015 on windows.

Jatin
  • 1,857
  • 4
  • 24
  • 37
  • I looked at the linked questions, but they describe about multiplication of two integer. My case involved decimal numbers also. And please have a look at the cout statements #1 and #4. I need to know why they are failing? please answer. This question is not a duplicate of any question. – Jatin May 17 '21 at 08:42
  • Hi, @πάντα ῥεῖ, I just need to know why #1 and #4 cout statements are failing. Can you please help me in this regard? – Jatin May 17 '21 at 08:52
  • 2
    Use a debugger, single-step through the progam and see yourself. – j6t May 17 '21 at 08:53
  • 2
    This is not a duplicate of the question which considers to unsigned types as the parameters. Please don't be so eager to close questions as duplicates. Thank you. – Bathsheba May 17 '21 at 08:54
  • @Bathsheba: Thank you so much. I appreciate you went through the question and reopened it again. – Jatin May 17 '21 at 08:57
  • Could you give an example of values that require the else part of your code? I ask because using MSVC 2019, return ((A * B) > ULONG_MASK) seems to always give the correct answer. – fpiette May 17 '21 at 09:19
  • 1
    Note that a `double` or any IEEE 754 representation is often not an exact number. Most of the time it's an approximation. – JHBonarius May 17 '21 at 09:21
  • @JHBonarius: Ta, I'll clean up. – Bathsheba May 17 '21 at 09:28
  • @fpiette: Indeed the simplistic implementation works on MSVC because type long is 32-bit even on 64-bit architectures in the Microsoft world. On unix systems `return ((A * B) > ULONG_MASK)` fails for A and B equal to `pow(2,32)` (ie `0x1p32`). – chqrlie May 17 '21 at 09:32
  • Then you should tag your question with linux and tell which compiler you use. Stop talking about MSVC except to say it works without your else clause. – fpiette May 17 '21 at 12:08

1 Answers1

4

There is a problem if ULONG_MAX cannot be represented exactly as a double. For example if type double uses IEEE representation and long has 64 bits, ULONG_MAX be rounded to the next power of 2 when converted implicitly to double type for the comparison. Hence if this happens, the comparison should be result >= ULONG_MAX to ensure that isGtThanULONG_MAX(0x1p32, 0x1p32) returns true, instead of just > which works for 32-bit longs:

#include <float.h>
#include <limits.h>
#include <stdbool.h>

bool isGtThanULONG_MAX(double A, double B) {
    double result = A * B;
    if (ULONG_MAX + 1.0 == ULONG_MAX)
        return result >= ULONG_MAX;
    else
        return result > ULONG_MAX;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    Hope you don't mind my edit. Have an upvote. – Bathsheba May 17 '21 at 09:29
  • With 64-bit longs and 64-bit IEEE doubles, `(double)ULONG_MAX == (double)ULONG_MAX + 1`. – n. m. could be an AI May 17 '21 at 09:41
  • 1
    Sure? Try 65536 * 65535.99999. – n. m. could be an AI May 17 '21 at 10:57
  • @n.'pronouns'm. Thank you for pointing this problem. I updated the solution with an explicit test for overflow, which is optimized by the compiler. – chqrlie May 17 '21 at 11:57
  • @chqrlie: I didn't understand this condition "if (ULONG_MAX + 1.0 == ULONG_MAX)". Can you tell me, On what values of A & B control will go inside the if condition? – Jatin May 18 '21 at 05:10
  • @Jatin: if the value of `ULONG_MAX` is so large that it cannot be represented precisely as a `double`, eg: 64-bit `long` and IEEE `double` with a `53` bit mantissa, converting `ULONG_MAX` to `double` type rounds its value to `ULONG_MAX+1` which is a power of 2. On such platforms adding `1.0` to this value has no effect, and comparing `A * B > (double)ULONG_MAX` would be false if the product `A * B` is exactly `pow(2,64)`. Testing `A * B >= (double)ULONG_MAX` catches this case, which occurs for example for `A = B = pow(2,32)`. – chqrlie May 18 '21 at 06:08
  • @chqrlie: thank you for explanation. Does your code also work on both windows & Linux/Unix flovours with gcc compiler. Just wanted to know if this code is portable? – Jatin May 18 '21 at 06:40
  • @Jatin: this code should be portable to a variety of targets. You might get a warning about `ULONG_MAX + 1.0` having a precision issue, which is exactly the reason the test works :) – chqrlie May 21 '21 at 07:25