3

When you're a total noob programming, a lot of things looks like magic, so solving some classic problems at SPOJ using C language, I found one called DIAGONAL.

After some attempts I gave up and went searching for solutions and I found this one:

#include <stdio.h>

int main() {
    int num_cases, i;
    long long mod_by = 24*1000000007L;
    scanf("%d", &num_cases);

    long long n;
    long long answer;
    for(i = 0; i < num_cases; i++) {
        scanf("%lld", &n);
        long long x = (n*(n-1)) % (mod_by);
        long long y = (x*(n-2)) % (mod_by);
        long long z = (y*(n-3)) % (mod_by);
        answer = z / 24;
        printf("%lld\n", answer);
    }

    return 0;
} 

At first glance I thought that L with the modulo was some kind of mistake made by the user who posted the code (haha who would mix numbers with letters this way?! nonsense! -thought the noob-), but when I fixed the (many) wrongs in my code and used this modulo, it didn't work without the magic L (I got Wrong Answer). Then I substituted the L with the ASCII code number (because well, maybe it was that!) and it also didn't work.

Since then I'm trying to understand what is the logic behind this. How can I get the same results removing this L?

It's not like one just woke up in the morning and "uhm, maybe if I add this L it will work", but I just couldn't find other examples of this (random letter added to a large number for calculations) googling.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • An important difference between this post and the dupe is that the suffix has a following insufficient effect due to a calculation whereas the dupe is a simple constant. – chux - Reinstate Monica Jun 29 '20 at 23:20

3 Answers3

3

long long mod_by = 24*1000000007L; is a problem.

The L suffix insures the constant 1000000007 is at least of type long.1 Without the L, the type may have been int or long.

Yet since long may only be 32-bit, the ~36-bit product can readily overflow long math leading to undefined behavior (UB).

The assignment to a long long happens after and does not affect the type nor range of the multiplication.

Code should use LL to form the product and protect against overflow.

long long mod_by = 24LL*1000000007;
// or
long long mod_by = 24*1000000007LL; 

In general, make certain the calculation occurs at least with the width of the destination.

See also Why write 1,000,000,000 as 1000*1000*1000 in C?.


1 In OP's case, apparently int is 32-bit and long is 64-bit so code "worked" with a L and failed without it. Yet porting this code to a 32-bit long implementation, code fails with or without an L. Insure long long math with an LL constant.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

L is a suffix that means that the number is of type long, otherwise it will default to type int that means the result of the operation 24 * 1000000007 will also be of type int.

As an int is usually 4 bytes in size the result will overflow, this happens before it being assigned to mod_by, and for that reason it invokes undefined behavior.

As the result of the arithmetic operation is converted to the larger type, e.g:

int * int = int

int * long = long

For the result of the operation to be of type long one of the operands must also be of type long.

Note that long is not guaranteed to have 8 bytes in size, the minimum allowed size for a long is 4 bytes, so you can invoke the same undefined behavior deppending on the platform where you compile your program.

Using LL for long long will be more portable.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
anastaciu
  • 23,467
  • 7
  • 28
  • 53
1

The L suffix when applied to an integer constant means the constant has type long.

This is done to prevent overflow when the value is multiplied by 24. Without the suffix, two constants of type int are multiplied giving an int result. Assuming 32 is 32 bits, the result will overflow the range of an int (231-1) causing undefined behavior.

By making the constant have type long, assuming a long is 64 bits, it allows the multiplication to be done using and type and therefore not cause overflow and give you the correct value.

dbush
  • 205,898
  • 23
  • 218
  • 273