[英]PHP Rounding Float
我正在開發一個系統,我需要四舍五入到最接近的一分錢財務付款。 我天真地以為我會乘以 100,發言,然后再除以。 但是,以下示例行為不端:
echo 1298.34*100;
正確顯示:
129834
但
echo floor(1298.34*100);
意外顯示:
129833
例如,我使用intval
遇到了同樣的問題。
我懷疑乘法與浮點舍入有關。 但是如果我不能依賴乘法,我該怎么做呢? 我總是想可靠地四舍五入,我不需要考慮負數。
需要明確的是,我希望剝離任何零碎的一分錢:
1298.345 should give 1298.34
1298.349 should give 1298.34
1298.342 should give 1298.34
您可以使用round()
舍入到所需的精度,並在舍入最后 5 時具有預期的行為(這是您可能遇到的另一個財務障礙)。
$display = round(3895.0 / 3.0, 2);
另外,提醒一下,我習慣用最后一個點或“.0”寫浮點整數。 這可以防止某些語言推斷錯誤的類型並執行 integer 除法,因此 5 / 3 將產生 1 。
如果您需要“自定義舍入”並且想要確定它不起作用的原因是因為並非所有浮點數都存在於機器表示中。 1298.34 不存在; 確實存在的東西(我正在制作精確的數字。)代替它的可能是 1298.33999999999999124。
所以當你乘以 100 得到 129833.999999999999124 時,當然截斷它會得到 129833。
然后你需要做的是添加一個小數量,它必須足以彌補機器錯誤,但不足以影響財務計算。 有一種算法可以確定這個數量,但你可能會得到“放大后的千分之一”。
所以:
$display = floor((3895.0 / 3.0)*100.0 + 0.001);
請注意,您將“看到”為 1234.56 的這個數字可能再次不精確存在。 它可能真的是 1234.5600000000000123 或 1234.559999999999876。 這可能會對復雜的復合計算產生影響。
由於您提到您僅將其用於顯示目的,您可以獲取金額,將其轉換為字符串並截斷小數點后第二位的任何內容。 正則表達式可以完成這項工作:
preg_match('/\d+\.{0,1}\d{0,2}/', (string) $amount, $matches);
此表達式適用於任意數量的小數(包括零)。 詳細工作原理:
\d+
匹配任意位數\.{0,1}
匹配 0 或 1 個文字點\d{0,2}
匹配點后的零位或兩位數您可以運行以下代碼對其進行測試:
$amounts = [
1298,
1298.3,
1298.34,
1298.341,
1298.349279745,
];
foreach ($amounts as $amount) {
preg_match('/\d+\.{0,1}\d{0,2}/', (string) $amount, $matches);
var_dump($matches[0]);
}
在這個小提琴中也可以作為現場測試。
由於您正在使用金融,您應該使用某種貨幣庫( https://github.com/moneyphp/money )。 幾乎所有其他解決方案都在自找麻煩。
我不推薦的其他方法是:a)僅使用整數,b)使用bcmath
計算或 c)使用 Money 庫中的 Number class 例如:
function getMoneyValue($value): string
{
if (!is_numeric($value)) {
throw new \RuntimeException(sprintf('Money value has to be a numeric value, "%s" given', is_object($value) ? get_class($value) : gettype($value)));
}
$number = \Money\Number::fromNumber($value)->base10(-2);
return $number->getIntegerPart();
}
其他可用的 function 是 round(),它采用兩個參數 - 要舍入的數字和要舍入的小數位數。 如果一個數字恰好在兩個整數之間,round() 將始終向上取整。
使用圓形:
echo round (1298.34*100);
結果:
129834
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.