* @version : HUOSDK 8.0 */ namespace huolib\tool; class Math { const DIV_N = 15; /** * 进一法强制保留两位小数 * * @param $num * * @return mixed|string */ public static function showFloatTwoMax($num) { return self::accMul(ceil(self::accMul($num, 100)), 0.01); } /** * 两数相加 * * @param $a * @param $b * * @param int $decimals 保留小数位 * * @return string */ public static function addNum($a, $b, $decimals = 2) { if (!$decimals) { $decimals = 2; } $_n = self::getMaxPoint($a, $b, $decimals); return number_format((number_format($a, $_n, '.', '') + number_format($b, $_n, '.', '')), $decimals, '.', ''); } /** * 两数相加 可正数、负数、小数 * * @param $a * @param $b * * @return string */ public static function addBigNum($a, $b) { $_first_a = substr($a, 0, 1); $_first_b = substr($b, 0, 1); if ('-' == $_first_a && '-' == $_first_b) { return '-'.self::realBigNum(substr($a, 1), substr($b, 1)); } else if ('-' == $_first_a && $_first_b != '-') { return self::addBigMic($b, substr($a, 1)); } else if ('-' == $_first_b && $_first_a != '-') { return self::addBigMic($a, substr($b, 1)); } else if ('-' != $_first_b && $_first_a != '-') { return self::realBigNum($a, $b); } } /** * 两个大数相加 可带小数 * * @param $a * @param $b * * @return string */ public static function realBigNum($a, $b) { $_la = ''; $_la_l = 0; if (strpos($a, '.')) { $_arr_a = explode('.', $a); if (isset($_arr_a[1])) { $_la_l = strlen($_arr_a[1]); $_la = $_arr_a[1]; } $_la_n = $_arr_a[0]; } else { $_la_n = $a; $_la = ''; $_la_l = 0; } $_lb = ''; $_lb_l = 0; if (strpos($b, '.')) { $_arr_b = explode('.', $b); if (isset($_arr_b[1])) { $_lb_l = strlen($_arr_b[1]); $_lb = $_arr_b[1]; } $_lb_n = $_arr_b[0]; } else { $_lb_n = $b; $_lb = ''; $_lb_l = 0; } /* 整数相加 */ $_lr_n = self::realAdd($_la_n, $_lb_n); /* 小数同位 */ if ($_la_l && $_lb_l) { if ($_la_l > $_lb_l) { $_lb = str_pad($_lb, $_la_l, '0', STR_PAD_RIGHT); } elseif ($_lb_l > $_la_l) { $_la = str_pad($_la, $_lb_l, '0', STR_PAD_RIGHT); } $_lr_low = self::realAdd($_la, $_lb); $_lr_long = strlen($_lr_low); $_lr_a = strlen($_la); if ($_lr_long > $_lr_a) { $_real_low = substr($_lr_low, $_lr_long - $_lr_a); $_real_low_n = substr($_lr_low, 0, $_lr_long - $_lr_a); $_real_n = self::realAdd($_lr_n, $_real_low_n); return $_real_n.'.'.$_real_low; } return $_lr_n.'.'.$_lr_low; } elseif ($_la_l && !$_lb_l) { return $_lr_n.'.'.$_la; } elseif ($_lb_l && !$_la_l) { return $_lr_n.'.'.$_lb; } elseif (!$_lb_l && !$_la_l) { return $_lr_n; } } /** * 两个大数相减 过滤首尾无用0 * * @param $a * @param $b * * @return string */ public static function addBigMic($a, $b) { $_re = self::realBigMic($a, $b); if (strpos($_re, '-') === 0) { $_return_str = ltrim(substr($_re, 1), '0'); if (strpos($_re, '.') === 0) { return '-0'.rtrim($_return_str); } else { return '-'.$_return_str; } } $_return_str = ltrim($_re, '0'); if (strpos($_return_str, '.') === 0) { return '0'.rtrim($_return_str); } else { return $_return_str; } } /** * 两个大数相减 保留首尾无用0 * * @param $a * @param $b * * @return string */ public static function realBigMic($a, $b) { $_la = ''; $_la_l = 0; if (strpos($a, '.')) { $_arr_a = explode('.', $a); if (isset($_arr_a[1])) { $_la_l = strlen($_arr_a[1]); $_la = $_arr_a[1]; } $_la_n = $_arr_a[0]; } else { $_la_n = $a; $_la = ''; $_la_l = 0; } $_lb = ''; $_lb_l = 0; if (strpos($b, '.')) { $_arr_b = explode('.', $b); if (isset($_arr_b[1])) { $_lb_l = strlen($_arr_b[1]); $_lb = $_arr_b[1]; } $_lb_n = $_arr_b[0]; } else { $_lb_n = $b; $_lb = ''; $_lb_l = 0; } /* 整数相减 */ $_lr_n = self::realMic($_la_n, $_lb_n); /* 小数同位 */ if ($_la_l && $_lb_l) { if ($_la_l > $_lb_l) { $_lb = str_pad($_lb, $_la_l, '0', STR_PAD_RIGHT); } elseif ($_lb_l > $_la_l) { $_la = str_pad($_la, $_lb_l, '0', STR_PAD_RIGHT); } $_lr_low = self::realMic($_la, $_lb); $_lr_long = strlen($_lr_low); $_lr_a = strlen($_la); if ($_lr_long > $_lr_a) { $_real_low = substr($_lr_low, $_lr_long - $_lr_a); $_real_n = self::realMic($_lr_n, 1); return $_real_n.'.'.$_real_low; } return $_lr_n.'.'.$_lr_low; } elseif ($_la_l && !$_lb_l) { return $_lr_n.'.'.$_la; } elseif ($_lb_l && !$_la_l) { return $_lr_n.'.'.$_lb; } elseif (!$_lb_l && !$_la_l) { return $_lr_n; } } /** * 两数相加 * * @param $a * @param $b * * @return string */ public static function realAdd($a, $b) { $a_str = $a.''; $b_str = $b.''; $m = strlen($a_str); $n = strlen($b_str); $num = $m > $n ? $m : $n; $result = ''; $flag = 0; while ($num--) { $t1 = 0; $t2 = 0; if ($m > 0) { $t1 = $a_str[--$m]; } if ($n > 0) { $t2 = $b_str[--$n]; } $t = $t1 + $t2 + $flag; if ($t > 9) { $flag = 1; $t = $t - 10; } else { $flag = 0; } $result = $t.$result; } if ($flag) { $result = $flag.$result; } return $result; } /** * 两数相减 * * @param $a * @param $b * * @return string */ public static function realMic($a, $b) { $a_str = $a.''; $b_str = $b.''; $m = strlen($a_str); $n = strlen($b_str); if ($m < $n) { $_need_flag = true; $a_str = $b.''; $_rm = $m; $m = $n; $n = $_rm; unset($_rm); $b_str = $a.''; } else { $_need_flag = false; } $num = $m > $n ? $m : $n; $result = ''; $flag = 0; while ($num--) { $t1 = 0; $t2 = 0; if ($m > 0) { $t1 = $a_str[--$m]; } if ($n > 0) { $t2 = $b_str[--$n]; } $_mt = $t2 + $flag; if ($t1 < $_mt) { $t = $t1 + 10 - $_mt; $flag = 1; } else { $t = $t1 - $_mt; $flag = 0; } $result = $t.$result; } if ($flag) { $result = '-'.$result; } if ($_need_flag) { $result = '-'.$result; } return $result; } /** * 获取指定位数的随机数字 * * @param int $num 位数 * @param bool $first_zero 是否首位不为零 * * @return string */ public static function getRandNum($num = 1, $first_zero = false) { if (empty($num)) { return 0; } $return_str = ''; for ($i = 0; $i < $num; $i++) { if ($i == 0 && $first_zero) { $return_str .= mt_rand(1, 9); } else { $return_str .= mt_rand(0, 9); } } return $return_str; } /** * 获取随机手机号码 * * @return string */ public static function getRandMobile() { return '1'.self::getRandNum(10, true); } /** * 获取随机时间 * * @param int $before 时间上限 * @param int $after 时间下限 * * @return false|string */ public static function getRandTime($before = 0, $after = 0) { if (empty($before) && empty($after)) { /* 随机0到rand_Max位数的随机数 */ return date('Y-m-d H:i:s', self::getRandNum(mt_rand(0, 20))); } else { if ($before - $after > 1) { return date('Y-m-d H:i:s', $after + intval(mt_rand(0, $before - $after - 1))); } else { if ($before > $after) { return date('Y-m-d H:i:s', mt_rand($after, $before)); } else { return date('Y-m-d H:i:s'); } } } } /** * 获取两数最大的小数点位数 * * @param $a * @param $b * @param int $n 默认位数 * * @return int */ public static function getMaxPoint($a, $b, $n = 2) { $_n = $n; if (strpos($a, '.') || strpos($b, '.')) { $_al = self::getNumPoint($a); if ($_al > $_n) { $_n = $_al; } $_bl = self::getNumPoint($b); if ($_bl > $_n) { $_n = $_bl; } } return $_n; } /** * 获取一个数的小数位数 * * @param $a * * @return int */ public static function getNumPoint($a) { if (strpos($a, '.')) { $_arr_a = explode('.', $a); if (isset($_arr_a[1])) { return strlen($_arr_a[1]); } } return 0; } /** * 两数相减 * * @param $a * @param $b * * @param int $decimals 保留位数 * * @return string */ public static function addMic($a, $b, $decimals = 2) { if (!$decimals) { $decimals = 2; } $_n = self::getMaxPoint($a, $b, $decimals); return number_format((number_format($a, $_n, '.', '') - number_format($b, $_n, '.', '')), $decimals, '.', ''); } /** * 两个数精确相乘 小学生算法 * * @param $strA * @param $strB * * @return mixed|string */ public static function accMul($strA, $strB) { $m = 0; $_al = self::getNumPoint($strA); $A = $strA; if ($_al) { $m = $m + $_al; $A = str_replace('.', '', $strA); } $B = $strB; $_bl = self::getNumPoint($strB); if ($_bl) { $m = $m + $_bl; $B = str_replace('.', '', $strB); } $sResult = ""; //反转字符串 $A = strrev($A); $B = strrev($B); $_a_len = strlen($A); $_b_len = strlen($B); $_ab_len = $_a_len + $_b_len; //建立temp变量 $flag = array(); for ($i = 0; $i < ($_ab_len + 1); $i++) { $flag[$i] = "0"; } //依次相乘叠加 for ($i = 0; $i < $_a_len; $i++) { for ($j = 0; $j < $_b_len; $j++) { $flag[$i + $j] = $flag[$i + $j] + (intval($A[$i]) * intval($B[$j])) % 10; $flag[$i + $j + 1] = $flag[$i + $j + 1] + (int)((intval($A[$i]) * intval($B[$j])) / 10); } } //再次相乘叠加 for ($i = 0; $i < count($flag) - 1; $i++) { $flag[$i + 1] = $flag[$i + 1] + (int)(intval($flag[$i]) / 10); $flag[$i] = intval($flag[$i]) % 10; } //去除高位无用的0; $mark = 0; for ($i = count($flag) - 1; $i >= 0; $i--) { if ($flag[$i] != 0 && $mark == 0) { $mark = $i; } if ($mark != 0) { $sResult = $sResult.$flag[$i]; } } if ($sResult == '' && $flag[0] != '') { $sResult = $flag[0]; } if ($m) { $rel = strlen($sResult); if ($m < $rel) { return substr($sResult, 0, $rel - $m).'.'.substr($sResult, -$m); } else { if ($m == $rel) { return '0.'.$sResult; } else { $re = '0.'; for ($i = 0; $i < $m - $rel; $i++) { $re .= '0'; } return $re.$sResult; } } } else { return $sResult; } } /** * 高精度除法 php 自带 * * @param $a * @param $b * @param int $n *$a=bcdiv("111111111111111111","1"); * * @return string */ public static function divNum($a, $b, $n = 3) { return bcdiv($a, $b, $n); } /** * 任意精度除法 a/b 保留n 位小数 用php bcdiv方法 * * @param $a * @param $b * @param $n * * @return string */ public static function phpDivNum($a, $b, $n = 0) { if (!$n) { $n = self::DIV_N; } $_str_a = $a.''; $_str_b = $b.''; /* 先计算符号 */ $_a_one = substr($a, 0, 1); $_b_one = substr($b, 0, 1); $_need_flag = false; if ('-' == $_a_one && '-' == $_b_one) { $_str_a = substr($a, 1); $_str_b = substr($b, 1); $b = $_str_b; $a = $_str_a; } elseif ('-' == $_a_one && '-' != $_b_one) { $_str_a = substr($a, 1); $a = $_str_a; $_need_flag = true; } elseif ('-' != $_a_one && '-' == $_b_one) { $_str_b = substr($b, 1); $b = $_str_b; $_need_flag = true; } elseif ('-' != $_a_one && '-' != $_b_one) { } /* 先计算符号 */ /* 对齐小数点同时去掉小数点 start */ $_an = self::getNumPoint($a); $_bn = self::getNumPoint($b); if ($_an > $_bn) { $_point_str_l = strlen($_str_b) + $_an - $_bn; $_str_b = str_pad($_str_b, $_point_str_l, '0', STR_PAD_RIGHT); if ($_an > $n) { $n = $_an + 1; } } elseif ($_bn > $_an) { $_point_str_l = strlen($_str_a) + $_bn - $_an; $_str_a = str_pad($_str_a, $_point_str_l, '0', STR_PAD_RIGHT); if ($_bn > $n) { $n = $_bn + 1; } } $_str_b = str_replace('.', '', $_str_b); $_str_a = str_replace('.', '', $_str_a); /* 对齐小数点同时去掉小数点 end */ $_do_a = str_pad($_str_a, strlen($_str_a) + $n, '0', STR_PAD_RIGHT); $return_str = self::realDivNum($_do_a, $_str_b, $n); $_re_z = str_pad('0.', 1 + $n, '0', STR_PAD_RIGHT).'1'; if ($_need_flag) { return '-'.self::accMul($return_str, $_re_z); } else { return self::accMul($return_str, $_re_z); } } /** * 用php函数计算两数相除精确到 n 个位 * * @param $a * @param $b * @param int $n * * @return string */ public static function realDivNum($a, $b, $n = 0) { if (!$n) { $n = self::DIV_N; } return self::divNum($a, $b, $n); } /** * 用自己写的小生除法进行计算两个数相除 保留$n 位小数 * * @param $a * @param $b * @param int $n * * @return mixed|string */ public static function bigDivNum($a, $b, $n = 0) { $_str_a = $a.''; $_str_b = $b.''; if (!$n) { $_n = self::DIV_N; } else { $_n = $n + 1; } /* 先计算符号 */ $_a_one = substr($a, 0, 1); $_b_one = substr($b, 0, 1); $_need_flag = false; if ('-' == $_a_one && '-' == $_b_one) { $_str_a = substr($a, 1); $_str_b = substr($b, 1); $b = $_str_b; $a = $_str_a; } elseif ('-' == $_a_one && '-' != $_b_one) { $_str_a = substr($a, 1); $a = $_str_a; $_need_flag = true; } elseif ('-' != $_a_one && '-' == $_b_one) { $_str_b = substr($b, 1); $b = $_str_b; $_need_flag = true; } elseif ('-' != $_a_one && '-' != $_b_one) { } /* 先计算符号 */ /* 对齐小数点同时去掉小数点 start */ $_an = self::getNumPoint($a); $_bn = self::getNumPoint($b); if ($_an > $_bn) { $_point_str_l = strlen($_str_b) + $_an - $_bn; $_str_b = str_pad($_str_b, $_point_str_l, '0', STR_PAD_RIGHT); if ($_an > $_n) { $_n = $_an + 1; } } elseif ($_bn > $_an) { $_point_str_l = strlen($_str_a) + $_bn - $_an; $_str_a = str_pad($_str_a, $_point_str_l, '0', STR_PAD_RIGHT); if ($_bn > $_n) { $_n = $_bn + 1; } } $_str_b = str_replace('.', '', $_str_b); $_str_a = str_replace('.', '', $_str_a); /* 对齐小数点同时去掉小数点 end */ $_do_a = str_pad($_str_a, strlen($_str_a) + $_n, '0', STR_PAD_RIGHT); $return_arr = self::getDevFist($_do_a, $_str_b, $_n); $_re_z = str_pad('0.', 1 + $_n, '0', STR_PAD_RIGHT).'1'; if ($_need_flag) { return '-'.self::accMul($return_arr['q'], $_re_z); } else { return self::accMul($return_arr['q'], $_re_z); } } /** * 两数相除 * 获得 商和余数 * remainder 余数 quotient 商 * * @param $a * @param $b * * @param int $n * * @return array */ public static function getDevFist($a, $b, $n = 0) { $_n = $n + 1; $_str_la = strlen($a); $_str_lb = strlen($b); if (!($_str_la > $_str_lb)) {/* $a的位数大于 $b的位数 商0 余数为a */ return array( 'q' => '0', 'r' => $a, ); } $_mir = $_str_la - $_str_lb; if ($_mir < 2) { return self::getDevRealFist($a, $b); } else { $_str_a = $a; $_str_b = $b; $_r = str_pad('1', $_mir, '0', STR_PAD_RIGHT); $_new_b = self::accMul($_str_b, $_r); $_r_r_arr = self::getDevRealFist($_str_a, $_new_b); /* 取得第一位商 */ $_result = self::accMul($_r_r_arr['q'], $_r); $_result_r = $_r_r_arr['r']; $_rr_lr = strlen($_result_r); if (!($_rr_lr > 1)) {/* 没有余数 */ return array( 'q' => $_result, 'r' => $_result_r, ); } $_mir_r = $_rr_lr - $_str_lb; while ($_mir_r > 1 && ($_n > 0)) { $_r = str_pad('1', $_mir_r, '0', STR_PAD_RIGHT); $_new_b = self::accMul($b, $_r); $_r_r_arr = self::getDevRealFist($_result_r, $_new_b); /* 取得第一位商 */ $_result = self::addBigNum($_result, self::accMul($_r_r_arr['q'], $_r)); $_result_r = $_r_r_arr['r']; $_rr_lr = strlen($_result_r); if (!($_rr_lr > 1)) {/* 没有余数 */ return array( 'q' => $_result, 'r' => $_result_r, ); } $_mir_r = $_rr_lr - $_str_lb; $_n = $_n - 1; } $_return_rr = self::getDevRealFist($_result_r, $b); $_result = self::addBigNum($_result, $_return_rr['q']); return array( 'q' => $_result, 'r' => $_return_rr['r'], ); } } /** * 获取位数差不大于两位的两数相除的商和余数 * * @param $a 被除数 * @param $b 除数 * * @return array * array( * ' * ) */ public static function getDevRealFist($a, $b) { $_str_la = strlen($a); $_str_lb = strlen($b); if ($_str_la < $_str_lb) {/* $a的位数大于 $b的位数 商0 余数为a */ return array( 'q' => '0', 'r' => $a, ); } $_str_a = $a.''; $_str_b = $b.''; $_a_one = substr($_str_a, 0, 1); $_b_one = substr($_str_b, 0, 1); if ($_str_la == $_str_lb) { if ($_a_one < $_b_one) { return array( 'q' => '0', 'r' => $a, ); } elseif ($_a_one == $_b_one) { $_q_test = self::addBigMic($_str_a, $_str_b); if ('-' == substr($_q_test, 0, 1)) { return array( 'q' => '0', 'r' => $a, ); } else { return array( 'q' => '1', 'r' => $_q_test, ); } } elseif ($_a_one > $_b_one) { $_q_test = floor($_a_one / $_b_one); if (1 == $_q_test) { $_q_r = self::addBigMic($_str_a, $_str_b); return array( 'q' => '1', 'r' => $_q_r, ); } else { $_q_r = self::addBigMic($_str_a, self::accMul($_str_b, $_q_test)); while ('-' == substr($_q_r, 0, 1)) { $_q_test = $_q_test - 1; $_q_r = self::addBigMic($_str_a, self::accMul($_str_b, $_q_test)); } return array( 'q' => $_q_test, 'r' => $_q_r, ); } } } elseif ($_str_la > $_str_lb) { $_min_r = $_str_la - $_str_lb; if (1 == $_min_r) {/* 被除数比除数多一位 */ $_a_two = substr($_str_a, 0, 2); $_q_test = floor($_a_two / $_b_one); if (1 == $_q_test) { $_q_r = self::addBigMic($_str_a, $_str_b); return array( 'q' => '1', 'r' => $_q_r, ); } else { $_q_r = self::addBigMic($_str_a, self::accMul($_str_b, $_q_test)); while ('-' == substr($_q_r, 0, 1)) { $_q_test = $_q_test - 1; $_q_r = self::addBigMic($_str_a, self::accMul($_str_b, $_q_test)); } return array( 'q' => $_q_test, 'r' => $_q_r, ); } } else {/* 被除数比除数多两位以上 */ return array( 'q' => 'error', 'r' => 'a比b多两位或2位以上', ); } } } }