Codeup String Processing (1983)/ Algorithmic Notes 6.3 Subsection A Ideas and AC Codes for Exercise A

Because this topic has too many details, one detail can't be AC without taking into account, which has bothered me for three nights...Looking at the other big guys'code, although short, it can't be AC and is different from my solution method. Just this noon, this topic finally passed AC, decided to write your own solution ideas and small problems encountered in the process of solving, Finally, share your AC code, hoping to help more partners. This is the first article I wrote. If there are any shortcomings, I would like to be more inclusive.

Title Description

Reads in two strings, which may include'-','E','e','.', in addition to numbers, and outputs the result when added, requiring scientific notation (up to 10 significant digits) if floating point.

input

The input contains multiple sets of test data.

Each set of inputs takes up two lines, one string per line, and test data ensures that the string is structured exactly as described in the title.

output

Outputs the result of the addition of two numbers, one line for each set of outputs.

sample input

34.56
2.45e2

sample output

2.7956e2

First, I'll take you to analyze the title requirements. Strings contain four characters in addition to numbers (note that the first character is a'-', or minus sign), which means the input string may be a negative number, a floating point number, or a real number in standard scientific notation, where the number after e may have a negative sign if expressed in scientific notation. That is, the number after e may be a negative number. There may be little partners who are worried that the string they enter does not conform to the scientific notation specification, such as entering a "100e2" string. Here I want to reassure these little partners that, in my tests, there is no such input sample that does not conform to the scientific notation specification.

Next, let's talk about my ideas for solving problems. After I read the topic, the first idea was to solve the problem with the large integer operation, which I learned in the algorithmic notes before. Although it is a large integer, the idea can be migrated to floating point number to achieve large floating point operation, so that no matter how long the input string exponent is, it can be solved. The specific solution is to first convert the input string into a large floating point number, then convert the input real number represented by scientific notation into a real number of the general representation and then into a large floating point number, so that the two large floating point numbers can be added and subtracted. Then when exporting large floating-point numbers, consider converting floating-point numbers to real output represented by scientific notation, but if it's an integer you don't need to care about it, and if it's OK, you'll also want to consider whether to output a negative sign.

The idea sounds simple, but it is slightly more difficult to achieve, because there are many details to consider. Next, I will give you my AC code, then step by step analysis and explanation to help you understand.

#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;

struct bigf {
	string d, f;
	bool isPlus;
};

void deleteZero(bigf& a) {
	while (a.d[a.d.length() - 1] == '0' && a.d.length() > 1) {
		a.d.erase(a.d.length() - 1,1);
	}
	
	while (a.f.length() > 0 && a.f[a.f.length() - 1] == '0') {
		a.f.erase(a.f.end() - 1);
	}
}

bigf change(string str) {
	bigf a;
	if (str[0] == '-') {
		a.isPlus = false;
		str.erase(str.begin());
	}
	else a.isPlus = true;
	int e = 0;
	int eIndex=str.find('e');
	bool eIsPlus = true;
	if (eIndex == -1)eIndex = str.find('E');
	if (eIndex!=-1) {
		int i = eIndex + 1;
		if (str[i] == '-') {
			eIsPlus = false;
			i++;
		}
		for (; i < str.length(); i++) {
			e = e * 10 + (str[i] - '0');
		}
		str.erase(eIndex, str.length() - eIndex);
		if (!eIsPlus)e = 0 - e;
	}
	int pointIndex = str.find('.');
	if (pointIndex == -1) {
		if (e > 0) {
			for (int i = 0; i < e; i++) {
				a.d += '0';
			}
		}
		else if (e < 0) {
			a.d += '0';
			for (int i = -1; i > e; i--) {
				a.f += '0';
			}
			a.f += str[0];
		}
		for (int i = str.length() - 1; i >= 0; i--) {
			a.d += str[i];
		}
		
	}
	else {
		pointIndex += e;
		if (e >= 0) {
			for (int i = pointIndex; i >= 0; i--) {
				if (i >= str.length())a.d += '0';
				else if (str[i] != '.') a.d += str[i];
			}
			for (int i = pointIndex + 1; i < str.length(); i++) {
				a.f += str[i];
			}
		}
		else {
			a.d += '0';
			for (int i = pointIndex; i < 0; i++) {
				a.f += '0';
			}
			for (int i = 0; i < str.length(); i++) {
				if (str[i] != '.')a.f += str[i];
			}
			
		}
	}
	deleteZero(a);
	return a;
}

