长浮点数加法(北理工/北京理工大学/程序设计方法与实践/小学期)

本文介绍了一种解决400位非负实数加法问题的算法,利用int数组存储数据,处理浮点数的精度问题,包括读取、计算有效数字宽度、翻转数字顺序以及进位调整。核心思路涉及整数对齐和浮点数特殊处理。
摘要由CSDN通过智能技术生成

在这里插入图片描述

题干

Input two nonnegative real numbers a and b, the maximum length of which is less than 400.
output the value a+b with the simplest form,i.e. without prefix 0, without postposition 0, omit decimal point if decimal part is 0.

翻译一下就是,400位的长浮点数加法。

输入两个最大长度小于400的非负实数a和b。用最简单的形式输出a+b的值,即。不带前缀0,不带后置0,小数部分为0则省略小数点。

思路

数据结构

数据结构:统一采用int数组进行储存,计算,在读取的时候就将char-48转化为对应的int值。

这种结构的优点在于计算过程简单,缺点是无法区分整数0还是初始化后的0,应对这种情况需要在函数中考虑有效数字的宽度,比如add函数,里面的参数就有一个len

函数

  1. read函数
    • 读入浮点数
    • 获取基本信息。pos代表以小数点为单位,左边的位数。
    • 对于整数来说,扫描不到浮点,所以最后会进行修正,将浮点放到最后,即400.这种感觉。
  2. fltlen函数。
    • 类似strlen函数
    • 逆序扫描,具有更高的适应性
    • 考虑到pos,保证整数部分,让500的长度为3,而不是1
  3. print函数。
    • 输出浮点数格式
    • 考虑宽度,这个宽度在前面会计算出来,这样就可以去除无效后导0
    • 考虑pos,这样就可以确定浮点是否要打出来
  4. reverse函数。
    • 翻转
    • 可以指定翻转的宽度
  5. add函数。
    • 顺次相加
    • 同样需要指定宽度

核心思路

本题的核心思路类似于整数加法,但是对齐的点位不同,整数只需要把尾部直接对齐就行,所以处理很简单。本题也类似,只是有更多细节:

  1. 读入浮点数,计算初始信息(各种宽度)
  2. 翻转
  3. 叠加
  4. 翻转
  5. 根据各种信息格式化输出

其中关于翻转,浮点数比较特别,浮点数要把浮点对齐(整数也可以理解为浮点在尾部的实数),这样的话就要考虑浮点后的位数(neg),在翻转的时候,neg比较小的要补上差的位,这样也就可以在反转的时候顺便对齐了。

