php intval () ve zemin () dönüş değeri, bu çok düşük?

5 Cevap php

PHP float veri türü yanlış olduğunu ve MySQL bir HALKA bir INT daha fazla yer kaplıyor (ve yanlış) Çünkü, ben her zaman hassas tam 2 ondalık basamak sağlamak için saklamadan önce 100 ile çarparak, IntS olarak fiyatları saklayın . Ancak ben PHP istenmeyen davranış olduğuna inanıyorum. Örnek kod:

echo "<pre>";

$price = "1.15";
echo "Price = ";
var_dump($price);

$price_corrected = $price*100;
echo "Corrected price = ";
var_dump($price_corrected);


$price_int = intval(floor($price_corrected));
echo "Integer price = ";
var_dump($price_int);

echo "</pre>";

Üretilen çıktı:

Price = string(4) "1.15"
Corrected price = float(115)
Integer price = int(114)

Ben şaşırdım. Nihai sonuç 1 ile beklenenden daha düşük iken, benim test çıktısı daha fazla benzemeye bekliyordum:

Price = string(4) "1.15"
Corrected price = float(114.999999999)
Integer price = int(114)

şamandıra türü yanlışlık göstermek hangi. Ama neden kat (115) 114 dönen olduğunu?

5 Cevap

Hızlı bir düzeltme olarak bu deneyin:

$price_int = intval(floor($price_corrected + 0.5));

Karşılaştığınız sorun, kayan nokta aritmetiği ile gerçek sayıları kullanarak tüm programlama dilleri benzer sorunları var, PHP'nin hatası değildir.

Parasal hesaplamalar için başparmak genel kural yüzer (veritabanındaki ne de komut ne) kullanmak asla etmektir. Sen her zaman yerine dolar sent saklayarak her türlü sorunları önleyebilirsiniz. Sent tamsayı ve özgürce birlikte ekleyebilirsiniz, ve diğer tamsayılar ile çarpabilirsiniz. Eğer numarasını görüntülemek zaman, son iki basamak önünde bir nokta eklemek emin olun.

Yerine 115 114 alıyorsanız neden nedeni floor, böylece kat, en yakın tam sayıya doğru, aşağı yuvarlar (114,999999999) 114 olur olmasıdır. * 100 1.15 114,999999999 yerine 115 neden daha ilginç bir soru. Bunun nedeni, 1.15 tam olarak 115/100 değil, 100 ile çarpın eğer, 115 daha küçük biraz daha küçük bir sayı olsun bu yüzden, çok daha az olmasıdır.

İşte echo 1.15 * 100; ne daha ayrıntılı bir açıklama:

  • Bu ikili kayan nokta sayısına 1.15 ayrıştırır. Bu 1.15 yakın ikili kayan nokta sayısını almak için biraz aşağı yuvarlamak olur, yuvarlama içerir. Eğer (yuvarlama hata olmaksızın) bir tam sayı alamayan neden nedeni 1.15 baz 2 sayıların sonsuz sayıda olmasıdır.
  • Bu ikili kayan nokta sayısına 100 ayrıştırır. Bu yuvarlama içerir, ancak 100 küçük bir tam sayı olduğu, yuvarlama hata sıfırdır.
  • Bu, önceki iki sayının çarpımını hesaplar. Bu, aynı zamanda en yakın ikili kayan nokta sayısını bulmak için, biraz yuvarlama içerir. Yuvarlama hatası bu operasyon sıfır olur.
  • Bir nokta ile bir taban 10 ondalık sayıya ikili kayan nokta sayısını dönüştürür ve baskılar bu temsili. Bu da biraz yuvarlama içerir.

PHP şaşırtıcı yazdıran nedeni Corrected price = float(115) (yerine 114,999 arasında ...) var_dump (!) Tam sayısını yazdırmak, ama n yuvarlanır numarasını yazar olmamasıdır - 2 (n - 1) n basamaklı hesaplama hassas olan rakam. Bunu kolayca doğrulayabilirsiniz:

echo 1.15 * 100;  # this prints 115
printf("%.30f", 1.15 * 100);  # you 114.999....
echo 1.15 * 100 == 115.0 ? "same" : "different";  # this prints `different'
echo 1.15 * 100 < 115.0 ? "less" : "not-less";    # this prints `less'

Katları baskı yapıyorsanız, hatırlıyorum: you don't always see all digits when you print the float.

Ayrıca PHP float docs başlangıcına yakın büyük uyarıyı bkz.

Diğer cevaplar sorunun nedenini ve iyi bir çözüm örtülü var, ben inanıyorum.

Farklı bir açıdan sorunu gidermekle hedefliyoruz için:

MySQL fiyat değerlerini saklamak için, muhtemelen ondalık kesin değerleri saklamanıza olanak DECIMAL type, bakmak gerekir.

PHP önemli rakama dayanarak yuvarlama yapıyor. Bu (on line 2) tutarsızlığını saklanıyor. Zemin birlikte gelince tabii ki, bu daha iyi bilmiyor ve tüm yol aşağı LOP'lar.

Intval mi (number_format ($ problematic_float, 0)); Bu "sorun" için başka bir olası çözüm?

Bu PHP başına bir sorun değil belirtildiği gibi, o kadar yuvarlanırken sonlu kayan nokta değerleri dolayısıyla karakterinin kaybına neden olarak ifade edilemez kesirleri işleme bir sorunu daha fazladır.

Çözüm sağlamak olduğunu size Kayan nokta değerleri üzerinde çalışan ve doğruluğunu sağlamak için gerektiğinde - bcpow, bcmul ark - gmp işlevleri veya BC matematik fonksiyonlarını kullanın. ve sorun kolayca çözülecektir.

E.g instead of $price_corrected = $price*100;

kullanmak $ price_corrected = bcmul ($ fiyat, 100);