2016-11-07 55 views
3

首先,採取特定浮動f浮動的不一致轉換爲十進制在Ruby中

f = [64.4, 73.60, 77.90, 87.40, 95.40].sample # take any one of these special Floats 
f.to_d.class == (1.to_d * f).class # => true (BigDecimal) 

因此,通過BigDecimal乘以蒙上fBigDecimal。因此1.to_d * f(或f * 1.to_d)可以被看作是將(f)轉換爲BigDecimal的(差但仍然)形式。然而,對於這些具體的值,我們有:

f.to_d == 1.to_d * f # => false (?!) 

這不是一個錯誤嗎?我假設,乘以1.to_d Ruby應該在內部調用f.to_d。但結果不同,即對於f = 64.4

f.to_d # => #<BigDecimal:7f8202038280,'0.644E2',18(36)> 
1.to_d * f # => #<BigDecimal:7f82019c1208,'0.6440000000 000001E2',27(45)> 

我不明白爲什麼浮點表示誤差應該在這裏的藉口,但它顯然是一個原因,不知何故。那麼爲什麼會這樣呢?

PS。我寫的代碼這個問題玩弄一個片段:

https://github.com/Swarzkopf314/ruby_wtf/blob/master/multiplication_by_unit.rb

+0

'f == f.to_d'是否返回true? –

+0

在'.to_d'之後'f'的值是多少?內部表示通常非常明顯。 – tadman

+0

我編輯了我的問題。 –

回答

1

那麼爲什麼會發生這種情況呢?

TL;使用不同的精度。

龍答:

64.4.to_d電話bigdecimal/utilFloat#to_d

def to_d(precision=nil) 
    BigDecimal(self, precision || Float::DIG) 
end 

除非另有說明,它使用的Float::DIG一個隱含的精度是15對當前實現:

Float::DIG 
#=> 15 

所以64.4.to_d相當於:

BigDecimal(64.4, Float::DIG) 
#=> #<BigDecimal:7fd7cc0aa838,'0.644E2',18(36)> 

在另一方面BigDecimal#*轉換給定的浮點參數via

if (RB_TYPE_P(r, T_FLOAT)) { 
    b = GetVpValueWithPrec(r, DBL_DIG+1, 1); 
} 

DBL_DIGFloat::DIG的C相等的,所以它基本上是:

BigDecimal(64.4, Float::DIG + 1) 
#=> #<BigDecimal:7fd7cc098408,'0.6440000000 000001E2',27(36)> 

這就是說,你可以得到預期的結果,如果你明確提供精度,eit她說:

f.to_d(16) == 1.to_d * f 
#=> true 

或:

f.to_d == 1.to_d.mult(f, 15) 
#=> true 

經由to_d明確轉換f,當然還有:

f.to_d == 1.to_d * f.to_d 
#=> true 

這是不是一個錯誤?

它看起來像一個,你應該提交一個錯誤報告。

請注意,0.644E20.6440000000000001E2都不是給定浮點數的精確表示。正如已經noted由禮Sadoff,64.4的精確值64.400000000000005684341886080801486968994140625,所以最確切BigDecimal表示是:

BigDecimal('64.400000000000005684341886080801486968994140625') 
#=> #<BigDecimal:7fd7cc04a0c8,'0.6440000000 0000005684 3418860808 0148696899 4140625E2',54(63)> 

IMO,64.4.to_d應該返回這一點。

2

這是不是一個錯誤。 f == f.to_d返回false,因此如果f == 1.to_d * f爲真,那麼f.to_d == 1.to_d * f必須爲false,因爲f != f.to_dBigDecimal==方法旨在比較BigDecimal s不是BigDecimalfloat。有時平等會起作用,但對於某些fBigDecimal表示是準確的,而float則不是。

編輯:有關更多解釋,請參閱Is Floating Point Math Broken

+1

是的,但我認爲'f.to_d == 1.to_d * f'應該*永遠*保持,但它不。就好像「f」有兩種不同的表示方式,即「BigDecimal」。對我而言,這種平等並不總是成立。 –

+0

'64.4'不能準確表示。它代表「64.400000000000005684341886080801486968994140625」。由於在浮點數中沒有確切的表示形式,而有精確的十進制表示形式,所以會出現問題。 –

+0

你實際上可以驗證這一點。設置'f = 64.4',然後測試'f == 64.400000000000005684341886080801486968994140625'。它會返回true。 –

相關問題