探索C/C++ 进制转换之美:从原理到应用(二)

简介: 探索C/C++ 进制转换之美:从原理到应用

探索C/C++ 进制转换之美:从原理到应用(一)https://developer.aliyun.com/article/1464271


3.5 小数点进制转换

对于小数部分,我们可以使用类似的方法进行进制转换。这里我们以二进制和十进制之间的互相转换为例:

小数:二进制转十进制

例如,将二进制小数 0.1101 转换为十进制小数:

按权展开:(1 × 2^-1) + (1 × 2^-2) + (0 × 2^-3) + (1 × 2^-4) = 0.5 + 0.25 + 0 + 0.0625 = 0.8125。

小数:十进制转二进制

例如,将十进制小数 0.8125 转换为二进制小数:

执行连续乘法,将乘法结果的整数部分一次提取出来,直到其结果为 0 或达到所需的精度:

0.8125 × 2 = 1.625 ...提取 1
0.6250 × 2 = 1.250 ...提取 1
0.2500 × 2 = 0.500 ...提取 0
0.5000 × 2 = 1.000 ...提取 1

将提取的整数部分依次排列:1101;从而得到二进制小数 0.1101。

对于小数部分的八进制、十六进制与其他进制之间的转换,与上述过程类似。可以先将小数转换为二进制形式,然后再将其转换为目标进制。在实际应用中,注意控制所需的精度以避免无限循环小数的出现。

3.6 进制转换综述

下表总结了二进制、八进制、十进制和十六进制之间的数学转换方法与代码转换方法。

转换类型 数学方法 代码方法(C++)
二进制 -> 十进制 按权展开求和 使用 std::bitset 将二进制字符串转换为 unsigned long
十进制 -> 二进制 连续除法求余 使用 std::bitset 将整数转换为二进制字符串
八进制 -> 十进制 按权展开求和 使用 std::istringstream 将八进制字符串转换为整数
十进制 -> 八进制 连续除法求余 使用 std::stringstream 将整数转换为八进制字符串
十六进制 -> 十进制 按权展开求和 使用 std::istringstream 将十六进制字符串转换为整数
十进制 -> 十六进制 连续除法求余 使用 std::stringstream 将整数转换为十六进制字符串
符号数二进制 -> 十进制 补码形式的逆过程求和 使用 std::bitset 将补码形式的二进制字符串转换为整数
符号数十进制 -> 二进制 按补码形式转换 使用 std::bitset 将整数转换为补码形式的二进制字符串
小数二进制 -> 十进制 按权展开求和 使用类似按权展开的自定义实现
小数十进制 -> 二进制 连续乘法求整数部分 使用类似连续乘法的自定义实现

扩展内容:

  1. 符号数进制转换:处理负数时需要使用补码表示法。当转换负数的进制时,需先计算绝对值的目标进制表示,然后将其转换成补码形式。
  2. 小数的进制转换:对于小数部分,使用按权展开求和的方法(二进制 -> 十进制)以及连续乘法求整数部分的方法(十进制 -> 二进制)进行转换。对于小数部分的八进制、十六进制与其他进制之间的转换,可以先将小数转换为二进制形式,然后再将其转换为目标进制。注意控制所需的精度以避免无限循环小数的出现。

3.7 BaseConverter 类的实现

以下是一个 BaseConverter 类的实现,它具有所需的功能。

首先,定义一个结构体 NumberRepresentation 以存储各种进制的数值表示:

#include <string>
#include <iostream>
#include <sstream>
#include <bitset>
struct NumberRepresentation {
    std::string binary;
    std::string octal;
    std::string decimal;
    std::string hexadecimal;
};

然后,创建一个进制转换类 BaseConverter,实现所需的功能:

