4

PHP をフロントエンド、MySQL をバックエンドとする小さな金融アプリケーションがあります。私には古くからの偏見があり、通貨の値をセント単位の整数として MySQL に保存しています。私の HTML フォームでは、「156.64」などのドル値を入力できます。PHP を使用してそれをセントに変換し、そのセントをデータベースに保存します。

フォームからドルの値を消去し、セントに変換する関数があります。先頭のテキストを削除し、末尾のテキストを削除し、100 を掛けて整数に変換します。その最後のステップは

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

これは、一貫して 15663 セントに変換される '156.64' のような非常に少数の値を除いて、ほとんどすべてに対して正常に機能します。なぜこれを行うのですか?

私がこれを行う場合:

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

その後、一貫して機能します。その丸め値を追加する必要があるのはなぜですか?

また、金額を浮動小数点値ではなく整数として保存することについての私の偏見は、もう必要ありませんか? 最新のフロート計算は、100% 正確な会計を維持するのに十分な、適切に丸められた正確な金額を生成しますか?

4

9 に答える 9

4

精度が必要な場合は、MySQL でDECIMALデータ型を使用して金額の値を格納する必要があります。

于 2008-10-11T02:12:58.807 に答える
2

フロートに関するあなたの「偏見」は決して克服されることはありません。それはフロートの動作の基本です。あまり詳しく説明しなくても、2 のべき乗に基づいて数値を格納します。すべての 10 進数をこの方法で表示できるわけではないため、常に機能するとは限りません。唯一の信頼できる解決策は、数値を一連の数字と小数点の位置として保存することです (上記の DECIMAL タイプに従って)。

私はPHPで100%ではありませんが、乗算がintをfloatに変換しているため、回避しようとしている問題が正確に発生する可能性はありますか?

于 2008-10-11T02:21:11.117 に答える
2

通貨/お金の値は、フロートとしてデータベースに保存 (またはプログラムで使用) しないでください。

DECIMALNUMERICまたは使用可能な場合はタイプを使用しているため、整数メソッドは問題MONEYありません。

あなたの問題は、 $dollars が float として扱われていることが原因であり、PHP にはお金を処理するためのより良い型がありません。$dollars がいつ割り当てられるかによって、文字列または浮動小数点数として扱われる可能性がありますが、浮動小数点数のように見える場合、* 100 操作の文字列のままであれば確実に浮動小数点数に変換されます。

PHP が行っている暗黙の変換に頼るのではなく、(正規表現を使用して) 自分で文字列を整数の "money" 値に解析する方がよい場合があります。

于 2008-10-11T04:03:14.203 に答える
2

キャストはround()最近接への丸めとは異なり、小数点以下で切り捨てられ(int)3.99ます: yields 3(int)-3.99利回り-3

浮動小数点演算はしばしばエラーを誘発するため (そしておそらく意図した方向にならない可能性があります)、round()信頼できる丸めが必要な場合に使用します。

于 2008-10-11T04:33:30.757 に答える
2

あなたが投稿したコードは、値を整数に変換する前に、最初に乗算を行い、エラーを引き起こす浮動小数点計算を強制します。代わりに、順序を逆にして、浮動小数点演算を完全に回避する必要があります。最初に整数値に変換してから、演算を実行します。

前のコードが既に入力を検証してフォーマットしていると仮定して、これを試してください:

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

お金を表すための浮動小数点値に対するあなたの偏見は、切り捨てと、値が基数 10 から基数 2 に変換され、再び元に戻されるため、十分に根拠があります。

于 2008-10-13T05:31:29.093 に答える
1

予期しない結果が常に得られるため、通貨を浮動小数点に格納しないでください。

php BC Mathsを確認してください。通貨を文字列として保存し、それらに対して非常に高精度の演算を実行できます。

于 2011-01-04T23:00:02.480 に答える
0

使用する代わりに

$セント = (整数) ($ドル * 100);

あなたは使用しようとするかもしれません:

$cents = bcmul($dollars, 100, 2);

于 2009-07-04T05:39:17.410 に答える
0

浮動小数点演算を介して入力する場合、お金を整数として格納しても意味がありません (しゃれは意図されていません)。stringから変換してint「偏見」と一致させたい場合は、単に文字列関数を使用できます。

bcdiv()gmp_div_q()など、任意精度のライブラリを使用して 10 で割ることができます (数値を内部的に文字列として処理します) 。ただし、もちろん、最初からすべての数学に使用することもできます。

または、プレーンな文字列関数を使用できます:

<?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);

?>
于 2010-01-14T13:29:23.730 に答える
0

float から integer に変換する場合、数値はゼロ( src ) に向かって丸められます。

浮動小数点精度の警告を読んでください。

于 2010-01-14T12:31:21.917 に答える