Search code examples
c++type-conversionlong-integerimplicit-conversionuint8t

Implicit conversion from uint8_t to int gone wrong, when explicit one gone well


I've got a program, which gets numbers n(quantity of blocks) and r(volume), and after it gets n sizes in format firstsize secondsize thirdsize (e.g. 1 1 1) so the overall simplest input is: 1 1 1 1 1 which in theory should return 1. But it doesn't.

I have the following code:


#include <iostream>
#include <cstdint>
#include <vector>

using namespace std;

struct Size{
    long long w=0,h=0,d=0;
};
istream& operator>>(istream& istr, Size& rval){
    istr >> rval.w >> rval.h >> rval.d;
    return istr;
}


long long calcMass(Size s, int r){
    long long res = s.d*s.h*s.w*r;

    cout << "Gained:" << res << '\n';
    cout << "Got: sizes:{" << s.d << ' ' << s.h << ' ' << s.w << "}, p = " << r << '\n';
    return res;
}


int main(){
    int n;
    long long sum = 0;

    Size s;
    uint8_t r; // Condition is that r<100

    cin >> n >> r;

    for(int i=0; i<n; i++){
        cin >> s;
        sum += calcMass(s,r);
    }
    cout << sum;

}

Actually, I got this problem solved by changing type of the r variable in main() from uint8_t to int, but the problem is, I don't understand why it didn't work whilst being uint8_t. When I try the following code:

    cout <<'\n' << static_cast<long long>(static_cast<int>((static_cast<uint8_t>(1))));

It gives me 1.

So, I decided to inspect the situation with uint8_t r in main(). uint8_t r = 1 in main() somehow becomes the int r = 49 in calcMass() and I do not undertand why.

I know about the rules of implicit conversion, that before the operation variables smaller in size than int are converted to int, and then the "smaller in size" variable is converted to bigger one (e.g. int r to long long in calcMass(Size,int) function?), but I do not understand the thing: why do uint8_t r = 1 becomes int r = 49?

Also, I tried changing the type of r in calcMass() from int to uint8_t. As supposed, uint8_t r = 1 in main() becomes uint8_t r = 1 in calcMass(), but the result of this function is still 49!

These I used to run the program:

Operating System: Windows 10 Enterprise LTSC
System Type: 64bit
C++ compiler: MinGW

I would be pleased if somebody could explain why explicit conversion of uint8_t to long long isn't equal to the imlpicit one when passing it to calcMass() and doing operations with long long using it.


Solution

  • IMHO, OP suspected implicit vs. explicit conversion issues but that's not the reason. Instead, the stream I/O operators have to be investigated.

    Elaborating the comment of S.M.:

    49 is ASCII code of '1'. So look in the direction of unsigned char to understand why you get 49.

    The std::stream operators work a little bit different for char, signed char, and unsigned char (operator<<(std::basic_ostream), operator>>(std::basic_istream)) than for the other integral types (std::basic_ostream::operator<<, std::basic_istream::operator>>).

    That's for input as well as for output.

    std::cout << (int)49; prints 49 but std::cout << (char)49; prints 1.

    std::uint8_t is very likely a typedef unsigned char uint8_t;.
    (SO: Implementation of fixed width integer types std::uint8_t and std::int8_t, C++)

    Sample:

    #include <iostream>
    #include <sstream>
    #include <cstdint>
    
    int main()
    {
      std::cout << "(int)49: " << (int)49 << "\n";
      std::cout << "(char)49: " << (char)49 << "\n";
      std::cout << "(std::uint8_t)49: " << (std::uint8_t)49 << "\n";
      int i; char c; std::uint8_t u8;
      std::istringstream in("49\n1\n1");
      in >> i >> c >> u8;
      std::cout << "i: " << i << '\n';
      std::cout << "(int)c: " << (int)c << '\n';
      std::cout << "(int)u8: " << (int)u8 << '\n';
    }
    

    Output:

    (int)49: 49
    (char)49: 1
    (std::uint8_t)49: 1
    i: 49
    (int)c: 49
    (int)u8: 49
    

    Live Demo on coliru