探索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

目录
相关文章
|
1月前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
21天前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
1月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
39 1
|
1月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
53 2
|
2月前
|
编译器 C++
【C++核心】函数的应用和提高详解
这篇文章详细讲解了C++函数的定义、调用、值传递、常见样式、声明、分文件编写以及函数提高的内容,包括函数默认参数、占位参数、重载等高级用法。
22 3
|
3月前
|
存储 算法 C++
C++ STL应用宝典:高效处理数据的艺术与实战技巧大揭秘!
【8月更文挑战第22天】C++ STL(标准模板库)是一组高效的数据结构与算法集合,极大提升编程效率与代码可读性。它包括容器、迭代器、算法等组件。例如,统计文本中单词频率可用`std::map`和`std::ifstream`实现;对数据排序及找极值则可通过`std::vector`结合`std::sort`、`std::min/max_element`完成;而快速查找字符串则适合使用`std::set`配合其内置的`find`方法。这些示例展示了STL的强大功能,有助于编写简洁高效的代码。
48 2
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
25 4