1

This is my code with int j:

void solve(){
    unsigned long long n;
    cin>>n;
    unsigned long long sum = 0;
    int j = 1;
    for(int i=3;i<n+1;i+=2){
        sum += ((4*i)-4)*(j);
        j++;
    }
    cout<<sum<<"\n";
    }
Input:
499993

Output:
6229295798864

but it is giving wrong output, and here is my code with long long j which is working fine:

void solve(){
    int n;
    cin>>n;
    unsigned long long sum = 0;
    long long j = 1;
    for(int i=3;i<n+1;i+=2){
        sum += ((4*i)-4)*(j);
        j++;
    }
    cout<<sum<<"\n";
    }
Input:
499993

Output:
41664916690999888

In this case value of j is well below 499993, which is in int range but still, it's not working. Why is it actually happening?

Here is the link to the actual problem. In case, you want to have a look.

  • 1
    Presumably `((4*i)-4)*(j)` produces something larger than an `int` can hold – UnholySheep May 15 '20 at 10:46
  • @UnholySheep Yes, it is but we are storing it in variable `sum`. The value of `j` is still not crossing int limit? – Keshav Garg May 15 '20 at 10:48
  • 7
    More to the point, the expression is not promoted to unsigned long long until the actual assignment (due to the rank of `unsigned long long` vs `int`). All of the operands during evaluation remain `int`, as does the result (which overflowed). You'll find if you changed `i` instead of `j` it would likewise resolve correctly. – WhozCraig May 15 '20 at 10:48
  • 1
    You seem to be under the impression an expression will self-promote when it no longer "fits" in the given type. That's not how the language works. Implicit promotion happens at the operand level, and in your case it doesn't happen until the assignment. Btw, you can also switch the leading `4` to `4ULL` (think I got that right; i rarely use literal constants beyond UL), and it should also work, leaving `i` and `j` as is. The point is to *force* the promotion of that expression to `unsigned long long` *before* the assignment. – WhozCraig May 15 '20 at 10:57
  • 1
    The point of having `long long` is to be able to store larger integral values. This has absolutely nothing to do whatsoever with the fact that all operands in an integer expression are plain `int`s. As long as they are, the operation will be done on `int`s. The fact that there's some other variable in your program that's a `long long int`, or even that the result of the expression will be assigned to it, is irrelevant and doesn't change the fact that all operands are `int`s, so all the expressions will be evaluated as such. – Sam Varshavchik May 15 '20 at 10:58
  • 1
    By the way, this is typically the case where you should avoid the use of unsigned integral types. If your sum overflowed, it would probably go unnoticed given that it would simply warp around, producing an incorrect but seemingly acceptable answer. – Albert May 15 '20 at 11:11
  • 1
    We can call it "outward" type evaluation. Some languages do opposite, they assume resulting type and evaluate types "inside". I am not sure which languages overall, because I really know only few. – sanaris May 15 '20 at 11:14
  • 1
    The first routine crashed on my machine with an int overflow. (An int overflow is undefined behavior. They cause crashes on my machine.) – Eljay May 15 '20 at 11:38
  • 1
    This doesn't address the question, but when you talk about types it's important to be precise. Your question talks about `long long`, but your code uses `unsigned long long`. Those are two different types, with significantly different properties. If you muddle them, sooner or later, you'll get yourself into trouble. – Pete Becker May 15 '20 at 12:45

2 Answers2

3

Notice that the result of ((4*i)-4)*(j) is an int, since both i and j are int types. The right hand side is promoted to unsigned long long only when adding ((4*i)-4)*(j) to sum. But the expression ((4*i)-4)*(j) already overflows the size of the int type for a large enough n before being promoted.

However, if you change either of i or j to unsigned long long, the expression ((4*i)-4)*(j) is evaluated to unsigned long long, safely inside the size limits.

  • 2
    It's not just `i` and `j` that are `int` types; `4` is, too. Changing the expression to `(4ULL * i - 4) * j` (yes, I've removed (redundant (confusing)) parentheses) works, too. I don't have an opinion on which of the three approaches here is best. +1. – Pete Becker May 15 '20 at 12:48
2

In the first code snippet in the expression

((4*i)-4)*(j)

of the assignment statement

sum += ((4*i)-4)*(j);

the both operands (4*i)-4) and (j) have the type int. So the type of the expression (the common type of the operands) is also int. But an object of the type int is not large enough to store the result value. So here an overflow takes place.

When the j is declared as having the type long long

long long j = 1;

then the common type of the expression above is also long long. It means that due to the usual arithmetic conversion this operand (4*i)-4) is also converted to the type long long. And an object of this type can store the result value provided for the inputted data.

You can check what are the maximum values that can be stored in objects of the type int and long long.

Here you are.

#include <iostream>
#include <limits>

int main() 
{
    std::cout << "The maximum value of an object of the type int is "
              << std::numeric_limits<int>::max()
              << '\n';

    std::cout << "The maximum value of an object of the type long long is "
              << std::numeric_limits<long long>::max()
              << '\n';

    return 0;
}

The program output might look like

The maximum value of an object of the type int is 2147483647
The maximum value of an object of the type long long is 9223372036854775807
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335