浮點數(shù)判斷相等問題
啤酒和飲料
啤酒每罐2.3元,飲料每罐1.9元。小明買了若干啤酒和飲料,一共花了82.3元。
我們還知道他買的啤酒比飲料的數(shù)量少,請你計算他買了幾罐啤酒。
注意:答案是一個整數(shù)。請通過瀏覽器提交答案
不要書寫任何多余的內(nèi)容(例如:寫了飲料的數(shù)量,添加說明文字等)。
這道題是送分題,很多同學(xué)用通過暴力破解法這種方法實現(xiàn)的:
public class 浮點數(shù)1_啤酒和飲料 {
public static void main(String[] args) {
for (int a = 0; a < 100; a++) //a 是啤酒數(shù)量,b是飲料的數(shù)量,假設(shè)它們的取值范圍都在0 ~ 100
{
for (int b = 0; b < 100; b++) {
if (a * 2.3 + b * 1.9 == 82.3 && a < b)
System.out.println(a + "," + b);
}
}
}
}
只不過,犯了一個大忌——兩個浮點數(shù)之間不能用“==”來判斷是否相等。因為浮點數(shù)在計算式內(nèi)部是采用二進(jìn)制的方式來表示。因此在計算機(jī)中0.1+0.2不是等于0.3,而是等于“0.30000000000000004”(不信自己去敲一敲)。這道題能算出結(jié)果算是你運氣好。
既然這樣,那我們?nèi)绾蝸砼袛喔↑c數(shù)是否相等呢?
要判斷浮點數(shù)是否相等,則a-b的絕對值無限接近一個非常小的值。
即|a - b| < set,一般set=1E-6(科學(xué)計數(shù)法,1*10的負(fù)6次方)即可。
由此可以這樣實現(xiàn):
public class 浮點數(shù)1_啤酒和飲料 {
public static void main(String[] args) {
for (int a = 0; a < 100; a++) //a 是啤酒數(shù)量,b是飲料的數(shù)量,假設(shè)它們的取值范圍都在0 ~ 100
{
for (int b = 0; b < 100; b++) {
if (Math.abs(a * 2.3 + b * 1.9 - 82.3) < 1E-10 && a < b)
System.out.println(a + "," + b);
}
}
}
}
由于題目給的這些浮點數(shù)都保留一位小數(shù),則可以把所有數(shù)值都乘以10,變成整數(shù)來全等判斷:
public class 浮點數(shù)1_啤酒和飲料 {
public static void main(String[] args) {
for (int a = 0; a < 100; a++) //a 是啤酒數(shù)量,b是飲料的數(shù)量,假設(shè)它們的取值范圍都在0 ~ 100
{
for (int b = 0; b < 100; b++) {
if (a * 23 + b * 19 == 823 && a < b)
System.out.println(a + "," + b);
}
}
}
}
使用整數(shù)進(jìn)行全等判斷,比浮點數(shù)安全多了。
海盜問題
有一群海盜(不多于20人),在船上比拼酒量。過程如下:打開一瓶酒, 所有在場的人平分喝下,有幾個人倒下了。再打開一瓶酒平分,又有倒下的,再次重復(fù)...... 直到開了第4瓶酒,坐著的已經(jīng)所剩無幾,海盜船長也在其中。當(dāng)?shù)?瓶酒平分喝下后,大家都倒下了。等船長醒來,發(fā)現(xiàn)海盜船擱淺了。他在航海日志中寫到:“......昨天,我正好喝了一瓶.......奉勸大家,開船不喝酒,喝酒別開船......”
請你根據(jù)這些信息,推斷開始有多少人,每一輪喝下來還剩多少人。
如果有多個可能的答案,請列出所有答案,每個答案占一行。
格式是:人數(shù),人數(shù),...
例如,有一種可能是:20,5,4,2,0
思路:這道題關(guān)鍵是船長的這句話,“昨天,我正好喝了一瓶”,船上打開了4瓶酒,每瓶酒向沒醉的人進(jìn)行平分。所以,由題意可得,每次平分的酒量相加為1。
代碼如下:
public class 浮點數(shù)2_海盜問題 {
public static void main(String[] args) {
int a, b, c, d; //每一輪剩下的人
for (a = 20; a >= 1; a--)
//第一輪,海盜的人不多于20人,但至少會有一人
for (b = a - 1; b >= 1; b--)
//每一輪都有人倒下,因此第二輪肯定比第一輪a的人數(shù)少
for (c = b - 1; c >= 1; c--)
//第三輪人數(shù)比第二輪少,但至少有一人
for (d = c - 1; d >= 1; d--) {
//第四輪人數(shù)比第三輪少,但至少有一人
//船長四輪都喝了,每輪都是平分的酒,一共喝了一瓶
if (1.0 / a + 1.0 / b + 1.0 / c + 1.0 / d == 1.0)
System.out.println(a + "," + b + "," + c + "," + d);
}
}
}
注意:判斷式內(nèi)一定是(1.0/a+1.0/b+1.0/c+1.0/d==1.0),如果是(1/a+1/b+1/c+1/d==1),由于1/a是整型,所以1/a為0,不是小數(shù)。
但是,上面的代碼忽略了“兩個浮點數(shù)之間不能用全等來判斷是否相等。”這個易錯點。
修改的代碼如下:
public class 浮點數(shù)2_海盜問題 {
public static void main(String[] args) {
int a, b, c, d; //每一輪剩下的人
for (a = 20; a >= 1; a--)
//第一輪,海盜的人不多于20人,但至少會有一人
for (b = a - 1; b >= 1; b--)
//每一輪都有人倒下,因此第二輪肯定比第一輪a的人數(shù)少
for (c = b - 1; c >= 1; c--)
//第三輪人數(shù)比第二輪少,但至少有一人
for (d = c - 1; d >= 1; d--) {
//第四輪人數(shù)比第三輪少,但至少有一人
//船長四輪都喝了,每輪都是平分的酒,一共喝了一瓶
if (Math.abs(1.0 / a + 1.0 / b + 1.0 / c + 1.0 / d - 1.0) < 1E-10)
System.out.println(a + "," + b + "," + c + "," + d);
}
}
}
這道題,我們還有另一個判斷的方式,就是在(1/a+1/b+1/c+1/d==1)中,每項分別乘以(a * b * c * d)
修改的代碼如下:
public class 浮點數(shù)2_海盜問題 {
public static void main(String[] args) {
int a, b, c, d; //每一輪剩下的人
for (a = 20; a >= 1; a--)
//第一輪,海盜的人不多于20人,但至少會有一人
for (b = a - 1; b >= 1; b--)
//每一輪都有人倒下,因此第二輪肯定比第一輪a的人數(shù)少
for (c = b - 1; c >= 1; c--)
//第三輪人數(shù)比第二輪少,但至少有一人
for (d = c - 1; d >= 1; d--) {
//第四輪人數(shù)比第三輪少,但至少有一人
//船長四輪都喝了,每輪都是平分的酒,一共喝了一瓶
if (b * c * d + a * c * d + a * b * d + a * b * c == a * b * c * d)
System.out.println(a + "," + b + "," + c + "," + d);
}
}
}
四舍六入五成雙
Java舍入方式叫“四舍六入五成雙”。
規(guī)則:
(1)被修約的數(shù)字小于5時,該數(shù)字舍去;
(2)被修約的數(shù)字大于5時,則進(jìn)位;
(3)被修約的數(shù)字等于5時,要看5前面的數(shù)字,若是奇數(shù)則進(jìn)位,若是偶數(shù)則將5舍掉,即修約后末尾數(shù)字都成為偶數(shù);若5的后面還有不為“0”的任何數(shù),則此時無論5的前面是奇數(shù)還是偶數(shù),均應(yīng)進(jìn)位。
舉例,用上述規(guī)則對下列數(shù)據(jù)保留3位有效數(shù)字:
9.8249=9.82, 9.82671=9.83
9.8350=9.84, 9.8351 =9.84
9.8250=9.82, 9.82501=9.83
從統(tǒng)計學(xué)的角度,“四舍六入五成雙”比“四舍五入”要科學(xué),在大量運算時,它使舍入后的結(jié)果誤差的均值趨于零,而不是像四舍五入那樣逢五就入,導(dǎo)致結(jié)果偏向大數(shù),使得誤差產(chǎn)生積累進(jìn)而產(chǎn)生系統(tǒng)誤差,“四舍六入五成雙”使測量結(jié)果受到舍入誤差的影響降到最低。