C++中高精度运算问题

前言:在备赛蓝桥杯时,遇到C++处理数值较大的浮点数问题,特此记录一下。

C++ 的 std::string 类使用动态内存分配,其长度可以根据需要动态增加或减少,自动调整内存大小以适应字符串内容的变化。当字符串长度超过当前分配的内存时,std::string 会自动重新分配更大的内存空间以容纳更多字符。这种动态内存管理使得 std::string 在大多数情况下没有长度限制。

数据类型 描述 大小(字节) 范围/取值示例
bool 布尔类型,表示真或假 1 truefalse
char 字符类型,通常用于存储 ASCII 字符 1 -128 到 127 或 0 到 255
signed char 有符号字符类型 1 -128 到 127
unsigned char 无符号字符类型 1 0 到 255
short 短整型 2 -32,768 到 32,767
unsigned short 无符号短整型 2 0 到 65,535
int 整型 4 -2,147,483,648 到 2,147,483,647
unsigned int 无符号整型 4 0 到 4,294,967,295
long 长整型 4 或 8 取决于平台
unsigned long 无符号长整型 4 或 8 取决于平台
long long 长长整型(C++11 引入) 8 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long long 无符号长长整型(C++11 引入) 8 0 到 18,446,744,073,709,551,615
float 单精度浮点数 4 约 ±3.4e±38(6-7 位有效数字)
double 双精度浮点数 8 约 ±1.7e±308(15 位有效数字)
long double 扩展精度浮点数 8、12 或 16 取决于平台

题目:传送门

题解:

cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

string add(string num1, string num2) {
    int carry = 0;
    string result;
    int i = num1.length() - 1;
    int j = num2.length() - 1;
    
    while (i >= 0 || j >= 0 || carry > 0) {
        int digit1 = (i >= 0) ? num1[i--] - '0' : 0; 
        int digit2 = (j >= 0) ? num2[j--] - '0' : 0;
        int sum = digit1 + digit2 + carry;
        carry = sum / 10;
        result += to_string(sum % 10);
    }
    
    reverse(result.begin(), result.end());
    return result;
}

string multiply(string num1, int num2) {
    int carry = 0;
    string result;
    
    for (int i = num1.length() - 1; i >= 0; --i) {
        int digit = num1[i] - '0';
        int product = digit * num2 + carry;
        carry = product / 10;
        result += to_string(product % 10);
    }
    
    if (carry > 0) {
        result += to_string(carry);
    }
    
    reverse(result.begin(), result.end());
    return result;
}

string multiply(string num1, string num2) {
    string result = "0";
    for (int i = num2.length() - 1; i >= 0; --i) {
        string temp = multiply(num1, num2[i] - '0');
        temp += string(num2.length() - 1 - i, '0');
        result = add(result, temp);
    }
    return result;
}

int main() {
    int n; string d; cin >> n >> d;
    
    size_t pos = d.find('.');
    string d1 = d.substr(0, pos);
    string d2 = d.substr(pos + 1);
    int len_float = d2.size();
    
    string pow2n = "1";
    for (int i = 1; i <= n; ++i) pow2n = multiply(pow2n, "2");
    
    d1 = multiply(d1, pow2n);
    d2 = multiply(d2, pow2n);
    
    string d2_int = "0";
    if(d2.size() != len_float) d2_int = d2.substr(0, d2.size() - len_float);
    
    int x = d2[d2.size() - len_float] - '0';
    if (x >= 5) d2_int = add(d2_int, "1");
    
    cout << add(d1, d2_int) << '\n';
    
    return 0;
}

举例说明 :

1. add( string , string )

cpp 复制代码
string add(string num1, string num2) {
    int carry = 0; // 进位
    string result; // 存储结果
    int i = num1.length() - 1; // num1 的最后一个字符索引
    int j = num2.length() - 1; // num2 的最后一个字符索引
    
    while (i >= 0 || j >= 0 || carry > 0) {
        int digit1 = (i >= 0) ? num1[i--] - '0' : 0; // 获取 num1 当前位的数字
        int digit2 = (j >= 0) ? num2[j--] - '0' : 0; // 获取 num2 当前位的数字
        int sum = digit1 + digit2 + carry; // 计算当前位的和
        carry = sum / 10; // 更新进位
        result += to_string(sum % 10); // 将当前位的结果添加到结果字符串中
    }
    
    reverse(result.begin(), result.end()); // 反转结果字符串
    return result; // 返回最终结果
}

