FloatDouble与定点数

C++中整数表示

在 C++ 中,整数的表示原理主要涉及计算机如何在内存中存储和处理整数数据。以下是简洁而全面的解释,涵盖整数表示的核心原理:

1. 整数的二进制表示

C++ 中的整数类型(如 intshortlonglong long 等)在内存中以 二进制形式 存储。计算机使用固定数量的位(bit)来表示一个整数,位数由整数类型决定,例如:

每个位可以是 0 或 1,整数的值通过这些位的组合来表示。

2. 有符号整数与无符号整数

C++ 支持 有符号(signed)和 无符号(unsigned)整数类型,它们的表示方式不同:

2n1 2^n - 1

,其中 (n) 是位数。例如,32 位无符号整数的范围是 0 到

2321 2^{32} - 1

(即 0 到 4,294,967,295)。

3. 有符号整数的编码方式

有符号整数通常采用 二进制补码(Two’s Complement)表示法,这是 C++ 标准中常用的编码方式。补码的优点是简化了加减法运算。补码表示法的特点如下:

2n1 -2^{n-1}

2n11 2^{n-1} - 1

。例如,32 位 int 的范围是 (-2,147,483,648) 到 (2,147,483,647)。

4. 内存存储与字节序

整数在内存中以 字节(8 位)为单位存储,多字节整数的存储方式涉及 字节序(Endianness):

现代 x86 和 x86-64 架构通常使用小端序,而某些嵌入式系统可能使用大端序。C++ 程序员通常无需直接处理字节序,除非涉及跨平台数据传输。

5. 整数溢出

2321 2^{32} - 1

,加 1 后变为 0。

6. C++ 中的整数类型

C++ 提供了多种整数类型,位数和范围因编译器和平台而异:

7. 示例代码

以下代码展示如何查看整数的二进制表示:

#include <iostream>
#include <bitset>
int main() {
    int x = 5;
    int y = -5;
    std::cout << "5 in binary: " << std::bitset<32>(x) << std::endl;
    std::cout << "-5 in binary: " << std::bitset<32>(y) << std::endl;
    return 0;
}

输出(假设 32 位 int):

5 in binary: 00000000000000000000000000000101
-5 in binary: 11111111111111111111111111111011

8. 注意事项

C++中float表示原理

在 C++ 中,float 是一种浮点数类型,用于表示带有小数部分的实数。其表示原理基于 IEEE 754 标准(通常为单精度浮点数)。以下是简洁而全面的解释,涵盖 float 的表示原理:

1. float 的内存结构

C++ 中的 float 通常占用 32 位(4 字节),按照 IEEE 754 单精度浮点数标准分为三个部分:

内存布局如下:

| 符号位 (1 位) | 指数 (8 位) | 尾数 (23 位) |

2. 浮点数表示公式

float 的值通过以下公式计算:

(1)符号位×2指数偏移×(1.尾数) (-1)^{\text{符号位}} \times 2^{\text{指数} - \text{偏移}} \times (1.\text{尾数})

例如,十进制数 6.5 的二进制表示为: - 二进制:110.1(即

1.101×22 1.101 \times 2^2

)。 - 符号位:0(正数)。 - 指数:实际指数 2,存储值为 (2 + 127 = 129),二进制为 10000001。 - 尾数:101 补齐为 23 位,101000...0。 - 最终 32 位表示:0 10000001 10100000000000000000000

3. 特殊值

IEEE 754 标准定义了一些特殊值:

0/0 0/0

1 \sqrt{-1}

4. 范围与精度

1.18×1038 1.18 \times 10^{-38}

3.4×1038 3.4 \times 10^{38}

5. 字节序

与整数类似,float 的多字节存储涉及 字节序(Endianness): - 大端序:高位字节存储在低地址。 - 小端序:低位字节存储在低地址(x86 架构常用)。 字节序通常对程序员透明,但在跨平台数据传输时需注意。

6. 浮点数运算的问题

7. 示例代码

以下代码展示如何查看 float 的二进制表示:

#include <iostream>
#include <bitset>
int main() {
    float f = 6.5f;
    unsigned int* ptr = reinterpret_cast<unsigned int*>(&f);
    std::cout << "6.5 in binary: " << std::bitset<32>(*ptr) << std::endl;
    return 0;
}

输出(假设小端序,32 位):

6.5 in binary: 01000000110100000000000000000000

解释:

8. 注意事项

C++中double表示原理

在 C++ 中,doublefloat 都是浮点数类型,均基于 IEEE 754 标准 表示浮点数,因此在表示原理上有许多相同之处,但也有关键的区别,主要体现在存储大小、精度和范围上。以下是简洁而全面的对比,分析 doublefloat 的表示原理的 相同之处不同之处

相同之处

  1. 基于 IEEE 754 标准

(1)符号位×2指数偏移×(1.尾数) (-1)^{\text{符号位}} \times 2^{\text{指数} - \text{偏移}} \times (1.\text{尾数})

(对于规格化数)。

  1. 内存结构

  2. 特殊值表示

  3. 字节序

  4. 浮点运算特性

不同之处

