自己编写算法,将一个字符串类型的小数,转换成double类型的小数。
比喻:"15.246"转换成15.246,不能使用系统提供的Integer.parseInt和Double.parseDouble等方法。
初步一想,感觉是没办法啊。
其实不难,我之前讲过一个类似的例子,
两个大数相加:https://blog.csdn.net/fesdgasdgasdg/article/details/80953829
使用对应字符与'0'的ASCII相减,得到对应的字面值即可。
嗯,这个确实可以实现,转换成int类型就简单了。但是,小数怎么办?
以小数点为分隔符,切割成两个字符串,然后各自转换成int类型,然后再后期处理成double即可。
嗯,理解。简单了,下面开始描述性的演示"15.246"例子:
String str = "15.246";
先以小数点为分隔符,拆分成两个子字符串:
String[] arr = str.split("\\.");
注意上面步骤,为什么split时,需要\\符号?因为split的参数是正则表达式,正则中的.代表什么?自己去查一查吧。
在正则中
\\.表示字符.
\\\\表示字符串\\
那么arr数组的内容就是{"15","246"},我们先只考虑同时包含整数小数的情况
字符串"15"如何转换成int类型的15?
十位值 = ('1' - '0') * 10 = 10
个位值 = ('5' - '0') * 1 = 5
总和 = 15
注意,每个位上的数字,需要乘以位的单位。
同样的步骤,小数部分也得到了对应的int值246,然后想办法让246转换成0.246.
最后结果 = 15 + 0.246 = 15.246。
大致思路如上,我们开始撸点代码:
private static double toDouble(String str) {
if (str == null || "".equals(str.trim())) {
return 0;
}
str = str.trim();
//整个字符串直面值结果
double result = 0;
//先把字符串的小数,以小数点分割切开成两部分小数(注意正则)
String[] strNumArr = str.split("\\.");
//先处理整数部分的数字,转换成int数字
int intNumResult = 0;
for (int i = 0; i < strNumArr[0].length(); i++) {
int val = strNumArr[0].charAt(i) - '0';
//获得当前字符的字面整数值,然后乘以当前位的进制值
intNumResult += val * makeInteger10N(strNumArr[0].length() - i - 1);
}
//存储小数部分的整数值
int decimalNumResult = 0;
//strNumArr数组的长度大于1,证明原始字符串有小数部分
for (int i = 0; i < strNumArr[1].length(); i++) {
int val = strNumArr[1].charAt(i) - '0';
//获得当前字符的字面整数值,然后乘以当前位的进制值
decimalNumResult += val * makeInteger10N(strNumArr[1].length() - i - 1);
}
//保存小数部分最终结果
result = decimalNumResult * makeDecimal01N(strNumArr[1].length());
//加上之前的整数部分
result += intNumResult;
return result;
}
上面出现了两个函数,一个是构造整数十进制位的单位,即1、10、100等
另一个是构造小数值,即0.1、0.01、0.001,目的是转换上小数部分的int值为小数值。
/**
* 构造10的n倍数
*
* @return
*/
private static int makeInteger10N(int n) {
int i = 1;
for (int j = 0; j < n; j++) {
i *= 10;
}
return i;
}
/**
* 构造小数n个10分之一
*
* @return
*/
private static double makeDecimal01N(int n) {
double i = 1;
for (int j = 0; j < n; j++) {
i /= 10;
}
return i;
}
大概的代码撸完了,思路原理也讲了。面试时如果写到这里算是过关了。
但是,如果追求完美的话,上面的代码是有问题的。
1、如果字符串不包含小数点呢?如:"13"
2、如果字符串前面包含符号呢?如:"+24","-156"
3、如果字符串只包含小数部分呢?如:".246","-.132"
4、如果字符串还包含无效字符呢?如:"ff2.67","-1hh5.246","-1a2.5a5"
处理起来很简单,对于正负符号,需要在最后的结果乘以 -1 与否 即可。
对于其他的无效字符,判断 '?' - '0' 结果是否在0 ~ 9之间即可得知。
最后贴出完整代码:
public class ConvertToDouble {
public static void main(String[] args) {
String strDouble = "-12.25";
double result = toDouble(strDouble);
System.out.println(result);
}
private static double toDouble(String str) {
if (str == null || "".equals(str.trim())) {
return 0;
}
str = str.trim();
boolean isMinus = false;
if (str.startsWith("-")) {
//是负数
isMinus = true;
//去除负号
str = str.substring(1);
}
//整个字符串直面值结果
double result = 0;
//先把字符串的小数,以小数点分割切开成两部分小数(注意正则)
String[] strNumArr = str.split("\\.");
//先处理整数部分的数字,转换成int数字
int intNumResult = 0;
for (int i = 0; i < strNumArr[0].length(); i++) {
int val = strNumArr[0].charAt(i) - '0';
if (val < 0 || val > 9) {
//非数字
intNumResult /= 10;
continue;
}
//获得当前字符的字面整数值,然后乘以当前位的进制值
intNumResult += val * makeInteger10N(strNumArr[0].length() - i - 1);
}
//存储小数部分的整数值
int decimalNumResult = 0;
if (strNumArr.length > 1) {
//记录非法字符个数
int numNaN = 0;
//strNumArr数组的长度大于1,证明原始字符串有小数部分
for (int i = 0; i < strNumArr[1].length(); i++) {
int val = strNumArr[1].charAt(i) - '0';
if (val < 0 || val > 9) {
//非数字
decimalNumResult /= 10;
numNaN++;
continue;
}
//获得当前字符的字面整数值,然后乘以当前位的进制值
decimalNumResult += val * makeInteger10N(strNumArr[1].length() - i - 1);
}
//保存小数部分最终结果
result = decimalNumResult * makeDecimal01N(strNumArr[1].length() - numNaN);
}
//加上之前的整数部分
result += intNumResult;
//加上数值负号
if (isMinus) {
result *= -1;
}
return result;
}
/**
* 构造10的n倍数
*
* @return
*/
private static int makeInteger10N(int n) {
int i = 1;
for (int j = 0; j < n; j++) {
i *= 10;
}
return i;
}
/**
* 构造小数n个10分之一
*
* @return
*/
private static double makeDecimal01N(int n) {
double i = 1;
for (int j = 0; j < n; j++) {
i /= 10;
}
return i;
}
}