假设我们要计算 num1 = "123"num2 = "456" 的和。

  1. 初始化

    • carry = 0

    • result = ""

    • i = 2 (指向 num1 的最后一个字符 '3')

    • j = 2 (指向 num2 的最后一个字符 '6')

  2. 第一次循环

    • digit1 = 3 (从 num1[2])

    • digit2 = 6 (从 num2[2])

    • sum = 3 + 6 + 0 = 9

    • carry = 9 / 10 = 0

    • result = "9"

  3. 第二次循环

    • i = 1, j = 1

    • digit1 = 2 (从 num1[1])

    • digit2 = 5 (从 num2[1])

    • sum = 2 + 5 + 0 = 7

    • carry = 7 / 10 = 0

    • result = "97"

  4. 第三次循环

    • i = 0, j = 0

    • digit1 = 1 (从 num1[0])

    • digit2 = 4 (从 num2[0])

    • sum = 1 + 4 + 0 = 5

    • carry = 5 / 10 = 0

    • result = "975"

  5. 第四次循环

    • i = -1, j = -1

    • carry = 0,循环条件不再满足,退出循环。

  6. 反转结果

    • result 在反转后变为 "579"
  7. 返回结果

    • 函数返回字符串 "579"

2. multiply(string , int )

cpp 复制代码
string multiply(string num1, int num2) {
    int carry = 0; // 进位
    string result; // 存储结果
    
    for (int i = num1.length() - 1; i >= 0; --i) {
        int digit = num1[i] - '0'; // 获取当前位的数字
        int product = digit * num2 + carry; // 计算当前位的乘积
        carry = product / 10; // 更新进位
        result += to_string(product % 10); // 将当前位的结果添加到结果字符串中
    }
    
    if (carry > 0) {
        result += to_string(carry); // 如果还有进位,添加到结果中
    }
    
    reverse(result.begin(), result.end()); // 反转结果字符串
    return result; // 返回最终结果
}

假设我们要计算 num1 = "123"num2 = 4 的乘积。

  1. 初始化

    • carry = 0

    • result = ""

  2. 循环处理每一位

    • num1 的最后一位开始,逐位处理。
  3. 第一次循环(i = 2,处理 '3'):

    • digit = 3 (从 num1[2])

    • product = 3 * 4 + 0 = 12

    • carry = 12 / 10 = 1

    • result += to_string(12 % 10) = "2" (当前结果是 "2")

  4. 第二次循环(i = 1,处理 '2'):

    • digit = 2 (从 num1[1])

    • product = 2 * 4 + 1 = 9

    • carry = 9 / 10 = 0

    • result += to_string(9 % 10) = "2" + "9" (当前结果是 "29")

  5. 第三次循环(i = 0,处理 '1'):

    • digit = 1 (从 num1[0])

    • product = 1 * 4 + 0 = 4

    • carry = 4 / 10 = 0

    • result += to_string(4 % 10) = "29" + "4" (当前结果是 "294")

  6. 检查进位

    • carry 为 0,因此不需要添加额外的进位。
  7. 反转结果

    • result 在反转后变为 "492"
  8. 返回结果

    • 函数返回字符串 "492"
相关推荐
明月醉窗台8 分钟前
Qt 入门 1 之第一个程序 Hello World
开发语言·c++·qt
Craaaayon21 分钟前
Java八股文-List集合
java·开发语言·数据结构·list
幻想趾于现实27 分钟前
C# Winform 入门(11)之制作酷炫灯光效果
开发语言·c#·winform
hy____12331 分钟前
类与对象(中)(详解)
开发语言·c++
wen__xvn36 分钟前
c++STL入门
开发语言·c++·算法
2301_794461571 小时前
多线程编程中的锁策略
java·开发语言
XYN611 小时前
【嵌入式学习3】基于python的tcp客户端、服务器
服务器·开发语言·网络·笔记·python·学习·tcp/ip
the_nov1 小时前
20.IP协议
linux·服务器·网络·c++·tcp/ip
只有月亮知道1 小时前
C++list常用接口和模拟实现
开发语言·c++
Epiphany心理1 小时前
R语言使用ggplot2作图
开发语言·r语言