特性 float double
存储大小 32 位(4 字节) 64 位(8 字节)
符号位 1 位 1 位
指数位数 8 位 11 位
尾数位数 23 位 52 位
指数偏移 127 1023
精度 约 6-7 位十进制精度 约 15-16 位十进制精度
内存布局 1 位符号 8 位指数 23 位尾数 1 位符号 11 位指数 52 位尾数

float范围

1.18×1038 1.18 \times 10^{-38}

3.4×1038 3.4 \times 10^{38}

double范围

2.23×10308 2.23 \times 10^{-308}

1.8×10308 1.8 \times 10^{308}

详细解释

  1. 存储大小与位分配
  2. 指数偏移
  3. 范围与精度
  4. 性能

示例代码

以下代码展示 floatdouble 的二进制表示差异:

#include <iostream>
#include <bitset>
int main() {
    float f = 6.5f;
    double d = 6.5;
    unsigned int* f_ptr = reinterpret_cast<unsigned int*>(&f);
    unsigned long long* d_ptr = reinterpret_cast<unsigned long long*>(&d);
    std::cout << "float 6.5: " << std::bitset<32>(*f_ptr) << std::endl;
    std::cout << "double 6.5: " << std::bitset<64>(*d_ptr) << std::endl;
    return 0;
}

输出(假设小端序):

float 6.5: 01000000110100000000000000000000
double 6.5: 0100000000011010000000000000000000000000000000000000000000000000

解释:

注意事项

定点数

如bitcoin中用64位整数来表示Satoshi

1 Bitcoin (BTC) = 100,000,000 Satoshi

则0.00000001个比特币也就是表示为 1个Statoshi,在计算用int64表示就是存放的整数1,而不是存储浮点数。

#include <string>
#include <iomanip>
#include <sstream>

// 将 Satoshi (int64_t) 转换为 Bitcoin (double),并返回格式化的字符串
std::string satoshiToBitcoin(int64_t satoshi) {
    // 1 BTC = 100,000,000 Satoshi
    constexpr double SATOSHI_PER_BTC = 100000000.0;
    
    // 转换为 Bitcoin 值
    double bitcoin = static_cast<double>(satoshi) / SATOSHI_PER_BTC;
    
    // 使用 stringstream 格式化输出,保留 8 位小数
    std::ostringstream oss;
    oss << std::fixed << std::setprecision(8) << bitcoin;
    
    // 返回格式化字符串,移除末尾多余的零
    std::string result = oss.str();
    result.erase(result.find_last_not_of('0') + 1);
    if (result.back() == '.') {
        result.pop_back(); // 移除末尾的小数点
    }
    return result;
}

float转int64定点

float floatVal = 1.2;
double doubleVal = floatVal;
(int64_t)(doubleVal * (1LL << 32));

让我们逐步分析这段 C++ 代码,解释 float floatVal = 1.2; double doubleVal = floatVal; (int64_t)(doubleVal * (1LL << 32)); 的执行过程和结果。这涉及浮点数表示、类型转换和位运算。

代码分解

  1. 定义和初始化 float 变量

    float floatVal = 1.2;
  2. float 转换为 double

    double doubleVal = floatVal;
  3. 计算 (int64_t)(doubleVal * (1LL << 32))

232=4,294,967,296 2^{32} = 4,294,967,296

步骤 1:floatVal = 1.2 的表示 - 十进制 1.2 的二进制表示是无限循环的:1.0011001100110011...(即

1+0.2=1+23+24+27+28+ 1 + 0.2 = 1 + 2^{-3} + 2^{-4} + 2^{-7} + 2^{-8} + \dots

1.21.00110011...×20 1.2 \approx 1.00110011... \times 2^0

,存储指数 (e = E + 127 = 127)(二进制:01111111)。 - 尾数:小数部分 00110011001100110011001...,截取前 23 位:00110011001100110011001。 - 内存布局:0 01111111 00110011001100110011001。 - 十六进制:0x3F99999A。 - 实际存储值:由于尾数截断,floatVal1.2000000476837158203125(略大于 1.2,因为 float 精度有限)。

步骤 2:doubleVal = floatVal

步骤 3:(int64_t)(doubleVal * (1LL << 32))

232=4,294,967,296 2^{32} = 4,294,967,296

1.2000000476837158203125×4,294,967,2965,159,780,352.000002384185791015625 1.2000000476837158203125 \times 4,294,967,296 \approx 5,159,780,352.000002384185791015625

结果

1.2×4,294,967,296=5,153,960,755.2 1.2 \times 4,294,967,296 = 5,153,960,755.2

,但由于 float 精度限制,floatVal 实际值略大于 1.2,导致结果略大。

验证代码

以下代码验证上述计算:

#include <iostream>
#include <iomanip>
int main() {
    float floatVal = 1.2f;
    double doubleVal = floatVal;
    int64_t result = (int64_t)(doubleVal * (1LL << 32));
    std::cout << "floatVal: " << std::fixed << std::setprecision(20) << floatVal << std::endl;
    std::cout << "doubleVal: " << std::fixed << std::setprecision(20) << doubleVal << std::endl;
    std::cout << "Result: " << result << std::endl;
    return 0;
}

输出:

floatVal: 1.20000004768371582031
doubleVal: 1.20000004768371582031
Result: 5159780352

精度误差说明

总结