I'm reading Computer Systems: A Programmer’s Perspective, then I found the Special Values
's definition and corresponding bit patterns.
Now, I wanna output their bits using C . I use their macro to output bits, obviously is incorrect, because macro defined to Integer!
#define FP_NAN 0x0100
#define FP_NORMAL 0x0400
#define FP_INFINITE (FP_NAN | FP_NORMAL)
What should I do to correctly output bits in the image above? and, Why compiler defined those Integer macros rather than IEEE standard?
below is my code.
#include <iostream>
#include <cmath>
#include <bitset>
using namespace std;
union U {
float f;
int i;
};
int main() {
U u1, u2;
u1.f = FP_NAN;
u2.f = FP_INFINITE;
cout << bitset<32>(u1.i) << endl;
cout << bitset<32>(u2.i) << endl;
return 0;
}
output:
01000011100000000000000000000000
01000100101000000000000000000000
My computer environment:
- win10
- mingw64
CodePudding user response:
I wrote a quick-and-dirty double
bit-wise output program a while back. You could modify it to work for float
.
It has ANSI escape sequences in it, which might not be suitable for your environment.
The key part is just using a byte memory pointer and examining the bit state directly, rather than trying to get std::bitset
to play nice.
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
using std::cout;
using std::fpclassify;
using std::memcpy;
using std::nan;
using std::numeric_limits;
using std::reverse;
using std::setw;
using std::size_t;
using std::string;
using std::stringstream;
using std::uint32_t;
using std::uint64_t;
namespace {
uint32_t low32_from(double d) {
char const* p = reinterpret_cast<char const*>(&d);
uint32_t result;
memcpy(&result, p, sizeof result);
return result;
}
uint32_t high32_from(double d) {
char const* p = reinterpret_cast<char const*>(&d);
p = 4;
uint32_t result;
memcpy(&result, p, sizeof result);
return result;
}
string hexstr(uint32_t value) {
char hex[] = "0123456789ABCDEF";
unsigned char buffer[4];
memcpy(buffer, &value, sizeof buffer);
auto p = &buffer[0];
stringstream ss;
char const* sep = "";
for (size_t i = 0; i < sizeof buffer; i) {
ss << sep << hex[(*p >> 4) & 0xF] << hex[*p & 0xF];
sep = " ";
p;
}
return ss.str();
}
string bits(uint64_t v, size_t len) {
string s;
int group = 0;
while (len--) {
if (group == 4) { s.push_back('\''); group = 0; }
s.push_back(v & 1 ? '1' : '0');
v >>= 1;
group;
}
reverse(s.begin(), s.end());
return s;
}
string doublebits(double d) {
auto dx = fpclassify(d);
unsigned char buffer[8];
memcpy(buffer, &d, sizeof buffer);
stringstream ss;
uint64_t s = (buffer[7] >> 7) & 0x1;
uint64_t e = ((buffer[7] & 0x7FU) << 4) | ((buffer[6] >> 4) & 0xFU);
uint64_t f = buffer[6] & 0xFU;
f = (f << 8) (buffer[5] & 0xFFU);
f = (f << 8) (buffer[4] & 0xFFU);
f = (f << 8) (buffer[3] & 0xFFU);
f = (f << 8) (buffer[2] & 0xFFU);
f = (f << 8) (buffer[1] & 0xFFU);
f = (f << 8) (buffer[0] & 0xFFU);
ss << "sign:\033[0;32m" << bits(s, 1) << "\033[0m ";
if (s) ss << "(-) ";
else ss << "( ) ";
ss << "exp:\033[0;33m" << bits(e, 11) << "\033[0m ";
ss << "(" << setw(5) << (static_cast<int>(e) - 1023) << ") ";
ss << "frac:";
// 'i' for implied 1 bit, '.' for not applicable (so things align correctly).
if (dx == FP_NORMAL) ss << "\033[0;34mi";
else ss << "\033[0;37m.\033[34m";
ss << bits(f, 52) << "\033[0m";
if (dx == FP_INFINITE) ss << " \033[35mInfinite\033[0m";
else if (dx == FP_NAN) ss << " \033[35mNot-A-Number\033[0m";
else if (dx == FP_NORMAL) ss << " \033[35mNormal\033[0m";
else if (dx == FP_SUBNORMAL) ss << " \033[35mDenormalized\033[0m";
else if (dx == FP_ZERO) ss << " \033[35mZero\033[0m";
ss << " " << d;
return ss.str();
}
} // anon
int main() {
auto lo = low32_from(1111.2222);
auto hi = high32_from(1111.2222);
cout << hexstr(lo) << "\n";
cout << hexstr(hi) << "\n";
cout << doublebits(1111.2222) << "\n";
cout << doublebits(1.0) << "\n";
cout << doublebits(-1.0) << "\n";
cout << doublebits( 0.0) << "\n";
cout << doublebits(-0.0) << "\n";
cout << doublebits(numeric_limits<double>::infinity()) << "\n";
cout << doublebits(-numeric_limits<double>::infinity()) << "\n";
cout << doublebits(nan("")) << "\n";
double x = 1.0;
while (x > 0.0) {
cout << doublebits(x) << "\n";
x = x / 2.0;
}
}
CodePudding user response:
There are multiple problems with your code.
Problem #1:
FP_NAN and FP_INFINITE are not constants representing return values of std::fpclassify
, which returns classification of given floating point number.
Problem 2:
Accessing inactive union member, i.e. not the latest assigned to, is UB. Most robust, well-known way to inspect memory representation of an object is to memcpy
it into char buffer.
Taking it into account, you can write your code in following way:
#include <bitset>
#include <cmath> // nanf
#include <cstring> // memcpy
#include <iostream>
#include <limits>
#include <ranges>
template <typename T> // Template, because reusability
void print_bits(const T& t)
{
char buffer[sizeof(T)];
std::memcpy(buffer, &t, sizeof(T));
for (char c: buffer | std::views::reverse) //Endianness
{
std::cout << std::bitset<8>(c);
}
}
int main()
{
const double nan = std::nanf("");
const double inf = std::numeric_limits<float>::infinity();
print_bits(nan);
std::cout << '\n';
print_bits(inf);
std::cout << '\n';
}
0111111111111000000000000000000000000000000000000000000000000000 0111111111110000000000000000000000000000000000000000000000000000