Using std::bitset for double representation - c++

In my application i'm trying to display the bit representation of double variables.
It works for smaller double variables. Not working for 10^30 level.
Code:
#include <iostream>
#include <bitset>
#include <limits>
#include <string.h>
using namespace std;
void Display(double doubleValue)
{
bitset<sizeof(double) * 8> b(doubleValue);
cout << "Value : " << doubleValue << endl;
cout << "BitSet : " << b.to_string() << endl;
}
int main()
{
Display(1000000000.0);
Display(2000000000.0);
Display(3000000000.0);
Display(1000000000000000000000000000000.0);
Display(2000000000000000000000000000000.0);
Display(3000000000000000000000000000000.0);
return 0;
}
Output:
/home/sujith% ./a.out
Value : 1e+09
BitSet : 0000000000000000000000000000000000111011100110101100101000000000
Value : 2e+09
BitSet : 0000000000000000000000000000000001110111001101011001010000000000
Value : 3e+09
BitSet : 0000000000000000000000000000000010110010110100000101111000000000
Value : 1e+30
BitSet : 0000000000000000000000000000000000000000000000000000000000000000
Value : 2e+30
BitSet : 0000000000000000000000000000000000000000000000000000000000000000
Value : 3e+30
BitSet : 0000000000000000000000000000000000000000000000000000000000000000
My worry is why bitset always gives 64, zero for later 3. Interestingly "cout" for the actual values works as expected.

If you look at the std::bitset constructor you will see that it either takes a string as argument, or an integer.
That means your double value will be converted to an integer, and there is no standard integer type that can hold such large values, and that leads to undefined behavior.
If you want to get the actual bits of the double you need to do some casting tricks to make it work:
unsigned long long bits = *reinterpret_cast<unsigned long long*>(&doubleValue);
Note that type-punning like this is not defined in the C++ specification, but as long as sizeof(double) == sizeof(unsigned long long) it will work. If you want the behavior to be well-defined you have to go through arrays of char and char*.

With C++14, std::bitset now takes an unsigned long long constructor, so this might work:
union udouble {
double d;
unsigned long long u;
};
void Display(double doubleValue)
{
udouble ud;
ud.d = doubleValue;
bitset<sizeof(double) * 8> b(ud.u);
cout << "Value : " << doubleValue << endl;
cout << "BitSet : " << b.to_string() << endl;
}
This should give you the internal representation of a double. See the working sample code on IdeOne.

Related

%X format specifier prints value only up to 4 bytes?

Hex value of 6378624653 is : 0x17C32168D
But this code prints : 0x7C32168D
#include<iostream>
int main()
{
int x = 6378624653;
printf("0x%x", x);
}
can anyone explain why this happens ? and what should I do to get the right output?
The obtained result means that an object of the type int can not store such a big value as 6378624653.
Try the following test program.
#include <iostream>
#include <limits>
int main()
{
std::cout << std::numeric_limits<int>::max() << '\n';
std::cout << 6378624653 << '\n';
std::cout << std::numeric_limits<unsigned int>::max() << '\n';
}
and see what the maximum value that can be stored in an object of the type int. In most cases using different compilers you will get the following output
2147483647
6378624653
4294967295
That is even objects of the type unsigned int can not store such value as 6378624653.
You should declare the variable x as having the type unsigned long long int.
Here is a demonstration program.
#include <cstdio>
int main()
{
unsigned long long int x = 6378624653;
printf( "%#llx\n", x );
}
The program output is
0x17c32168d

Float to 4 uint8_t and display