int cmp(bigf a, bigf b) {
	if (a.d.length() < b.d.length())return -1;
	else if (a.d.length() > b.d.length())return 1;
	else {
		for (int i = a.d.length() - 1; i >= 0; i--) {
			if (a.d[i] < b.d[i]) return -1;
			else if (a.d[i] > b.d[i]) return 1;
		}
		for (int i = 0; i < a.f.length() && i < b.f.length(); i++) {
			if (a.f[i] < b.f[i])return -1;
			else if (a.f[i] > b.f[i])return 1;
		}
		if (a.f.length() < b.f.length())return -1;
		else if (a.f.length() > b.f.length())return 1;
		else return 0;
	}
}

bigf add(bigf a, bigf b) {
	bigf c;
	for (int i = 0; i < a.f.length() || i < b.f.length(); i++) {
		c.f += '0';
	}
	while (a.d.length() < b.d.length()) {
		a.d += '0';
	}
	while (a.d.length() > b.d.length()) {
		b.d += '0';
	}
	int carry = 0;
	for (int i = a.f.length() >= b.f.length() ? a.f.length() - 1 : b.f.length() - 1; i >= 0; i--) {
		if (i >= a.f.length())c.f[i] = b.f[i];
		else if (i >= b.f.length())c.f[i] = a.f[i];
		else {
			int temp = (a.f[i] - '0') + (b.f[i] - '0') + carry;
			c.f[i] = (temp % 10) + '0';
			carry = temp / 10;
		}
	}
	for (int i = 0; i < a.d.length() || i < b.d.length(); i++) {
		int temp = (a.d[i] - '0') + (b.d[i] - '0') + carry;
		c.d += (temp % 10) + '0';
		carry = temp / 10;
	}
	if (carry)c.d += carry + '0';
	deleteZero(c);
	return c;
}

bigf sub(bigf a, bigf b) {
	bigf c;
	for (int i = 0; i < a.f.length() || i < b.f.length(); i++) {
		c.f += '0';
	}
	while (a.f.length() < b.f.length()) {
		a.f += '0';
	}
	while (b.f.length() < a.f.length()) {
		b.f += '0';
	}
	while (b.d.length() < a.d.length()) {
		b.d += '0';
	}
	for (int i = a.f.length() - 1; i >= 0; i--) {
		if (a.f[i] - b.f[i] < 0) {
			if (i > 0)a.f[i - 1] -= 1;
			else a.d[0] -= 1;
			a.f[i] += 10;
		}
		c.f[i] = a.f[i] - b.f[i] + '0';
	}
	for (int i = 0; i < a.d.length(); i++) {
		if (a.d[i] - b.d[i] < 0) {
			a.d[i + 1] -= 1;
			a.d[i] += 10;
		}
		c.d += a.d[i] - b.d[i] + '0';
	}
	deleteZero(c);
	return c;
}

void print(bigf a) {
	reverse(a.d.begin(), a.d.end());
	if (!a.f.length()) {
		cout << a.d << endl;
	}
	else {
		int e = a.d.length() - 1;
		if(e>0||e==0&&a.d[0]!='0')cout << a.d[0] << "." << a.d.substr(1, a.d.length() - 1) << a.f << "e" << e << endl;
		else {
			for (int i = 0; i < a.f.length(); i++) {
				if (a.f[i] != '0') {
					cout << a.f[i];
					if (i != a.f.length() - 1)cout << ".";
					cout << a.f.substr(i + 1, a.f.length() - i - 1) << "e-" << i + 1 << endl;
					break;
				}
			}
		}
	}
}

