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

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.

Related

Why in C++ do static_cast<unsigned> of negative numbers differ if the number is constant or not

What's the C++ rules that means equal is false?. Given:
float f {-1.0};
bool equal = (static_cast<unsigned>(f) == static_cast<unsigned>(-1.0));
E.g. https://godbolt.org/z/fcmx2P
#include <iostream>
int main()
{
float f {-1.0};
const float cf {-1.0};
std::cout << std::hex;
std::cout << " f" << "=" << static_cast<unsigned>(f) << '\n';
std::cout << "cf" << "=" << static_cast<unsigned>(cf) << '\n';
return 0;
}
Produces the following output:
f=ffffffff
cf=0
The behaviour of your program is undefined: the C++ standard does not define the conversion of a negative floating point type to an unsigned type.
(Note the familiar wrap-around behaviour only applies to negative integral types.)
So therefore there's little point in attempting to explain your program output.

Output of strtoull() loses precision when converted to double and then back to uint64_t

Consider the following:
#include <iostream>
#include <cstdint>
int main() {
std::cout << std::hex
<< "0x" << std::strtoull("0xFFFFFFFFFFFFFFFF",0,16) << std::endl
<< "0x" << uint64_t(double(std::strtoull("0xFFFFFFFFFFFFFFFF",0,16))) << std::endl
<< "0x" << uint64_t(double(uint64_t(0xFFFFFFFFFFFFFFFF))) << std::endl;
return 0;
}
Which prints:
0xffffffffffffffff
0x0
0xffffffffffffffff
The first number is just the result of converting ULLONG_MAX, from a string to a uint64_t, which works as expected.
However, if I cast the result to double and then back to uint64_t, then it prints 0, the second number.
Normally, I would attribute this to the precision inaccuracy of floats, but what further puzzles me, is that if I cast the ULLONG_MAX from uint64_t to double and then back to uint64_t, the result is correct (third number).
Why the discrepancy between the second and the third result?
EDIT (by #Radoslaw Cybulski)
For another what-is-going-on-here try this code:
#include <iostream>
#include <cstdint>
using namespace std;
int main() {
uint64_t z1 = std::strtoull("0xFFFFFFFFFFFFFFFF",0,16);
uint64_t z2 = 0xFFFFFFFFFFFFFFFFull;
std::cout << z1 << " " << uint64_t(double(z1)) << "\n";
std::cout << z2 << " " << uint64_t(double(z2)) << "\n";
return 0;
}
which happily prints:
18446744073709551615 0
18446744073709551615 18446744073709551615
The number that is closest to 0xFFFFFFFFFFFFFFFF, and is representable by double (assuming 64 bit IEEE) is 18446744073709551616. You'll find that this is a bigger number than 0xFFFFFFFFFFFFFFFF. As such, the number is outside the representable range of uint64_t.
Of the conversion back to integer, the standard says (quoting latest draft):
[conv.fpint]
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.
Why the discrepancy between the second and the third result?
Because the behaviour of the program is undefined.
Although it is mostly pointless to analyse reasons for differences in UB because the scope of variation is limitless, my guess at the reason for the discrepancy in this case is that in one case the value is compile time constant, while in the other there is a call to a library function that is invoked at runtime.

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.

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

#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.

Function Returning Negative Value

I still have not run it through enough tests however for some reason, using certain non-negative values, this function will sometimes pass back a negative value. I have done a lot of manual testing in calculator with different values but I have yet to have it display this same behavior.
I was wondering if someone would take a look at see if I am missing something.
float calcPop(int popRand1, int popRand2, int popRand3, float pERand, float pSRand)
{
return ((((((23000 * popRand1) * popRand2) * pERand) * pSRand) * popRand3) / 8);
}
The variables are all contain randomly generated values:
popRand1: between 1 and 30
popRand2: between 10 and 30
popRand3: between 50 and 100
pSRand: between 1 and 1000
pERand: between 1.0f and 5500.0f which is then multiplied by 0.001f before being passed to the function above
Edit:
Alright so after following the execution a bit more closely it is not the fault of this function directly. It produces an infinitely positive float which then flips negative when I use this code later on:
pPMax = (int)pPStore;
pPStore is a float that holds popCalc's return.
So the question now is, how do I stop the formula from doing this? Testing even with very high values in Calculator has never displayed this behavior. Is there something in how the compiler processes the order of operations that is causing this or are my values simply just going too high?
In this case it seems that when you are converting back to an int after the function returns it is possible that you reach the maximum value of an int, my suggestion is for you to use a type that can represent a greater range of values.
#include <iostream>
#include <limits>
#include <boost/multiprecision/cpp_int.hpp>
int main(int argc, char* argv[])
{
std::cout << "int min: " << std::numeric_limits<int>::min() << std::endl;
std::cout << "int max: " << std::numeric_limits<int>::max() << std::endl;
std::cout << "long min: " << std::numeric_limits<long>::min() << std::endl;
std::cout << "long max: " << std::numeric_limits<long>::max() << std::endl;
std::cout << "long long min: " << std::numeric_limits<long long>::min() << std::endl;
std::cout << "long long max: " << std::numeric_limits<long long>::max() << std::endl;
boost::multiprecision::cpp_int bigint = 113850000000;
int smallint = 113850000000;
std::cout << bigint << std::endl;
std::cout << smallint << std::endl;
std::cin.get();
return 0;
}
As you can see here, there are other types which have a bigger range. If these do not suffice I believe the latest boost version has just the thing for you.
Throw an exception:
if (pPStore > static_cast<float>(INT_MAX)) {
throw std::overflow_error("exceeds integer size");
} else {
pPMax = static_cast<int>(pPStore);
}
or use float instead of int.
When you multiply the maximum values of each term together you get a value around 1.42312e+12 which is somewhat larger than a 32 bit integer can hold, so let's see what the standard has to say about floating point-to-integer conversions, in 4.9/1:
A prvalue of a floating point type can be converted to a prvalue of an
integer type. The conversion trun- cates; that is, the fractional part
is discarded. The behavior is undefined if the truncated value cannot
be represented in the destination type.
So we learn that for a large segment of possible result values your function can generate, the conversion back to a 32 bit integer would be undefined, which includes making negative numbers.
You have a few options here. You could use a 64 bit integer type (long or long long possibly) to hold the value instead of truncating down to int.
Alternately you could scale down the results of your function by a factor of around 1000 or so, to keep the maximal results within the range of values that a 32 bit integer could hold.