why `( i < (str.size()-(3-vec.size())) )` is true? - c++

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string str("0");
vector<int> vec;
int upper_bound = str.size()-(3-vec.size());
int i = 0;
if ( i < upper_bound ) // Line 1
cout << "i < upper_bound - 1\n";
else
cout << "pass\n";
if ( i < (str.size()-(3-vec.size())) ) // Line 2
cout << "i < upper_bound - 2\n";
else
cout << "pass\n";
return 0;
}
Output is as follows:
pass
i < upper_bound
Question> Why Line 1 and Line 2 print different results?

Mathematically, str.size()-(3-vec.size()) is 1-(3-0), which is -2. However, since these are unsigned values, the result is also unsigned, and so has a large positive value.
Converting this to int to initialise upper_bound technically gives undefined behaviour, but in practise will give you -2; so the first test passes.
The second test compares with the large unsigned value rather than -2.

The problem is that expression str.size()-(3-vec.size())) has some unsigned type. So it may not be negative. It is an unsigned type due to 1) str.size() and vec.size() are of some unsigned types according to definitions of std::string::size_type and std::vector<int>::size_type 2) the usual arithmetic conversion.
In the first expression you explicitly assigned an unsigned expression to type int. So as the sign bit is set then the value of this object is negative.
To understand this try for example to execute these statements
std::cout << -1 << std::endl;
std::cout << -1u << std::endl;
or
std::cout << 0u -1 << std::endl;
Here -1u and 0u - 1 have type unsigned int and the same bit combination as -1.

While comparing signed and unsigned types, compiler will convert the signed types to unsigned. That creates the weird result.

Related

Returning multiple values using pointers in a function

Can anyone help me to understand how to return multiple values using pointers in a function? I am confused with the following example.
Can we assign other values to int value besides 0 or 1, while the program can still run normally?
What does defining the value of 0 and 1 in the if statement and else statement do for us in the int factor function?
What does if(!error) statement do in the main ()?
#include <iostream>
using namespace std;
int factor(int n, int *p_addition, int *p_subtraction, int *p_squared, int *p_cubed)
{
int value = 0;
if (n >= 0 && n <= 100)
{
*p_addition = n + n;
*p_subtraction = n - n;
*p_squared = n*n;
*p_cubed = n*n*n;
value = 0;
}
else
{
value = 1;
}
// This function will return a value of 0 or 1
return value;
}
int main()
{
int number, num_add, num_sub, squared, cubed;
int error;
cout << "Enter a number between 0 and 100: ";
cin >> number;
error = factor(number, &num_add, &num_sub, &squared, &cubed);
if (!error)
{
cout << "number: " << number << endl;
cout << "num_add: " << num_add << endl;
cout << "num_sub: " << num_sub << endl;
cout << "squared: " << squared << endl;
cout << "cubed: " << cubed << endl;
}
else
{
cout << "Error encountered!!" << endl;
}
return 0;
}
int is at least 16 bits (depending on the system - hardware, operating system, 32-bit computing / vs. 64-bit computing), for numeric range, cf: https://en.cppreference.com/w/cpp/language/types (at least -32768 to 32767 for 16 bit integers).
factor contains return value, which (here) signifies to the caller, whether an error occurred. It has no other effect within factor.
The ! negates the boolean value, so that the test is for false. if with an int implicitly converts to bool and tests for error being 0: https://en.cppreference.com/w/cpp/language/implicit_conversion#Boolean_conversions The value zero (for integral [...]) [...] become[s] false. All other values become true.
So any value beside 0 would have the same effect instead as 1.
It would have been better to name value something like wrong_input_range.
It would have been better to make value a bool type instead of int and make the return type of factor bool, too.
Whether your main returns a value beside 0 in case of the error, you can decide by yourself. Within a script, the returned value of called programs often is tested to know, whether the script can continue.

Converting a ulong to a long