a=123.45(pos=3,neg=2)
b=12.345(pos=2,neg=3

翻转的时候,要额外给a补上1位(3-2=1),因此翻转后是下面这样,那个0就是额外补的。使用int数组的好处就是,你在翻转的时候,直接翻就行,如果用char形,补的时候还要把’\0’转化为’0’。

054321
54321

计算过程中,可能会出现进位,但是进位只可能进1位,所以用一个carry变量来调整。

最后就是输出,输出的思路就是。

代码

/*
  @author cyy
 */

#include<stdio.h>
#include<string.h>

#define MAX_LEN 900      //粗略计算一下应该是801,a为400位的整数,b为400位的小数,考虑1进位,保险起见开900

int fltlen(int* num,int pos);//计算实数宽度
void read(int* num,int *pos);//读取实数,通过指针返回到pos里
void print(int* num,int pos);//打印实数
void reverse(int *pre,int* reverse,int end);//翻转实数
void add(int*a,int*b,int*c,int len);//累加

int main(void)
{
	//freopen("input.txt","r",stdin);//乐学提交把这个注释掉
	
	int a[MAX_LEN];
	int b[MAX_LEN];
	int a_rev[MAX_LEN];
	int b_rev[MAX_LEN];
	int c[MAX_LEN];
	int c_rev[MAX_LEN];
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(a_rev,0,sizeof(a_rev));
	memset(b_rev,0,sizeof(b_rev));
	memset(c,0,sizeof(c));
	memset(c_rev,0,sizeof(c_rev));

	//读取,初始信息获取
	int a_pos,b_pos;
	read(a,&a_pos);
	read(b,&b_pos);
	int a_len=fltlen(a,a_pos);
	int b_len=fltlen(b,b_pos);
	
	int a_neg=a_len-a_pos;
	int b_neg=b_len-b_pos;
	int init_len=(a_pos>b_pos?a_pos:b_pos)+(a_neg>b_neg?a_neg:b_neg);//结果初始长度(不考虑进位)
	int init_pos=a_pos>b_pos?a_pos:b_pos;//结果初始前缀长度(不考虑进位)
	int carry=0;//结果是否产生进位

	//printf("%d",init_pos);
	//翻转
	if(a_neg>b_neg)//1.234(3)>123.45(2)
	{
		reverse(a,a_rev,a_len-1);
		reverse(b,b_rev,b_len-1+(a_neg-b_neg));
	}
	else
	{
		reverse(a,a_rev,a_len-1+(b_neg-a_neg));
		reverse(b,b_rev,b_len-1);
	}
	//相加init_len位
	add(a_rev,b_rev,c_rev,init_len);
	if(c_rev[init_len]!=0)//查看是否有进位
		carry=1;
	//print(c_rev,-1);
	//再次翻转(考虑进位)
	reverse(c_rev,c,init_len+carry-1);
	//输出(考虑进位)
	print(c,init_pos+carry);
	
	return 0;
	
}


//输入浮点数数组,返回有效数字长度,考虑0.xx,0,x
//思路:逆序扫描保证前导0,加pos保证整数部分的长度
int fltlen(int* num,int pos)
{
	for(int i=MAX_LEN-1;i>0;i--)
	{
		if(num[i]!=0)
		{
			return (i+1)>pos?(i+1):pos;//取两者最大
		}
	}
	//i=1还没return,说明就是x或者0,至少占据1位
	return 1>pos?1:pos;//取两者最大
}

//读取一个浮点数,并且返回浮点正位置
void read(int* num,int *pos)
{
	char ch;
	int i=0;
	*pos=0;
	
	while((ch=getchar())!='\n')
	{
		if(ch=='.')//遇到浮点(TODO,考虑没有浮点的情况)
		{
			*pos=i;
		}
		else
		{
			num[i++]=(int)(ch-48);
		}
	}
	
	if(*pos==0)//没有发现小数点,判定为整数,修正pos
	{
		*pos=i;
	}
}

//输出一个浮点数,给定浮点位置,考虑0.xxx的情况和0的情况,pos<0(用-1)代表整数
void print(int* num,int pos)
{
	int len=fltlen(num,pos);//代表有效数字长度
	for(int i=0;i<len;i++)
	{
		if(i==pos)//如果pos<=len,代表整数,没机会打出.
		{
			printf(".");
		}
		printf("%d",num[i]);
	}
	
	printf("\n");
}

//从end开始,翻转
void reverse(int *pre,int* reverse,int end)
{
	for(int i=0;i<=end;i++)
	{
		reverse[i]=pre[end-i];
	}
}

//顺位相加,len位
void add(int*a,int*b,int*c,int len)
{
	for(int i=0;i<len;i++)
	{
		c[i]+=(a[i]+b[i]);//叠加
		c[i+1]+=c[i]/10;//进位
		c[i]=c[i]%10;//本位
	}
}

测试用例

你们自己去查freopen函数吧,基操。

测试的时候,把要用的用例放到最前面就好

9.00009
34320
34329.00009 (中间有连续0的情况)

1.10
09.100
10.2 (假设输入不是标准形式)

999
1.11
1000.11(整数+小数)

300
200
500  (整数)

0.11
0
0.110测试)

0
0
00情况)


0.11
0.1
0.21 (前导098.99
1.01
100(后导0 保证整数部分)

99.99
1.01
101

0.01
0.99
1 (后导0+取整)

99.89
1.01
100.9(后导0 10.11
0.9
1.01(进位1+前导099.9
1.12
101.02(普通11.234
123.45
124.684(普通2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亦梦亦醒乐逍遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值