如果用PHP的算术计算浮点数的时候,可能会遇到一些计算结果错误的问题
<?php
$a = 0.1;
$b = 0.7;
var_dump(($a + $b) == 0.8); //false
$f = 0.58;
var_dump(intval($f * 100)); //为啥输出57
解决方案:安装bcmath这个扩展
bc是Binary Calculator的缩写。bc*函数的参数都是操作数加上一个可选的 [int scale],比如string bcadd(string $left_operand, string $right_operand[, int $scale]),如果scale没有提供,就用bcscale的缺省值。这里大数直接用一个由0-9组成的string表示,计算结果返回的也是一个string,计算结果不会四舍五入。
<?php
var_dump(bcmul('6978', '0.0816', 4)); //569.4048
var_dump(bcmul('6978', '0.0816', 3)); //569.404
echo bcadd('1.266', '1', 2); //2.26
bcadd — 将两个高精度数字相加
bccomp — 比较两个高精度数字,返回-1, 0, 1
bcdiv — 将两个高精度数字相除
bcmod — 求高精度数字余数
bcmul — 将两个高精度数字相乘
bcpow — 求高精度数字乘方
bcpowmod — 求高精度数字乘方求模,数论里非常常用
bcscale — 配置默认小数点位数
bcsqrt — 求高精度数字平方根
bcsub — 将两个高精度数字相减
/**
* 精度计算四舍五入结果.
*
* @param string $number1 数字1
* @param string $number2 数字2
* @param string $bcMethod 精度计算方法
* @param int $precision 精度
*
* @return float
*/
public function bcCalculateRound($number1, $number2, $bcMethod, $precision = 3)
{
if (!in_array($bcMethod, [ 'bcadd', 'bcsub', 'bcmul', 'bcdiv' ])) {
throw new Exception('unknown bcmath method');
}
$result = call_user_func($bcMethod, $number1, $number2, ++$precision);
return round($result, $precision);
}
/**
* 保留小数点后几位,并且不四舍五入
* $bcscale 保留位数,默认2位
*/
function number_float_format($number, $bcscale = 2) {
$tmp_bcscale = $bcscale + 1;
return sprintf("%.{$bcscale}f", substr(sprintf("%.{$tmp_bcscale}f", $number), 0, -1));
}
/**
* 保留两位小数并向上取整
* $number 保留位数,默认2位
*/
function numberCeilHundred($number) {
return ceil($number * 10000 / 100) / 100;
}
/**
* 多数安全相加函数.
*
* @param array $arr
* @param int $scale
*
* @return bool|string
*/
public static function addFormat(array $arr, int $scale = 3)
{
$result = false;
if (is_array($arr) && count($arr) > 1 && is_int($scale)) {
$result = 0;
foreach ($arr as $value) {
if (is_numeric($value)) {
$result = bcadd($result, $value, $scale);
}
}
}
return $result;
}
/**
* 多数安全相乘法函数.
*
* @param array $arr
* @param int $scale
*
* @return bool|string
*/
public static function mulFormat(array $arr, int $scale = 3)
{
$result = false;
if (is_array($arr) && count($arr) > 1 && is_int($scale)) {
$result = 1;
foreach ($arr as $value) {
if (is_numeric($value)) {
$result = bcmul($result, $value, $scale);
}
}
}
return $result;
}
/**
* 多数安全相除法函数.
*
* @param array $arr
* @param int $scale
*
* @return bool|string
*/
public static function divFormat(array $arr, int $scale = 3)
{
$result = false;
if (is_array($arr) && count($arr) > 1 && is_int($scale)) {
foreach ($arr as $key => $value) {
if (is_numeric($value)) {
if (0 === $key) {
$result = $value;
} elseif (0 != $value) {
$result = bcdiv($result, $value, $scale);
}
}
}
}
return $result;
}
JS浮点数精确计算函数(加,减,乘,除)
<script type="text/javascript">
var Digit = {};
/**
* 四舍五入法截取一个小数
* @param float digit 要格式化的数字
* @param integer length 要保留的小数位数
* @return float
*/
Digit.round = function (digit, length) {
length = length ? parseInt(length) : 0;
if (length <= 0) return Math.round(digit);
digit = Math.round(digit * Math.pow(10, length)) / Math.pow(10, length);
return digit;
};
/**
* 舍去法截取一个小数Digit.floor(1.653,2)
* @param float digit 要格式化的数字
* @param integer length 要保留的小数位数
* @return float
*/
Digit.floor = function (digit, length) {
length = length ? parseInt(length) : 0;
if (length <= 0) return Math.floor(digit);
digit = Math.floor(digit * Math.pow(10, length)) / Math.pow(10, length);
return digit;
};
/**
* 进一法截取一个小数
* @param float digit 要格式化的数字
* @param integer length 要保留的小数位数
* @return float
*/
Digit.ceil = function (digit, length) {
length = length ? parseInt(length) : 0;
if (length <= 0) return Math.ceil(digit);
digit = Math.ceil(digit * Math.pow(10, length)) / Math.pow(10, length);
return digit;
}
//浮点数加法运算
Digit.add = function (arg1, arg2) {
var r1, r2, m;
try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0}
try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0}
m = Math.pow(10, Math.max(r1, r2))
return (arg1 * m + arg2 * m) / m
}
//浮点数减法运算
Digit.sub = function (arg1, arg2) {
var r1, r2, m, n;
try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0}
try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0}
m = Math.pow(10, Math.max(r1, r2));
//动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
//浮点数乘法运算
Digit.mul = function (arg1, arg2) {
var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
try{m+=s1.split(".")[1].length}catch(e){}
try{m+=s2.split(".")[1].length}catch(e){}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m)
}
//浮点数除法运算
Digit.div = function (arg1, arg2) {
var t1=0,t2=0,r1,r2;
try{t1=arg1.toString().split(".")[1].length}catch(e){}
try{t2=arg2.toString().split(".")[1].length}catch(e){}
with(Math){
r1 = Number(arg1.toString().replace(".", ""))
r2 = Number(arg2.toString().replace(".", ""))
return (r1 / r2) * pow(10, t2 - t1);
}
}
/** 这个函数可以添加分隔逗号或者进行四舍五入。
* Usage: (123456.789).number_format(2, '.', ',');
* result: 123,456.79
**/
Number.prototype.number_format = function (decimals, decimal_sep, thousands_sep) {
var n = this,
c = isNaN(decimals) ? 2 : Math.abs(decimals),
d = decimal_sep || '.',
t = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
sign = (n < 0) ? '-' : '',
i = parseInt(n = Math.abs(n).toFixed(c)) + '',
j = ((j = i.length) > 3) ? j % 3 : 0;
return sign + (j ? i.substr(0, j) + t : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : '');
}
/* 给数字添加"st, nd, rd, th"等序数词。
* Usage:
* var myNumOld = 23
* var myNumNew = myNumOld.toOrdinal()
* Result: 23rd
*/
Number.prototype.toOrdinal = function () {
var n = this % 100;
var suffix = ['th', 'st', 'nd', 'rd', 'th'];
var ord = n < 21 ? (n < 4 ? suffix[n] : suffix[0]) : (n % 10 > 4 ? suffix[0] : suffix[n % 10]);
return this + ord;
}
</script>
MySQL浮点计算存在的问题与解决方案
SELECT .1E0 + .2E0 = .3E0;
1.不要使用float、double以及其等价类型做精确计算。如果要在MySQL中做精确计算,推荐使用decimal或者将相关计算任务交给应用。
2、基于应用的解决方案:使用最小单位做字段。比如商品价格涉及到小数,用20.95元表示,那么可以把价格变成以分为单位,即变成2095。
3.查询做转化 cast('字段名' as decimal(18,4)))
select sum(cast(item_value as decimal(18,4))) from items