I have a number stored as a ulong. I want the bits stored in memory to be interpreted in a 2's complement fashion. So I want the first bit to be the sign bit etc. If I want to convert to a long, so that the number is interpreted correctly as a 2's complement , how do I do this?
I tried creating pointers of different data types that all pointed to the same buffer. I then stored the ulong into the buffer. I then dereferenced a long pointer. This however is giving me a bad result?
I did :
#include <iostream>
using namespace std;
int main() {
unsigned char converter_buffer[4];//
unsigned long *pulong;
long *plong;
pulong = (unsigned long*)&converter_buffer;
plong = (long*)&converter_buffer;
unsigned long ulong_num = 65535; // this has a 1 as the first bit
*pulong = ulong_num;
std:: cout << "the number as a long is" << *plong << std::endl;
return 0;
}
For some reason this is giving me the same positive number.
Would casting help?
Actually using pointers was a good start but you have to cast your unsigned long* to void* first, then you can cast the result to long* and dereference it:
#include <iostream>
#include <climits>
int main() {
unsigned long ulongValue = ULONG_MAX;
long longValue = *((long*)((void*)&ulongValue));
std::cout << "ulongValue: " << ulongValue << std::endl;
std::cout << "longValue: " << longValue << std::endl;
return 0;
}
The code above will results the following:
ulongValue: 18446744073709551615
longValue: -1
With templates you can make it more readable in your code:
#include <iostream>
#include <climits>
template<typename T, typename U>
T unsafe_cast(const U& from) {
return *((T*)((void*)&from));
}
int main() {
unsigned long ulongValue = ULONG_MAX;
long longValue = unsafe_cast<long>(ulongValue);
std::cout << "ulongValue: " << ulongValue << std::endl;
std::cout << "longValue: " << longValue << std::endl;
return 0;
}
Keep in mind that this solution is absolutely unsafe due to the fact that you can cast anyithing to void*. This practicle was common in C but I do not recommend to use it in C++. Consider the following cases:
#include <iostream>
template<typename T, typename U>
T unsafe_cast(const U& from) {
return *((T*)((void*)&from));
}
int main() {
std::cout << std::hex << std::showbase;
float fValue = 3.14;
int iValue = unsafe_cast<int>(fValue); // OK, they have same size.
std::cout << "Hexadecimal representation of " << fValue
<< " is: " << iValue << std::endl;
std::cout << "Converting back to float results: "
<< unsafe_cast<float>(iValue) << std::endl;
double dValue = 3.1415926535;
int lossyValue = unsafe_cast<int>(dValue); // Bad, they have different size.
std::cout << "Lossy hexadecimal representation of " << dValue
<< " is: " << lossyValue << std::endl;
std::cout << "Converting back to double results: "
<< unsafe_cast<double>(lossyValue) << std::endl;
return 0;
}
The code above results for me the following:
Hexadecimal representation of 3.14 is: 0x4048f5c3
Converting back to float results: 3.14
Lossy hexadecimal representation of 3.14159 is: 0x54411744
Converting back to double results: 6.98387e-315
And for last line you can get anything because the conversion will read garbage from the memory.
Edit
As lorro commented bellow, using memcpy() is safer and can prevent the overflow. So, here is another version of type casting which is safer:
template<typename T, typename U>
T safer_cast(const U& from) {
T to;
memcpy(&to, &from, (sizeof(T) > sizeof(U) ? sizeof(U) : sizeof(T)));
return to;
}
You can do this:
uint32_t u;
int32_t& s = (int32_t&) u;
Then you can use s and u interchangeably with 2's complement, e.g.:
s = -1;
std::cout << u << '\n'; // 4294967295
In your question you ask about 65535 but that is a positive number. You could do:
uint16_t u;
int16_t& s = (int16_t&) u;
u = 65535;
std::cout << s << '\n'; // -1
Note that assigning 65535 (a positive number) to int16_t would implementation-defined behaviour, it does not necessarily give -1.
The problem with your original code is that it is not permitted to alias a char buffer as long. (And that you might overflow your buffer). However, it is OK to alias an integer type as its corresponding signed/unsigned type.
In general, when you have two arithmetic types that are the same size and you want to reinterpret the bit representation of one using the type of the other, you do it with a union:
#include <stdint.h>
union reinterpret_u64_d_union {
uint64_t u64;
double d;
};
double
reinterpret_u64_as_double(uint64_t v)
{
union reinterpret_u64_d_union u;
u.u64 = v;
return u.d;
}
For the special case of turning an unsigned number into a signed type with the same size (or vice versa), however, you can just use a traditional cast:
int64_t
reinterpret_u64_as_i64(uint64_t v)
{
return (int64_t)v;
}
(The cast is not strictly required for [u]int64_t, but if you don't explicitly write a cast, and the types you're converting between are small, the "integer promotions" may get involved, which is usually undesirable.)
The way you were trying to do it violates the pointer-aliasing rules and provokes undefined behavior.
In C++, note that reinterpret_cast<> does not do what the union does; it is the same as static_cast<> when applied to arithmetic types.
In C++, also note that the use of a union above relies on a rule in the 1999 C standard (with corrigienda) that has not been officially incorporated into the C++ standard last I checked; however, all compilers I am familiar with will do what you expect.
And finally, in both C and C++, long and unsigned long are guaranteed to be able to represent at least −2,147,483,647 ... 214,7483,647 and 0 ... 4,294,967,295, respectively. Your test program used 65535, which is guaranteed to be representable by both long and unsigned long, so the value would have been unchanged however you did it. Well, unless you used invalid pointer aliasing and the compiler decided to make demons fly out of your nose instead.

Assign negative of unsigned to a signed, is it OK?