I read from float in Wikipedia and i tried print his bits.
i used std::bitset and this return other bits different from what I expected(i know because i used the same number of the example in the link), then i used memcpy() and copy the memory of float to 4 parts of 1 byte each and print, this method worked but i have 4 questions.
1) Why using bitset in a float, this print only the integer part?
2) Why bitset working only with the array and not with the float?
3) memcpy() worked in correct order?
The last question is because 0.15625f == 0b00111110001000000000000000000000.
Then i think that the correct order is:
bb[0] == 0b00111110;
bb[1] == 0b00100000;
bb[2] == 0b00000000;
bb[3] == 0b00000000;
But the order returned is inverse.
4) Why happend this ?
My code:
#include <cstring>
#include <iostream>
#include <bitset>
int main(int argc,char** argv){
float f = 0.15625f;
std::cout << std::bitset<32>(f) << std::endl;
//print: 00000000000000000000000000000000
//This print only integer part of the float. I tried with 5.2341 and others
uint8_t bb[4];
memcpy(bb, &f, 4);
std::cout << std::bitset<8>(bb[0]) << std::endl;
//print: 00000000
std::cout << std::bitset<8>(bb[1]) << std::endl;
//print: 00000000
std::cout << std::bitset<8>(bb[2]) << std::endl;
//print: 00100000
std::cout << std::bitset<8>(bb[3]) << std::endl;
//print: 00111110
return 0;
}
To construct std::bitset from a float, one of std::bitset construtors is used. The one that is relevant here is
constexpr bitset(unsigned long long val) noexcept;
Before this constructor is called, float is converted into unsigned long long, and its decimal part is truncated. std::bitset has no constructors that take floating-point values.
The bytes order of floating-point numbers is affected by machine endianness. On a little-endian machine bytes are stored in the reverse order. If your machine uses the same endianness for floating-point numbers and for integers, you can simply write
float f = 0.15625f;
std::uint32_t b;
std::memcpy(&b, &f, 4);
std::cout << std::bitset<32>(b) << std::endl;
// Output: 00111110001000000000000000000000
to get bytes in the correct order automatically.

Further understanding of unsigned integer overflow in C++14 [duplicate]

This question already has answers here:
uint8_t can't be printed with cout
(8 answers)
Closed 4 years ago.
Why does the following code fail for the case of uint8_t?
#include <iostream>
#include <cstdint>
#include <stack>
template <typename TT>
void PrintNumberScientificNotation (TT number) {
constexpr TT kBase{10}; // Base of the numerical system.
TT xx{number}; // Number to convert.
TT exponent{}; // Exponent.
std::stack<TT> fractional_part{}; // Values in the fractional part.
do {
fractional_part.push(xx%kBase);
xx /= kBase;
exponent++;
} while (xx > kBase);
std::cout << xx << '.';
while (!fractional_part.empty()) {
std::cout << fractional_part.top();
fractional_part.pop();
}
std::cout << " x 10^" << exponent << std::endl;
}
int main () {
uint8_t number_1{255};
PrintNumberScientificNotation(number_1); // Does not work.
uint16_t number_2{255};
PrintNumberScientificNotation(number_2); // Works.
uint16_t number_3{65'535};
PrintNumberScientificNotation(number_3); // Works.
uint32_t number_4{4'294'967'295};
PrintNumberScientificNotation(number_4); // Works.
uint64_t number_5{18'446'744'073'709'551'615};
PrintNumberScientificNotation(number_5); // Works.
}
Execute: http://cpp.sh/8c72o
Output:
. x 10^
2.55 x 10^2
6.5535 x 10^4
4.294967295 x 10^9
1.8446744073709551615 x 10^19
It is my understanding that uint8_t is able to represent unsigned integer numbers up to and including 255 (UINT8_MAX). Why can I represent the maximum values for all of the other representations?
The mathematical part of your code is fine, the printing is broken.
If you use cout for uint_t, it will interpret the uint_t as a character code. That's because uint_t is a type alias for unsigned char.
A possible fix is to explicitly convert to integer:
std::cout << unsigned(xx) << '.';
while (!fractional_part.empty()) {
std::cout << unsigned(fractional_part.top());
fractional_part.pop();
}
std::cout << " x 10^" << unsigned(exponent) << std::endl;
uint8_t is treated like a char thus it will print the character with the ASCII code. You have to convert the uint8_t value to unsigned before printing it as follow:
uint8_t number_1{255};
PrintNumberScientificNotation(unsigned(number_1));

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.

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.