Using float to check for overflow in unsigned long int

2

Hello, how are you? Please review this code:

#define max 4294967295
unsigned long int collatz(unsigned long int n, unsigned long int passos, float *maiorN) {
    float overflow = 3 * n + 1;

    if (n == 1)
        return passos;
    else if (n % 2 == 0)
        return collatz(n / 2, passos + 1, maiorN);
    else if ((overflow) < max) {
        if (overflow > *maiorN)
            *maiorN = overflow;
        return collatz(overflow, passos + 1, maiorN);
    }
    else
        return -1;
}

The value set to max is the largest possible value to store in type unsigned long int. It turns out it is quietly stored in a float. The idea is to test the multiplication and if it gives greater than the max constant the program should return -1.

However, there seems to be something wrong when this value is passed to the printipal function through the pointer.

Could someone please help me?

EDIT: I leave below the implementation of main

int main(int numargs, char *args[]) {
    unsigned long int passos, n, maiorP, total, N, media;
    FILE *arquivo;
    int inicio, fim;
    float maiorN;

    inicio = N = atoi(args[1]);
    fim = atoi(args[2]); 

    passos = total = maiorP = 0;
    maiorN = 1;
    arquivo = fopen("Log.txt", "w+");

    printf("Teste   Num passos\n");
    fprintf(arquivo, "Teste   Num passos\n");

    for (n = inicio; n <= fim; n++) {
        passos = collatz(n, passos, &maiorN);

        if (passos > maiorP) {
            N = n;
            maiorP = passos;
        }
        if (passos == -1)
            break;


        total += passos;

        fprintf(arquivo, "%lu %10lu\n", n, passos);
        printf("%lu %10lu\n", n, passos);
        passos = 0;
    }    

    media = total/(fim - inicio);

    printf("\nForam testados %d numeros\n", fim - inicio + 1);
    printf("\nDentre os valores testados o num %lu levou %lu passos para convergir para 1 e atingiu o valor maximo de %.0f", N, maiorP, maiorN);
    printf("\nA media de passos para chegar em 1 foi de %lu passos\n", media);

    fprintf(arquivo, "\nForam testados %d numeros\n", fim - inicio + 1);
    fprintf(arquivo, "\n\nDentre os valores testados o num %lu levou %lu passos para convergir para 1 e atingiu o valor maximo de %.0f", N, maiorP, maiorN);
    fprintf(arquivo, "\nA media de passos para chegar em 1 foi de %lu passos", media);



    fclose(arquivo);
    return 0;
}
    
asked by anonymous 24.08.2018 / 22:14

2 answers

0

float has maximum precision of 23 bits, as shown in this answer . Because% w / o% must be 32 bits,% w / o% will not be able to store 4294967295 with sufficient precision. See this:

#include <stdio.h>

int main(void) {
    float f = 4294967295;
    printf("%f", f);
}

The output is this:

4294967296.000000

See this on ideone.

Then you can use two outputs:

  • Use unsigned long int . That will give you 52 bits of precision. But if you want to switch to float that has 64 bits (or even double ), it will continue with same problem.

  • Verify that unsigned long long int is greater than 1431655764 and pop the overflow if it is (you can put a __uint128 logo at the beginning of the function to do this). How did I get on 1431655764? Simple:

    #define max_collatz ((max - 1) / 3)
    

The number 4294967295 is easier to express in hexadecimal: n . The if there is 0xFFFFFFFF , that is, a unit less than one third of the maximum value.

The approach to check if max_collatz does not work for numbers between 2147483648 and 2863311530 (in hexadecimal this is 0x55555554 and 3 * n + 1 < n , or half and two-thirds of the maximum value). So that's not a good approach to take.

EDIT:

I re-edited your 0x80000000 function and did this test:

#include <stdio.h>

#define max 0xFFFFFFFF
#define max_collatz ((max - 1) / 3)

unsigned long int collatz(unsigned long int n, unsigned long int passos, unsigned long int *maiorN) {
    printf("Passo %lu: %lu\n", passos, n);
    if (n == 1) return passos;
    if (n % 2 == 0) return collatz(n / 2, passos + 1, maiorN);
    if (n > max_collatz) return -1;
    int p = 3 * n + 1;
    if (p > *maiorN) *maiorN = p;
    return collatz(p, passos + 1, maiorN);
}

int main(void) {
    unsigned long int maior = 0;
    collatz(1161, 0, &maior);
    printf("Maior: %lu\n", maior);
    return 0;
}

The output was the entire 1161 series with 181 steps. The largest number found was 190996. See here working on ideone.

I tried with your original code:

#include <stdio.h>

#define max 4294967295
unsigned long int collatz(unsigned long int n, unsigned long int passos, float *maiorN) {
    printf("Passo %lu: %lu. Maior = %f\n", passos, n, *maiorN);
    float overflow = 3 * n + 1;

    if (n == 1)
        return passos;
    else if (n % 2 == 0)
        return collatz(n / 2, passos + 1, maiorN);
    else if ((overflow) < max) {
        if (overflow > *maiorN)
            *maiorN = overflow;
        return collatz(overflow, passos + 1, maiorN);
    }
    else
        return -1;
}

int main(void) {
    float maior = 0;
    collatz(1161, 0, &maior);
    printf("Maior: %d\n", (int) maior);
    return 0;
}

The output was also the entire 1161 series with 181 steps and the largest number found was also 190996. See here working on ideone.

    
24.08.2018 / 22:28
0

You do not have the code you use to call this function out there - but just passing a pointer to a float type for it to work.

The problem there, for large numbers is that as many as possible in an unsigned long int ( 2 ** 32 - 1 ) is not "is quietly stored in a float". A long int has 32 bits, a float has 32 bits in the toal, but uses 8 bits to hold the exponent - do you realize you have less precision information? What happens is that floating-point numbers, such as float, double, and long double, have a greater range, but a much lower precision - they use only 23 bits to store the precision of the number in itself, and 8 bits to indicate the exponent in base 2 of the number. So the largest number the float can contain without losing accuracy is 2 ** 23: 8388608 (8 million ...), with 2 ** 32 -1: 4294967295 - 4 billion ... you used is the largest unsigned int of 32 bits - if using unsigned long long int can have numbers up to 2 ** 64 -1: 18446744073709551615 without losing accuracy)

Then, but for numbers greater than 2 ** 23, the float type simply discards the information from the least significant digits - and the collatz algorithm does not stand a chance of working, since it just depends on the exact integers. >

Simply use long integers there, make the comparison with the maximum value of multiplying the number by 3, and everything should work.

that is:

#define max 18446744073709551615
#define max_3 max/3
...

if (overflow > max_3) return -1;
    
24.08.2018 / 22:34