When I run this:
int main() {
unsigned a = 5;
std::cout << -a << std::endl;
int b = -a;
std::cout << b << std::endl;
return 0;
}
I get this:
4294967291
-5
It seems like it works, and I can take the negative of an unsigned and assign it to an int, but is this really always OK? Why?
When I try something that to me looks like a similar situation:
int c = 1;
int d = 3;
double x = c/d;
std::cout << x << std::endl;
I get 0 (as expected).
PS: Maybe there is a dupe and I didnt find it, closest I could find is this
No. You have undefined behaviour possibilities.
Here is a counter-example that produces UB when assigning a negated unsigned int to an int:
unsigned u = (unsigned)std::numeric_limits<int>::max() - 1;
std::cout << "max int" << std::numeric_limits<int>::max() << '\n';
std::cout << "as unsigned - 1" << u << '\n';
std::cout << "negated:" << -u << '\n';
std::cout << std::boolalpha << ( std::numeric_limits<int>::max() < -u ) << '\n';
int s = -u;
std::cout << s << '\n';
On my machine:
int's max value is 2'147'483'647, but the negated unsigned int has a value of 2'147'483'650; that value is greater than the max value that can be represented by an int. Know that signed overflow is undefined behaviour. Thus, the algorithm is not safe for all of its possible values.
The Standard's (2016-07-12: N4604) word:
If during the evaluation of an expression, the result is not
mathematically defined or not in the range of representable values for
its type, the behavior is undefined. [ Note: Treatment of division by
zero, forming a remainder using a zero divisor, and all floating point
exceptions vary among machines, and is sometimes adjustable by a
library function. — end note ]
In the future, you can use the {}-style initialization to prevent such issues:
unsigned a = 5;
std::cout << -a << '\n';
int b{ -a }; // compiler detects narrowing conversions, warning/error
std::cout << b << '\n';
return 0;
Note that even though you know that -a will be a value that can be represented by an int, your compiler still warns you.
On signed overflow:
Is signed integer overflow still undefined behavior in C++?
On well defined unsigned overflow in both C and C++:
Why is unsigned integer overflow defined behavior but signed integer overflow isn't?
On implicit conversions:
http://en.cppreference.com/w/cpp/language/implicit_conversion
It is OK just as long as your target architecture is using two's compliment arithmetic and is treating int as 32 bits. Otherwise you'll get different results for your first program.

Storing the hex value FF in an unsigned 8 bit integer produces garbage instead of -1

Behold my code:
#include <iostream>
int main()
{
uint8_t no_value = 0xFF;
std::cout << "novalue: " << no_value << std::endl;
return 0;
}
Why does this output: novalue: ▒
On my terminal it looks like:
I was expecting -1.
After all, if we:
we get:
uint8_t is most likeley typedef-ed to unsigned char. When you pass this to the << operator, the overload for char is selected, which causes your 0xFF value to be interpreted as an ASCII character code, and displaying the "garbage".
If you really want to see -1, you should try this:
#include <iostream>
#include <stdint.h>
int main()
{
uint8_t no_value = 0xFF;
std::cout << "novalue (cast): " << (int)(int8_t)no_value << std::endl;
return 0;
}
Note that I first cast to int8_t, which causes your previously unsigned value to be instead interpretted as a signed value. This is where 255 becomes -1. Then, I cast to int, so that << understands it to mean "integer" instead of "character".
Your confusion comes from that fact that Windows calculator doesn't give you options for signed / unsigned -- it always considers values signed. So when you used an uint8_t, you made it unsigned.
Try this
#include <iostream>
int main()
{
uint8_t no_value = 0x41;
std::cout << "novalue: " << no_value << std::endl;
return 0;
}
You will get this output:
novalue: A
uint8_t probably the same thing as unsigned char.
std::cout with chars will output the char itself and not the char's ASCII value.

C++ : initialization, climits & variables

Disclaimer, new to programming, working my way through C++ Prime Plus 6th ed.
I'm working though listing 3.1.
#include <iostream>
#include <climits>
int main()
{
using namespace std;
int n_int = INT_MAX;
cout << "int is " << sizeof n_int << " bytes." << endl;
return 0;
}
So I get, that creates a variable sets the max int value.
However, is there any reason why I should not and can't go:
cout << "int is " << sizeof (INT_MAX) << " bytes." << endl;
As it gives the correct length. But when I try with (SHRT_MAX) it returns 4 bytes, when I'd hoped it would return 2.
Again with (LLONG_MAX) it returns correctly 8 bytes, however (LONG_MAX) incorrectly returns 8.
Any clarification would be great.
The values defined in <climits> are macros that expand to integer literals. The type of an integer literal is the smallest integer type that can hold the value, but no smaller than int.
So INT_MAX will have type int, and so sizeof INT_MAX is the same as sizeof (int). However, SHRT_MAX will also have type int, and so sizeof SHRT_MAX will not necessarily equal sizeof (short).