int main() {
	string stra, strb;
	while (cin >> stra >> strb) {
		bigf a = change(stra);
		bigf b = change(strb);
		if (a.isPlus && b.isPlus)print(add(a, b));
		else if (!a.isPlus && !b.isPlus) {
			cout << "-";
			print(add(a, b));
		}
		else if (a.isPlus) {
			if (cmp(a, b) == 1)print(sub(a, b));
			else if (cmp(a, b) == -1) {
				cout << "-";
				print(sub(b, a));
			}
			else cout << 0 << endl;
		}
		else {
			if (cmp(a, b) == -1)print(sub(b, a));
			else if (cmp(a, b) == 1) {
				cout << "-";
				print(sub(a, b));
			}
			else cout << 0 << endl;
		}
	}
	return 0;
}

Start with the structure:

struct bigf {
	string d, f;
	bool isPlus;
};

This structure is a large floating point number analogous to a large integer, where the strings d and f hold the integer and decimal parts of the floating point number respectively. It should be noted that this integer part is stored in the reverse order of the normal integer, that is, the character with a string subscript 0 holds the lowest bit of the integer. The highest bit of an integer is saved at the end of the string (see "Storage of algorithmic notes 5.6.1 large integers"), while the floating-point part is saved in the same order as the normal number sequence, so the first bit of both strings is the number closest to the decimal point. The bool variable isPlus holds whether the large floating point number is positive or not, and false indicates that the large floating point number is negative.

void deleteZero(bigf& a) {
	while (a.d[a.d.length() - 1] == '0' && a.d.length() > 1) {
		a.d.erase(a.d.length() - 1,1);
	}
	
	while (a.f.length() > 0 && a.f[a.f.length() - 1] == '0') {
		a.f.erase(a.f.end() - 1);
	}
}

This function is used to remove excess zeros from large floating-point numbers. This function is used both in the calculation process and in the processing of the final result, and it can process 0 represented by 0.000 directly to 0 to avoid errors in the output. It is important to note that the parameter is passed by reference, otherwise the extra 0 for large floating point numbers cannot be really removed! The first while loop handles the high-bit 0 of the integer part, which I'll just explain a little more. If you read Algorithmic Notes 5.6 Big Integer Operations, you should remember that the key is if the integer part is all zero, don't forget to keep a zero as the last integer. The second while loop deals with low 0 floating-point numbers. To understand this analogy with high 0 floating-point numbers, it is important to note that if all 0 floating-point numbers are zero, all 0 must be cleared. Do not reserve a 0 like integer part processing, otherwise the result after 6.000000 processing will be 6.0, which is obviously not the simplest result.

bigf change(string str) {
	bigf a;
	if (str[0] == '-') {
		a.isPlus = false;
		str.erase(str.begin());
	}
	else a.isPlus = true;
	int e = 0;
	int eIndex=str.find('e');
	bool eIsPlus = true;
	if (eIndex == -1)eIndex = str.find('E');
	if (eIndex!=-1) {
		int i = eIndex + 1;
		if (str[i] == '-') {
			eIsPlus = false;
			i++;
		}
		for (; i < str.length(); i++) {
			e = e * 10 + (str[i] - '0');
		}
		str.erase(eIndex, str.length() - eIndex);
		if (!eIsPlus)e = 0 - e;
	}
	int pointIndex = str.find('.');
	if (pointIndex == -1) {
		if (e > 0) {
			for (int i = 0; i < e; i++) {
				a.d += '0';
			}
		}
		else if (e < 0) {
			a.d += '0';
			for (int i = -1; i > e; i--) {
				a.f += '0';
			}
			a.f += str[0];
		}
		for (int i = str.length() - 1; i >= 0; i--) {
			a.d += str[i];
		}
		
	}
	else {
		pointIndex += e;
		if (e >= 0) {
			for (int i = pointIndex; i >= 0; i--) {
				if (i >= str.length())a.d += '0';
				else if (str[i] != '.') a.d += str[i];
			}
			for (int i = pointIndex + 1; i < str.length(); i++) {
				a.f += str[i];
			}
		}
		else {
			a.d += '0';
			for (int i = pointIndex; i < 0; i++) {
				a.f += '0';
			}
			for (int i = 0; i < str.length(); i++) {
				if (str[i] != '.')a.f += str[i];
			}
			
		}
	}
	deleteZero(a);
	return a;
}

