C++打印整数、浮点数的二进制

Tuesday, February 28, 2023
本文共1174字
3分钟阅读时长

⚠️本文是作者P3troL1er原创,首发于https://peterliuzhi.top/posts/c++%E6%89%93%E5%8D%B0%E6%95%B4%E6%95%B0%E6%B5%AE%E7%82%B9%E6%95%B0%E7%9A%84%E4%BA%8C%E8%BF%9B%E5%88%B6/。商业转载请联系作者获得授权,非商业转载请注明出处!

You give before you get. — Napoleon Hill

打印整数

打印整数很简单,只需要位运算就好了。

我使用了模板,这样就能针对不同长度的整数类型只用写一次代码:

template<typename T>
void _inner_print_bin(T num, char length){
    bool still_head = true;
    for (char i = length; i >= 0; i--)
    {
        char tmp = (num >> i) & 0x1;
        if ((still_head && tmp) || !still_head)
        {
            std::cout << (short)tmp ;
            still_head = false;
        }
    }
    std::cout << '\n';
}
template<typename T>
void printnum(T num){
    std::ios::sync_with_stdio(false);
    _inner_print_bin(num, sizeof(T)*8-1);
}

浮点数二进制打印

浮点数因为IEEE754的规定比较麻烦。

整个浮点数分为sign(符号位)、exponent(指数)、mantissa(尾数)三部分组成

文内图片

具体来说,最后小数的真实值 = $(-1)^{sign}*2^{exponent}*mantissa$,知道这个,我们只需要使用掩码就能分别得到sign、exponent和mantissa,然后进行一定的运算就能得到小数的二进制表示:

inline uint64_t mask_double_mantissa(uint64_t bits){
    return (bits & 0xFFFFFFFFFFFFF) | ((uint64_t)0x1 << 52);
};
inline int64_t mask_double_exponent(uint64_t bits){
    return ((int64_t)((bits >> 52) & 0x7FF)) - 1023;
}
inline char mask_double_sign(uint64_t bits){
    return (bits >> 63) & 1;
}

inline uint32_t mask_float_mantissa(uint32_t bits){
    return (bits & 0x7FFFFF) | (0x1 << 23);
};
inline int32_t mask_float_exponent(uint32_t bits){
    return ((int32_t)((bits >> 23) & 0xFF)) - 127;
}
inline char mask_float_sign(uint32_t bits){
    return (bits >> 31) & 1;
}

template<typename T1, typename T2>
void _inner_print_bin(T1 num, T2 exp, char length){
    bool still_head = true;
    char cnt = 0;
    for (char i = length; i >= 0; i--)
    {
        char tmp = (num >> i) & 0x1;
        if ((still_head && tmp) || !still_head)
        {
            std::cout << (short)tmp ;
            still_head = false;
            if (cnt == exp) std::cout << '.';
            ++cnt;
        }
    }
    std::cout << '\n';
}

void printnum(double num){
    std::ios::sync_with_stdio(false);
    uint64_t bits = *(uint64_t*)&num;
    char sign = mask_double_sign(bits);
    int64_t exp = mask_double_exponent(bits);
    uint64_t mantissa = mask_double_mantissa(bits);
    if (sign == 1){
        std::cout << '-';
    }
    if (exp > 0){
        _inner_print_bin(mantissa, exp, 63);
    }else{
        std::cout << "0.";
        for (unsigned char i = 0; i < (-exp) - 1; i++)
        {
            std::cout << '0';
        }
        _inner_print_bin(mantissa, 63);  
    }
}

void printnum(float num){
    std::ios::sync_with_stdio(false);
    uint32_t bits = *(uint32_t*)&num;
    char sign = mask_float_sign(bits);
    int32_t exp = mask_float_exponent(bits);
    uint32_t mantissa = mask_float_mantissa(bits);
    if (sign == 1){
        std::cout << '-';
    }
    if (exp > 0){
        _inner_print_bin(mantissa, exp, 31);
    }else{
        std::cout << "0.";
        for (unsigned char i = 0; i < (-exp) - 1; i++)
        {
            std::cout << '0';
        }
        _inner_print_bin(mantissa, 31); 
    }
}

注意,浮点数规定第一位必须是符号位,所以不能用unsigned修饰 虽然有些编译器会自动转换类型,有些编译器会抛出warning使得编译可以通过,但是这种写法是需要避免的

完整代码:

printnum.h:

