Dlaczego zmiana typu zmiennej wpływa na wynik ?

0

Miałem ostrzeżenie
warning: comparison of integers of different signs: 'int' and 'uint32_t' (aka 'unsigned int') [-Wsign-compare]
27 | if (diff < diff_min) {

Stwierdziłem że zmienię w kodzie kolegi typ zmiennej diff na uint , ostrzeżenie usunąłem ale się okazało że wynik jest inny 😄
int diff poprawna wartość WYNIK 7500000.000000
uint diff bledna wartość WYNIK -nan

Z tego co widzę po drodze jest dzielenie przez zero , ale dlaczego wyniki są inne ? UB ?

https://godbolt.org/z/Gf74E4hc6


#include <cstdint>
#include <cmath>
#include <cstdio>

auto Set_D = [](double newM, int Rmin, double &RefS)
{
    double RefSi;
    double roundFact;
    uint32_t diff_min{2147483647};
    int diff; // <<<<<<<<<<<<<< TUTAJ   <<<<<<<<<
    int nr;
    int dn;
    double rsf = newM * 1000000 ;
    double sr = 60000000;

    RefSi = rsf / sr;

    for (int i = 0; i < Rmin; ++i){

        roundFact = round(i * RefSi);
        printf("roundFact = %f\n", roundFact);

        auto tmp = rsf - roundFact * sr / (double)i;
        diff = fabs(tmp);
        printf("tmp:%f  diff:%d  diff_min:%d\n", tmp, diff, diff_min);

        if (diff < diff_min) {
            printf(".\n");
            diff_min = diff;
            nr = (int)roundFact;
            dn = i;
        }
    }

    printf("dn = %d\n", dn);

    RefS = sr * nr / (double)dn;

};

int main()
{
    double newM=7.5;
    int Rmin=16;
    double RefS=0;

    Set_D(newM, Rmin, RefS);

    printf("WYNIK %f", RefS);
}

Zdecydowanie podoba mi sie bardziej domyślny sposób obsługi dzielenia przez zero w Delphi
Domyślnie wygląda tak:
screenshot-20240509134145.png 😜

7

Bo int i uint mają inne zakresy liczbowe. Nie poprawiaj czyjegoś kodu tylko dlatego że ci kompilator wypluł warna, najpierw musisz zrozumieć jak ten kod działa.
https://www.geeksforgeeks.org/difference-between-unsigned-int-and-signed-int-in-c/

0

@hzmzp na chwilę sie rozproszyłem ;)
Ja wyszedłem z założenia: skoro zmienna diff ustawiana jest przez funkcje abs(), to nie będzie problemu

A tu chyba był zaplanowany zamysł autora kodu
diff = fabs(-nan);

Jak jest int to diff = -2147483648
A jak uint to diff = 0

I to zmienia całkowicie wynik

Aż muszę zapytać autora co miał na myśli

6

Clang UB sanitizer: https://godbolt.org/z/b76oosfYd

Program stderr

/app/example.cpp:26:16: runtime error: nan is outside the range of representable values of type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /app/example.cpp:26:16

W twoich logach na samej górze stoi:

roundFact = 0.000000
tmp:-nan  diff:-2147483648  diff_min:2147483647

diff:-2147483648 chyli konwersja z double do int nie poszła najlepiej, w tej linijce:

diff = fabs(tmp);

Wartość bezwzględna z NaN skonwertowana do int daje INT_MIN.
Po zmianie na uint32_t Nan jest konwertowane do 0 co staje się minimum dla diff_min i kończysz z dzieleniem przez zero na końcowym wyniku.

Wygląda na to, że pętla nie powinna iść od 0 by nie mieć dzielenia przez zero.
Po poprawieniu zakresu for i zmianie typu wynik jest ten sam: https://godbolt.org/z/153zdKW9W

7.3.10 Floating-integral conversions

A prvalue of a floating-point type can be converted to a prvalue of an integer type.
The conversion truncates; that is, the fractional part is discarded.
The behavior is undefined if the truncated value cannot be represented in the destination type.
[Note If the destination type is bool, see [conv.bool]. - end note ]

1
Marius.Maximus napisał(a):

Miałem ostrzeżenie
warning: comparison of integers of different signs: 'int' and 'uint32_t' (aka 'unsigned int') [-Wsign-compare]
27 | if (diff < diff_min) {

Stwierdziłem że zmienię w kodzie kolegi typ zmiennej diff na uint , ostrzeżenie usunąłem ale się okazało że wynik jest inny 😄
int diff poprawna wartość WYNIK 7500000.000000
uint diff bledna wartość WYNIK -nan

Z tego co widzę po drodze jest dzielenie przez zero , ale dlaczego wyniki są inne ? UB ?

https://godbolt.org/z/Gf74E4hc6


#include <cstdint>
#include <cmath>
#include <cstdio>

auto Set_D = [](double newM, int Rmin, double &RefS)
{
    double RefSi;
    double roundFact;
    uint32_t diff_min{2147483647};
    int diff; // <<<<<<<<<<<<<< TUTAJ   <<<<<<<<<
    int nr;
    int dn;
    double rsf = newM * 1000000 ;
    double sr = 60000000;

    RefSi = rsf / sr;

    for (int i = 0; i < Rmin; ++i){

        roundFact = round(i * RefSi);
        printf("roundFact = %f\n", roundFact);

        auto tmp = rsf - roundFact * sr / (double)i;
        diff = fabs(tmp);
        printf("tmp:%f  diff:%d  diff_min:%d\n", tmp, diff, diff_min);

        if (diff < diff_min) {
            printf(".\n");
            diff_min = diff;
            nr = (int)roundFact;
            dn = i;
        }
    }

    printf("dn = %d\n", dn);

    RefS = sr * nr / (double)dn;

};

int main()
{
    double newM=7.5;
    int Rmin=16;
    double RefS=0;

    Set_D(newM, Rmin, RefS);

    printf("WYNIK %f", RefS);
}

Zdecydowanie podoba mi sie bardziej domyślny sposób obsługi dzielenia przez zero w Delphi
Domyślnie wygląda tak:
screenshot-20240509134145.png 😜

Po zobaczeniu tego kodu nie do końca rozumiem "co autor miał na myśli"(bardziej jednoznaczne nazwy, ew. komentarze), ale tego typu błędy niemal zawsze są spowodowane innymi zakresami typów, albo coś jest nie tak przy rzutowaniu. Chat GPT od razu wykrył potencjalne przekroczenie limitów zmiennej. Można by też zdebugować aby sprawdzić na którym konkretnie etapie program przestaje działać tak jak powinien.

1 użytkowników online, w tym zalogowanych: 0, gości: 1