class BaseConverter {
public:
    NumberRepresentation convert(int number) {
        NumberRepresentation num_repr;
        num_repr.binary = int_to_binary(number);
        num_repr.octal = int_to_octal(number);
        num_repr.decimal = std::to_string(number);
        num_repr.hexadecimal = int_to_hex(number);
        return num_repr;
    }
    std::string hex_array_to_string(const uint8_t *hex_array, size_t size) {
        std::stringstream ss;
        ss << std::hex;
        for (size_t i = 0; i < size; ++i) {
            ss << static_cast<int>(hex_array[i]);
        }
        return ss.str();
    }
    // 提供数学转换接口,不使用C++ API
    int binary_to_decimal_math(const std::string& binary) {
        int decimal = 0;
        for (size_t i = 0; i < binary.length(); ++i) {
            decimal = decimal * 2 + (binary[i] - '0');
        }
        return decimal;
    }
    std::string decimal_to_binary_math(int decimal) {
        std::string binary;
        while (decimal > 0) {
            binary = std::to_string(decimal % 2) + binary;
            decimal /= 2;
        }
        return binary;
    }
    int octal_to_decimal_math(const std::string& octal) {
        int decimal = 0;
        for (size_t i = 0; i < octal.length(); ++i) {
            decimal = decimal * 8 + (octal[i] - '0');
        }
        return decimal;
    }
    std::string decimal_to_octal_math(int decimal) {
        std::string octal;
        while (decimal > 0) {
            octal = std::to_string(decimal % 8) + octal;
            decimal /= 8;
        }
        return octal;
    }
    int hexadecimal_to_decimal_math(const std::string& hex) {
        int decimal = 0;
        for (size_t i = 0; i < hex.length(); ++i) {
            char digit = hex[i];
            int value;
            if ('0' <= digit && digit <= '9') {
                value = digit - '0';
            } else if ('A' <= digit && digit <= 'F') {
                value = digit - 'A' + 10;
            } else if ('a' <= digit && digit <= 'f') {
                value = digit - 'a' + 10;
            } else {
                throw std::invalid_argument("Invalid hexadecimal character");
            }
            decimal = decimal * 16 + value;
        }
        return decimal;
    }
    std::string decimal_to_hexadecimal_math(int decimal) {
        const char* hex_digits = "0123456789ABCDEF";
        std::string hex;
        while (decimal > 0) {
            hex = hex_digits[decimal % 16] + hex;
            decimal /= 16;
        }
        return hex;
    }
    double binary_fraction_to_decimal_math(const std::string& binary_fraction) {
        double decimal = 0;
        double mult = 0.5;
        for (char ch : binary_fraction) {
            decimal += (ch - '0') * mult;
            mult *= 0.5;
        }
        return decimal;
    }
    std::string decimal_fraction_to_binary_math(double decimal_fraction, int precision) {
        std::string binary_fraction;
        while (precision > 0 && decimal_fraction > 0) {
            decimal_fraction *= 2;
            binary_fraction += (decimal_fraction >= 1) ? '1' : '0';
            if (decimal_fraction >= 1) {
                decimal_fraction -= 1;
            }
            --precision;
        }
        return binary_fraction;
    }
private:
    std::string int_to_binary(int number) {
        std::bitset<32> binary_bitset(number);
        std::string binary_str = binary_bitset.to_string();
        size_t non_zero_pos = binary_str.find_first_not_of('0');
        return binary_str.substr(non_zero_pos);
    }
    std::string int_to_octal(int number) {
        std::stringstream ss;
        ss << std::oct << number;
        return ss.str();
    }
    std::string int_to_hex(int number) {
        std::stringstream ss;
        ss << std::hex << number;
        return ss.str();
    }
};

现在,您可以使用这个类进行进制转换和其他相关任务。例如:

int main() {
    BaseConverter converter;
    int input_number = 42;
    NumberRepresentation num_repr = converter.convert(input_number);
    std::cout << "Binary: " << num_repr.binary << std::endl;
    std::cout << "Octal: " << num_repr.octal << std::endl;
    std::cout << "Decimal: " << num_repr.decimal << std::endl;
    std::cout << "Hexadecimal: " << num_repr.hexadecimal << std::endl;
    uint8_t hex_array[] = {0x74, 0x65, 0x73, 0x74};
    std::string hex_str = converter.hex_array_to_string(hex_array, sizeof(hex_array) / sizeof(uint8_t));
    std::cout << "Hexadecimal string: " << hex_str << std::endl;
    return 0;
}

在这个例子中,我们定义了一个结构体 NumberRepresentation 和一个进制转换类 BaseConverter,类中实现了所需的功能。类的方法可以为整数生成二进制、八进制、十进制和十六进制表示,将十六进制数组转换为字符串,以及提供数学转换接口(这些接口需要自行根据前述数学方法来实现)。

四、C/C++ 高级进制转换应用 (Advanced Base Conversion Applications in C/C++)

4.1 不同进制表示法的读写 (Reading and Writing in Different Base Representations)