This function can be said to be the core function, the function is to convert the sample input string into a large floating point number to save, more disgusting is the processing of e, with everyone's detailed analysis below.

The first if...else... statement is used to determine if the string is a negative number, false the isPlus variable of the large floating-point structure if it is a negative number, and then delete the'-'before the string to facilitate subsequent string conversions, without regard to the negative sign.

Next, the int variable E is used to hold the exponent represented by scientific counting. Next, eIndex is used to hold the subscript of the character "e" in the string, and the bool variable eIsPlus is used to hold whether the exponent e is a negative number. If "e" is not found in the string, then there may be an "E" according to the title description, so we will look for the "E" subscript next. If we find the "E" subscript, it means that the string is represented in scientific notation. We need to deal with exponents to store numbers in the normal representation. This is what the if statement below does. First let the int variable i be the next subscript to the character "e" or "E" to determine whether the character is a "-". If so, it means that the exponent e is a negative number, and then we need to take the opposite number to the variable E at the end. Then we calculate the subsequent exponent, which can be achieved by using a for loop. Finally, delete "e" or "E" in the string and all subsequent characters. Convenient string conversion later.

To do this, there are only numbers left in the string and possibly decimal points, so we want to determine if there are decimal points in the string and to process the exponent. Point Index keeps the subscript of the decimal point. If the exponent is positive, it can be understood that the subscript of the decimal point moves e bit to the right. Negative exponent is the absolute bit that the subscript of the decimal point moves e bit to the left. Over string length missing 0. If there is no decimal point, it is much simpler, indicating that the string has only an integer part, corresponding to the if (pointIndex=-1) judgment statement in the code. If the variable E is not equal to 0, it means that this is an integer in scientific notation, and there is only one integer, where greater than 0 means that the exponent is positive, we need to add 0 in the E-bit after it. Because the integer part of the large floating point number is stored in reverse order, we need to make up 0 before saving the only one integer. But if the variable E is less than 0, the exponent is negative. We need to make the integer part of the large floating point zero, then assume that the subscript number is -1, look left for the theoretical subscript of the decimal point, make the floating point part 0, and finally add a string unique integer to the floating point part (For example, 6e-2, the value of E is -2, 0.6 is the result of adding 0 to the integer, 0 is the result of adding 0 to the decimal part because i=-1>e) If e equals 0, the string is a normal integer, and we directly store the string in reverse order with the integer part, OK. But if there are decimal points that are relatively complex, first move the decimal point subscript according to the value of e to the right or left, and then determine if e is a non-negative number, let the large floating point integer part store the string decimal point theoretical position in reverse order with the decimal point subscript as described earlier If e is less than 0, all integers in the string are decimal, so large floating-point integers are 0 and floating-point parts are stored as numbers. String all numbers.

The previous deleteZero function is useful when the extra 0 is removed from the resulting large floating point number.

int cmp(bigf a, bigf b) {
	if (a.d.length() < b.d.length())return -1;
	else if (a.d.length() > b.d.length())return 1;
	else {
		for (int i = a.d.length() - 1; i >= 0; i--) {
			if (a.d[i] < b.d[i]) return -1;
			else if (a.d[i] > b.d[i]) return 1;
		}
		for (int i = 0; i < a.f.length() && i < b.f.length(); i++) {
			if (a.f[i] < b.f[i])return -1;
			else if (a.f[i] > b.f[i])return 1;
		}
		if (a.f.length() < b.f.length())return -1;
		else if (a.f.length() > b.f.length())return 1;
		else return 0;
	}
}