/*
 * @Author       : PeterLiu-all peterliuforever@gmail.com
 * @Date         : 2023-02-28T16:45:56+0800
 * @LastEditTime : 2023-02-28T22:50:28+0800
 * @LastEditors  : PeterLiu-all peterliuforever@gmail.com
 * @FilePath     : \print_num\libprintnum\printnum.h
 * @Description  : libprintnum头文件
 */
#ifndef __LIBPRINTNUM_PRINTNUM_H__
#define __LIBPRINTNUM_PRINTNUM_H__

#include <stdint.h>
#include <iostream>

constexpr uint64_t mask_double_mantissa(uint64_t bits){
    return (bits & 0xFFFFFFFFFFFFF) | ((uint64_t)0x1 << 52);
};
constexpr int64_t mask_double_exponent(uint64_t bits){
    return ((int64_t)((bits >> 52) & 0x7FF)) - 1023;
}
constexpr char mask_double_sign(uint64_t bits){
    return (bits >> 63) & 1;
}

constexpr uint32_t mask_float_mantissa(uint32_t bits){
    return (bits & 0x7FFFFF) | (0x1 << 23);
};
constexpr int32_t mask_float_exponent(uint32_t bits){
    return ((int32_t)((bits >> 23) & 0xFF)) - 127;
}
constexpr char mask_float_sign(uint32_t bits){
    return (bits >> 31) & 1;
}

template<typename T>
void _inner_print_bin(T num, char length){
    bool still_head = true;
    for (char i = length; i >= 0; i--)
    {
        char tmp = (num >> i) & 0x1;
        if ((still_head && tmp) || !still_head)
        {
            std::cout << (short)tmp ;
            still_head = false;
        }
    }
    std::cout << '\n';
}

template<typename T1, typename T2>
void _inner_print_bin(T1 num, T2 exp, char length){
    bool still_head = true;
    char cnt = 0;
    for (char i = length; i >= 0; i--)
    {
        char tmp = (num >> i) & 0x1;
        if ((still_head && tmp) || !still_head)
        {
            std::cout << (short)tmp ;
            still_head = false;
            if (cnt == exp) std::cout << '.';
            ++cnt;
        }
    }
    std::cout << '\n';
}

template<typename T>
void printnum(T num){
    std::ios::sync_with_stdio(false);
    _inner_print_bin(num, sizeof(T)*8-1);
}

// 注意,浮点数规定第一位必须是符号位,所以不能用unsigned修饰
// 虽然有些编译器会自动转换类型,有些编译器会抛出warning使得编译可以通过,但是这种写法是需要避免的
void printnum(double num);
void printnum(float num);

#endif /* __LIBPRINTNUM_PRINTNUM_H__ */

printnum.c:

/*
 * @Author       : PeterLiu-all peterliuforever@gmail.com
 * @Date         : 2023-02-28T16:45:48+0800
 * @LastEditTime : 2023-02-28T21:53:52+0800
 * @LastEditors  : PeterLiu-all peterliuforever@gmail.com
 * @FilePath     : \print_num\libprintnum\printnum.cpp
 * @Description  : 模板全特化
 */
#include "printnum.h"

void printnum(double num){
    std::ios::sync_with_stdio(false);
    uint64_t bits = *(uint64_t*)&num;
    char sign = mask_double_sign(bits);
    int64_t exp = mask_double_exponent(bits);
    uint64_t mantissa = mask_double_mantissa(bits);
    if (sign == 1){
        std::cout << '-';
    }
    if (exp > 0){
        _inner_print_bin(mantissa, exp, 63);
    }else{
        std::cout << "0.";
        for (unsigned char i = 0; i < (-exp) - 1; i++)
        {
            std::cout << '0';
        }
        _inner_print_bin(mantissa, 63);  
    }
}

void printnum(float num){
    std::ios::sync_with_stdio(false);
    uint32_t bits = *(uint32_t*)&num;
    char sign = mask_float_sign(bits);
    int32_t exp = mask_float_exponent(bits);
    uint32_t mantissa = mask_float_mantissa(bits);
    if (sign == 1){
        std::cout << '-';
    }
    if (exp > 0){
        _inner_print_bin(mantissa, exp, 31);
    }else{
        std::cout << "0.";
        for (unsigned char i = 0; i < (-exp) - 1; i++)
        {
            std::cout << '0';
        }
        _inner_print_bin(mantissa, 31); 
    }
}