在实际应用中,我们可能需要将不同进制的数值表示从文件、网络传输或其他源中读入,并将其转换为整数或浮点数以进行进一步处理。同样地,我们也可能需要将整数或浮点数转换为其他进制的形式,以便进行输出或传输。

读取

从文件或其他源中读取不同进制的表示时,需要了解该表示的格式。例如,二进制前面通常有 “0b” 或 “0B” 前缀,八进制有 “0” 前缀,十六进制有 “0x” 或 “0X” 前缀。读取过程中需忽略这些前缀,并将后续的字符读入字符串或字符数组进行进一步处理。

写入

写入不同进制表示时,通常需要为输出的字符串添加相应的前缀。添加前缀有助于在后续读取时识别不同的数值表示和进制。

4.2 使用位操作符进行进制转换 (Using Bitwise Operators for Base Conversion)

4.2.1 位操作符与进制转换 (Bitwise Operators and Base Conversion)

位操作符允许我们直接操作整数的二进制位。这可以让我们高效地实现二进制与其他进制之间的转换。位操作符包括:按位与(&),按位或(|),按位异或(^),按位非(~),左移(<<),右移(>>)等。

在进制转换过程中,位操作符可以帮助我们直接访问和修改数值的二进制位,从而简化计算过程。例如,对于加法和乘法等基本运算,可以通过按位操作实现而无需真正执行加法和乘法。同时,在需要高效执行的场景中(如网络编程、加密算法等),位操作符在性能上的优势使得它成为实现进制转换等操作的首选。

在实际编程中,结合具体问题和项目需求,我们可以灵活选择是否使用位操作符。选择使用位操作符时需要考虑到代码的可读性和性能。在一些情况下,使用位操作符可以提高代码性能,但可能会降低代码可读性。在这种情况下,我们需要在性能与可读性之间进行权衡。

4.2.2 用位操作符实现的进制转换算法 (Base Conversion Algorithm using Bitwise Operators)

使用位操作符实现的进制转换算法与前述的除法和乘法运算相似。例如,将一个十进制整数转换为二进制字符串时,可以使用右移操作符逐位读取整数的二进制表示:

std::string decimal_to_binary_bitwise(int decimal) {
    std::string binary;
    for (int i = 31; i >= 0; --i) {
        binary += ((decimal >> i) & 1) ? '1' : '0';
    }
    size_t non_zero_pos = binary.find_first_not_of('0');
    return binary.substr(non_zero_pos);
}

该函数首先创建一个空的二进制字符串。然后,从最高位(第31位)开始,逐位进行以下操作:使用右移操作符将当前位移动到二进制表示的最低位(第0位),然后用按位与操作符 & 和 1 进行按位与运算。这样可以得到当前位的值(0 或 1)。将这个值转换为字符并添加到二进制字符串中。

最后,使用 find_first_not_of 函数找到第一个不是 ‘0’ 的字符的位置,从该位置开始截取子字符串,以去除前导零。这样,我们使用位操作符实现了十进制整数到二进制字符串的转换。在实际应用中,还可以类似地使用位操作符实现其他进制间的转换。

4.2.3 实际应用案例 (Practical Application Cases)

在某些应用场景中,例如网络协议或加密技术等,位操作符特别适用。使用位操作符进行进制转换可以实现高效的算法,提高程序的性能。

案例1:网络协议

许多网络协议(如 IPv4、IPv6 或 TCP/IP 等)在传输数据时需要操作二进制位。在这些场景中,位操作符可以实现高效的算法,达到更好的性能。

例如,IPv4 地址通常表示为 4 个十进制整数(每个范围在 0-255 之间),用英文句点分隔。但在实际中,IPv4 地址是 32 位的二进制整数。使用位操作符可以方便地在二进制和点分十进制之间进行转换。例如,IP 地址子网掩码的计算就需要用到位操作符。

案例2:加密技术

在加密技术中,密钥和明文往往需要进行位级别的操作以产生密文。例如,DES(Data Encryption Standard)加密算法中,在密钥生成和加密过程中大量使用了位操作符。进行置换、选择、异或等操作时,位操作符可以简化实现过程,提高计算速度。

通过这些实际应用案例,可以看到位操作符在进制转换以及其他处理二进制位的场景中具有明显的性能优势。在性能敏感的场合,使用位操作符进行进制转换和其他二进制位操作是一个理想的选择。同时,我们需要权衡代码的可读性和性能,因此在不同的场景下,我们可以根据需要灵活地选择使用位操作符或其他方法。

