《JavaScript入门经典(第4版)》上第5章一个实例程序的修正,完善
今日,做《JavaScript入门经典(第4版)》第5章上的一个例题,感觉书上的代码有个小错误。
这是ch5_examp5.html上的一个实例,是计算一个数x保留y小数位后,四舍五入的结果是什么。
代码如下:
1: <script type="text/javascript">
2:
3: function fix(fixNumber, decimalPlaces)
4: {
5: var div = Math.pow(10,decimalPlaces);
6: fixNumber = Math.round(fixNumber * div) / div;
7: return fixNumber;
8: }
9: </script>
10: </head>
11: <body>
12: <script type="text/javascript">
13:
14: var number1 = prompt("Enter the number with decimal places you want to fix","");
15: var number2 = prompt("How many decimal places do you want?","");
16:
17: document.write(number1 + " fixed to " + number2 + " decimal places is: ");
18: document.write(fix(number1,number2));
19:
20: </script>
但这个代码运行后有个问题,就是如果输入1.995,要求保留2个小数位,但结果却是“2”,原因在于在用pow()方法乘以原数字并用Math.round()方法后,这样的情况会出现尾数为“0”的情况,再除以10,100这样的,就不能保留我们希望保留的小数位了。
因此,书上的代码是有问题,我捉摸了一下,可以对原有代码做个修正,加一个if判断,如果尾数为0,则说明这种情况出现了,需要进行修正。新的代码如下:
1: <script type = "text/javascript">
2: function deci(x,y){
3: var a = x*(Math.pow(10,y));
4: x = (Math.round(a)) / (Math.pow(10,y));
5: var z = new String(Math.round(a)); // 创建一个新变量z,将Math.round(a)转为字符串类型,这样就可以用charAt方法判断字符串的最后一位是否为0)
6: if (z.charAt(z.length-1) ==0) //如果变量z的最后一位为0,则说明除以10*y后,会变成整数,不保留小数位,所以要用toFixed方法强制保留小数位;
7: {
8: var u = new Number(x); //创建一个新变量u,将x转为Number类型,这样就可以调用toFixed方法
9: var t = u.toFixed(y); //创建一个新变量t,将u取小数位y位;
10: return t;
11: }
12: else
13: return x;
14: }
15: var X1 = prompt("please input x", "");
16: var Y1 = prompt("please input y","");
17:
18: document.write(deci(X1,Y1));
19: </script>
这个解决方案比较复杂,需要先创建一个新变量z,将原数据变量类型转为字符串,才好使用charAt方法去获取最后一个字符值(我试了,charAt方法,只能用于字符串类型),然后进行条件判断,如果变量z最后一位是0,则需要将变量x先转为Number类型,再使用toFixed方法,强制赋予y位的小数位。
但这个解决方案,也不是很好,简单的问题,搞得这么复杂,中间涉及大量数据转换乃至数据类型转换,如果不是为了学知识,仅是从算法而言,是在不符合“简单为美”的原则。后来想到既然将简单数值型变量,用new Number方法转为Number对象后,就可以使用toFixed()方法了,为何还要使用pow()方法+round()方法的方式去处理?可以直接转换成Number对象再强制保留小数位即可。代码优化后如下,执行结果完全无问题。
1: <script type = "text/javascript">
2: function deci(x,y){
3: var a = new Number(x); //将变量x转换为Number对象类型,这样就可以使用toFixed()方法了
4: var b = a.toFixed(y); //toFixed()方法,可以实现按规定小数位四舍五入
5: return b;
6: }
7: var X1 = prompt("please input x", "");
8: var Y1 = prompt("please input y","");
9:
10: document.write(deci(X1,Y1));
11: </script>
补充:
奇怪的是,最后一个代码,如果输入0.95和1.95这样的数字,取1位小数的四舍五入,结果应该是1.0、2.0,在ie9浏览器上,结果确实如此,但在firefox和chrome浏览器,都输出成0.9和1.9,而2.95就不存在这样的问题了。不知何故?
而运行第2段那个比较复杂的代码,则以上3个浏览器,都能完美计算四舍五入后的结果。
网上找到一篇文章,看来可能是toFixed()方法的一个bug造成的。
转载如下,这个代码还看不懂,以后再看吧:
原文网址:http://ahailu.blog.163.com/blog/static/833716120097104825237/
在JS中四舍五入的函数 toFixed(n) , n为要保留的小数位数。
n为0~20,当n超过20的时候,JS会出错。
var d=10.005;
var f=d.toFixed(2);
alert(f);
bug:
如果小数点前和要截取的前一位都是0时,不会按常理截取。
var h=0.07
h.toFixed(1)的值为0.0
如果要修改这个缺陷,可以把js中的Number类型的toFixed方法重写。
例如:
1:
2: Number.prototype.toFixed = function(d)
3: {
4: var s=this+"";if(!d)d=0;
5: if(s.indexOf(".")==-1)s+=".";s+=new Array(d+1).join("0");
6: if (new RegExp("^(-|\\+)?(\\d+(\\.\\d{0,"+ (d+1) +"})?)\\d*$").test(s))
7: {
8: var s="0"+ RegExp.$2, pm=RegExp.$1, a=RegExp.$3.length, b=true;
9: if (a==d+2){a=s.match(/\d/g); if (parseInt(a[a.length-1])>4)
10: {
11: for(var i=a.length-2; i>=0; i--) {a[i] = parseInt(a[i])+1;
12: if(a[i]==10){a[i]=0; b=i!=1;} else break;}
13: }
14: s=a.join("").replace(new RegExp("(\\d+)(\\d{"+d+"})\\d$"),"$1.$2");
15: }if(b)s=s.substr(1);return (pm+s).replace(/\.$/, "");} return this+"";
16: };
这样就可以修复Number类型的toFixed方法。