Hata tamsayı PHP para dize dönüştürme

10 Cevap php

Ben arka uç olarak ön uç ve MySQL gibi PHP ile küçük bir finans uygulaması var. Ben eski önyargıları var, ve ben sent bir tamsayı olarak MySQL para değerleri depolamak. Benim HTML formları "156,64" gibi, dolar değerlerin girilmesine izin verir ve ben sent dönüştürmek olduğunu PHP kullanmak ve sonra veritabanında sent saklayın.

Ben her iki formdan dolar değer temizler ve sent dönüştüren bir işlevi var. Ben önde gelen metin şerit, ben sondaki metin şerit, ben 100 ile çarpın ve bir tamsayı dönüştürmek. Bu son adımdır

$cents = (integer) ($dollars * 100);

Bu sürekli 15663 sent dönüştüren '156 .64 'gibi çok az değerler haricinde hemen hemen her şey için çalışıyor. Neden bunu yapıyor?

Eğer bunu yaparsam:

$cents = (integer) ($dollars * 100 + 0.5);

Daha sonra sürekli olarak çalışır. Neden o yuvarlama değeri eklemek gerekiyor?

Ayrıca, tamsayılar olarak para miktarlarda depolanması ve değerler, kayan noktalı değil hakkında benim önyargılar, artık gerekli olduğunu? Modern şamandıra hesaplamalar% 100 doğru muhasebe tutmak için güzel yuvarlak ve doğru para değerleri yeterli üretecek?

10 Cevap

Eğer hassasiyet isterseniz, MySQL DECIMAL veri türünü kullanarak para değerleri saklamak gerekir.

Şamandıralar hakkında "önyargılar" üstesinden asla - bu işe şekilde temel bulunuyor. Çok fazla detaya girmeden, onlar iki güçlere dayanan bir dizi depolamak ve tüm ondalık sayı bu şekilde sunulabilir beri, her zaman çalışmıyor. Sizin tek güvenilir çözüm rakam dizisi ve (yukarıda belirtilen DECIMAL'ın) başı olarak ondalık noktasının konumu olarak numarayı saklamak için.

Ben PHP üzerinde% 100 değilim, ama çarpma şamandıralara İnts dönüştürme ve dolayısıyla tam olarak önlemek için çalışıyoruz sorunu tanıtan mümkün mü?

Döviz / para değerleri bir veritabanında saklanan (veya bir programında kullanılan) su baskını gibi asla.

Bir DECIMAL, NUMERIC veya MONEY Müsait yazın olduğu gibi tamsayı yöntemi, gayet iyi.

Senin sorunun bir float olarak tedavi edilen ve PHP para ile başa çıkmak için daha iyi bir türü yok $ dolar neden olur. $ Dolar tahsis edilirken bağlı olarak, bir dize veya bir float olarak tedavi edilebilir, ama bir şamandıra gibi görünüyor eğer hala * 100 operasyon için bir dize ise kesinlikle bir float dönüştürülür.

Siz kendinizi yerine PHP yapıyor örtülü dönüşüm dayanarak (a regex kullanarak) bir tamsayı "para" değer dize ayrıştırma daha iyi olabilir.

Deftere kod bir tamsayı değeri dönüştürmeden önce, hata tanıtır bir kayan nokta hesaplama zorlayarak, ilk çarpma yapar. Bunun yerine, sırası ters çevrilerek tamamen kayan noktalı aritmetik kaçınmalısınız. İlk tamsayı değerlere dönüştürmek, daha sonra aritmetik gerçekleştirin.

Bu deneyin, önceki kod zaten onaylanmış ve giriş biçimlendirilmiş varsayarak:

list($bills, $pennies) = explode('.', $dollars);
$cents = 100 * $bills + $pennies;

Parayı temsil kayan nokta değeri karşı önyargı iyi çünkü kesilmesi nedeniyle ve tekrar temel-2 tabanından-10 dönüştürülen değerleri ve kurulmuştur.

Döküm etmez round() gibi yuvarlak-yakın, bu truncates ondalık at: (int)3.99 verimleri 3. (int)-3.99 verimleri -3.

Şamandıra aritmetik çoğunlukla (istediğiniz yönde ve muhtemelen değil) hatası neden beri güvenilir yuvarlama istiyorsanız, round() kullanın.

O her zaman beklemeyin sonuç almak çünkü hiç, kayan nokta birimini saklamak asla.

Php Çıkış BC Maths, bu onlara çok yüksek hassasiyetli aritmetik gerçekleştirmek ardından, dize olarak para saklamak için izin verir.

Kullanmak yerine

$ Cent = (integer) ($ dolar * 100);

Kullanmak denemek isteyebilirsiniz:

$ Cent = bcmul ($ dolar, 100, 2);

Tamsayı float dönüştürürken, sayı yuvarlanır olacak towards zero (src).

Floating point precision uyarıyı okuyun.

Eğer (hayır cinas amaçlanan) bir kayan nokta işlemi ile girerseniz tamsayı olarak para saklamak hiçbir anlamı yok. Eğer int için string dönüştürmek ve "önyargı" ile tutarlı olmak istiyorsanız, sadece dize işlevlerini kullanabilirsiniz.

Sen (onlar dizeleri olarak dahili numaralarını işlemek) 10 ile bölmek için bir keyfi hassas kütüphane kullanımı, örneğin bcdiv() veya gmp_div_q(), ama tabii ki, aynı zamanda tüm matematik için başından itibaren bunu kullanmış olabilir.

Veya düz dize işlevlerini kullanabilirsiniz:

<?php
// Quick ugly code not fully tested
$input = '156.64';
$output = NULL;

if( preg_match('/\d+(\.\d+)?/', $input) ){
    $tmp = explode('.', $input);
    switch( count($tmp) ){
        case 1:
            $output = $tmp[0];
            break;

        case 2:
            $output = $tmp[0] . substr($tmp[1], 0, 2);
            break;

        default:
            echo "Invalid decimal\n";
    }
}else{
    echo "Invalid number\n";
}

var_dump($output);

?>

Bunun yerine (tamsayı) ve (int) kullanın, ben aynı şeyi başkalarına öneririm.