This is a function of a larger floating point number that is used before subtraction to determine whether a-b or b-a is output and whether a negative sign is output. The comparison of functions is the same as that of large integer operations. If the length is equal, the integers are compared from the high bit. If the length is equal, the decimals are compared bit by bit from the high bit (be careful not to compare the length of the decimal part, is 0.000001 greater than 0.9?), or if they are equal, the two large floating-point numbers are equal.

bigf add(bigf a, bigf b) {
	bigf c;
	for (int i = 0; i < a.f.length() || i < b.f.length(); i++) {
		c.f += '0';
	}
	while (a.d.length() < b.d.length()) {
		a.d += '0';
	}
	while (a.d.length() > b.d.length()) {
		b.d += '0';
	}
	int carry = 0;
	for (int i = a.f.length() >= b.f.length() ? a.f.length() - 1 : b.f.length() - 1; i >= 0; i--) {
		if (i >= a.f.length())c.f[i] = b.f[i];
		else if (i >= b.f.length())c.f[i] = a.f[i];
		else {
			int temp = (a.f[i] - '0') + (b.f[i] - '0') + carry;
			c.f[i] = (temp % 10) + '0';
			carry = temp / 10;
		}
	}
	for (int i = 0; i < a.d.length() || i < b.d.length(); i++) {
		int temp = (a.d[i] - '0') + (b.d[i] - '0') + carry;
		c.d += (temp % 10) + '0';
		carry = temp / 10;
	}
	if (carry)c.d += carry + '0';
	deleteZero(c);
	return c;
}

The addition operation of large floating-point number operation has the same idea as that of large integer addition operation. There are three differences. First, because the string holding numbers in large floating-point number is string type, unlike int array, which can be initialized to assign 0 ahead of time, so it is necessary to add 0 to the decimal part of the result c and the integer part of a and B before operation. (In fact, you can also not fill in zeros, but the details will be a lot of trouble when you calculate it). Second, you need to add decimal parts first. Third, big floating-point number saves numbers as string strings, which involves the conversion of characters and integers. The specific calculation process is not explained in detail, and unintelligible little partners can refer to algorithmic notes. Section 5.6.2 Four arithmetic operations for large integers 1.High precision addition. Finally, don't forget to remove the redundant 0 from the floating-point arithmetic result C. (It is estimated that a little buddy would like to ask why not remove the redundant 0 from the integer parts of the additives A and b, because a and B are values passed here!)

bigf sub(bigf a, bigf b) {
	bigf c;
	for (int i = 0; i < a.f.length() || i < b.f.length(); i++) {
		c.f += '0';
	}
	while (a.f.length() < b.f.length()) {
		a.f += '0';
	}
	while (b.f.length() < a.f.length()) {
		b.f += '0';
	}
	while (b.d.length() < a.d.length()) {
		b.d += '0';
	}
	for (int i = a.f.length() - 1; i >= 0; i--) {
		if (a.f[i] - b.f[i] < 0) {
			if (i > 0)a.f[i - 1] -= 1;
			else a.d[0] -= 1;
			a.f[i] += 10;
		}
		c.f[i] = a.f[i] - b.f[i] + '0';
	}
	for (int i = 0; i < a.d.length(); i++) {
		if (a.d[i] - b.d[i] < 0) {
			a.d[i + 1] -= 1;
			a.d[i] += 10;
		}
		c.d += a.d[i] - b.d[i] + '0';
	}
	deleteZero(c);
	return c;
}

The subtraction of large floating-point numbers is also consistent with the subtraction of large integers. There are also three differences. First, the decimal part of the result c, the decimal part of the subtracted a, the integer and decimal part of the subtraction b are to be complemented by 0. Second, the decimal part is to be subtracted. Third, the saved number is a character, not an int integer, which needs to be converted. Refer to "Algorithmic Notes" section 5.6.2 Four arithmetic operations for large integers 2.High precision subtraction", and finally don't forget to remove the extra 0 from result C.