4.3 利用C++ 标准库中的进制转换功能 (Using Base Conversion Functions in C++ Standard Library)

C++ 标准库提供了一些实用功能,可帮助我们进行进制转换。例如,可以使用 处理二进制字符串, 实现进制间的转换等。比如以下示例:

例1: 使用 bitset 处理二进制字符串

#include <bitset>
std::string int_to_binary(int integer) {
    std::bitset<32> binary_bitset(integer);
    return binary_bitset.to_string();
}

在这个例子中,我们将整数转换为一个包含 32 位的 bitset。接着,我们使用 bitsetto_string() 函数将其转换为二进制字符串。需要注意的是,bitset::to_string() 生成的字符串可以包含前导零,根据需求可以选择进一步处理结果。

例2: 使用 stringstream 实现进制间的转换

#include <sstream>
#include <iomanip>
int hex_to_decimal(const std::string& hexadecimal) {
    int decimal;
    std::stringstream ss(hexadecimal);
    ss >> std::hex >> decimal;
    return decimal;
}

在此示例中,我们将十六进制字符串转换为十进制整数。首先,我们创建一个 stringstream 对象并将十六进制字符串输入其中。然后使用 std::hex 修饰符指定我们将从 stringstream 读取十六进制整数。最后,通过将 stringstream 对象转换为整数,实现进制间的转换。

在实际项目中,可以根据性能需求和代码可读性来决定是使用位操作符实现进制转换,还是使用 C++ 标准库中提供的功能。在很多情况下,使用 C++ 标准库的功能具有良好的代码可读性,且性能足够满足需求。

五、进制转换相关面试题

本章将提供一些针对进制转换的面试题。这些题目主要分为以下几个部分:

5.1 基本进制转换题目

这部分面试题主要测试对进制转换基本概念的理解和基本编程能力。

面试题1:将给定的十进制整数转换为二进制字符串

问题描述:给定一个非负整数 n,要求将其转换为二进制字符串。

要求:

  • 写一个函数 string decimal_to_binary(int n),输入为非负整数 n,输出为二进制字符串。

示例:

输入: 2
输出: "10"
输入: 10
输出: "1010"

面试题2:将给定的十进制整数转换为十六进制字符串

问题描述:给定一个整数 n,要求将其转换为十六进制字符串。注意:负整数应当用补码表示。

要求:

  • 写一个函数 string decimal_to_hex(int n),输入为整数 n,输出为十六进制字符串。

示例:

输入: 26
输出: "1A"
输入: -1
输出: "FFFFFFFF"

面试题3:将给定的二进制字符串转换为十进制整数

问题描述:给定一个二进制字符串 s,要求将其转换为十进制整数。

要求:

  • 写一个函数 int binary_to_decimal(const string &s),输入为二进制字符串 s,输出为十进制整数。

示例:

输入: "11010"
输出: 26
输入: "1010"
输出: 10


探索C/C++ 进制转换之美:从原理到应用(三)https://developer.aliyun.com/article/1464273

目录
相关文章
|
4天前
|
存储 负载均衡 算法
基于 C++ 语言的迪杰斯特拉算法在局域网计算机管理中的应用剖析
在局域网计算机管理中,迪杰斯特拉算法用于优化网络路径、分配资源和定位故障节点,确保高效稳定的网络环境。该算法通过计算最短路径,提升数据传输速率与稳定性,实现负载均衡并快速排除故障。C++代码示例展示了其在网络模拟中的应用,为企业信息化建设提供有力支持。
35 15
|
22天前
|
算法 Serverless 数据处理
从集思录可转债数据探秘:Python与C++实现的移动平均算法应用
本文探讨了如何利用移动平均算法分析集思录提供的可转债数据,帮助投资者把握价格趋势。通过Python和C++两种编程语言实现简单移动平均(SMA),展示了数据处理的具体方法。Python代码借助`pandas`库轻松计算5日SMA,而C++代码则通过高效的数据处理展示了SMA的计算过程。集思录平台提供了详尽且及时的可转债数据,助力投资者结合算法与社区讨论,做出更明智的投资决策。掌握这些工具和技术,有助于在复杂多变的金融市场中挖掘更多价值。
47 12
|
20天前
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
102 0
|
2月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
63 5
|
5月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
101 5
|
5月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
70 1
|
5月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
115 2
|
22天前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
4天前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
30 6
|
25天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)