void print(bigf a) {
	reverse(a.d.begin(), a.d.end());
	if (!a.f.length()) {
		cout << a.d << endl;
	}
	else {
		int e = a.d.length() - 1;
		if(e>0||e==0&&a.d[0]!='0')cout << a.d[0] << "." << a.d.substr(1, a.d.length() - 1) << a.f << "e" << e << endl;
		else {
			for (int i = 0; i < a.f.length(); i++) {
				if (a.f[i] != '0') {
					cout << a.f[i];
					if (i != a.f.length() - 1)cout << ".";
					cout << a.f.substr(i + 1, a.f.length() - i - 1) << "e-" << i + 1 << endl;
					break;
				}
			}
		}
	}
}

This is a function of printing large floating-point numbers, which can be printed as scientific notation with decimal points. The reverse() function of the first sentence inverts the integer part of a large floating point number to a string that stores integers sequentially for subsequent printing. If the decimal part of a large floating point number has a string length of 0, it means that the large floating point number is an integer and can be output directly without converting it to a scientific notation at all! If a large floating point number holds a floating point number, it needs to be output in scientific notation. Assuming that the floating point number is greater than 1, then the value of exponential e is the length of the integer part string minus one, that is, e is greater than 0 or E is 0 but the integer part is not 0. The first digit of the integer part that directly outputs the large floating point number is followed by the decimal point and then the remaining digits and decimal parts of the integer part. The final output of the values of E and E is the large floating point number represented in scientific notation. However, if the large floating point number holds a floating point number less than 1, that is, e is 0 and the integer part is 0, then you need to look for the first non-zero number from the highest position in the decimal part, find it and output it as an integer in scientific notation, then decide if there are any other digits after it, and if there are any, explain that the decimal part of scientific notation needs to be output. So output a decimal point, then all digits after the output and the values of E and e, the value of E is the number of zeros before the first digit that is not 0 (including integer 0), that is, subscript plus 1, and because the large floating point number is less than 1, the value of E is theoretically a negative number, but only one more'-'is needed to output.

int main() {
	string stra, strb;
	while (cin >> stra >> strb) {
		bigf a = change(stra);
		bigf b = change(strb);
		if (a.isPlus && b.isPlus)print(add(a, b));
		else if (!a.isPlus && !b.isPlus) {
			cout << "-";
			print(add(a, b));
		}
		else if (a.isPlus) {
			if (cmp(a, b) == 1)print(sub(a, b));
			else if (cmp(a, b) == -1) {
				cout << "-";
				print(sub(b, a));
			}
			else cout << 0 << endl;
		}
		else {
			if (cmp(a, b) == -1)print(sub(b, a));
			else if (cmp(a, b) == 1) {
				cout << "-";
				print(sub(a, b));
			}
			else cout << 0 << endl;
		}
	}
	return 0;
}

Finally, the main function here deals with whether to output a negative sign and whether to add or subtract. If the large floating point numbers a and b are all positive numbers, it is very simple to do the addition operation directly to output the result. If A and b are both negative numbers, then a negative sign is output and a and b are added. However, if a and b are positive and negative, you need to compare their absolute values to determine whether to output a negative sign, and then let the absolute values be large when subtracted to print the output.

Add some test samples to make it easier for little buddies to test their own code

sample input

34.56
2.45e2

3.57e2
345

999
1

1e2
3.54e1

1
-1

1e2
-100

1e20
4e15

3.732e-54
-5.78e-55

30.805573
-45.105871

sample output

2.7956e2
702
1000
1.354e2
0
0
100004000000000000000
3.154e-54

-1.4300298e1

Tags: C++ Algorithm data structure codeup

Posted on Fri, 26 Nov 2021 15:15:10 -0500 by stlewis