|
|
@@ -13,7 +13,16 @@ class Number
|
|
|
/** @var int 默认小数精度 */
|
|
|
const DEFAULT_SCALE = 20;
|
|
|
|
|
|
- /** @var int 万分位转换的基数 */
|
|
|
+ /** @var array 中文数位单位配置 */
|
|
|
+ const CHINESE_UNITS = [
|
|
|
+ 'GAI' => ['name' => '垓', 'value' => '100000000000000000000'], // 10^20
|
|
|
+ 'JING' => ['name' => '京', 'value' => '10000000000000000'], // 10^16
|
|
|
+ 'ZHAO' => ['name' => '兆', 'value' => '1000000000000'], // 10^12
|
|
|
+ 'YI' => ['name' => '亿', 'value' => '100000000'], // 10^8
|
|
|
+ 'WAN' => ['name' => '万', 'value' => '10000'], // 10^4
|
|
|
+ ];
|
|
|
+
|
|
|
+ /** @var int 万分位转换的基数(向后兼容) */
|
|
|
const WAN_BASE = 10000;
|
|
|
/**
|
|
|
* 万分位数据表示转换(高精度版本)
|
|
|
@@ -76,6 +85,109 @@ class Number
|
|
|
return $result;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 中文大数位单位转换(高精度版本)
|
|
|
+ *
|
|
|
+ * 支持万、亿、兆、京、垓等中文数位单位
|
|
|
+ * 例如:12345678901234567890 -> "1234垓5678京9012兆3456亿7890万"
|
|
|
+ *
|
|
|
+ * @param int|float|string $number 要转换的数字
|
|
|
+ * @param int $scale 小数精度,默认20位
|
|
|
+ * @param bool $simplify 是否简化显示(只显示最大单位),默认false
|
|
|
+ * @return string 中文数位单位表示的字符串
|
|
|
+ */
|
|
|
+ public static function formatToChineseUnits($number, int $scale = self::DEFAULT_SCALE, bool $simplify = false): string
|
|
|
+ {
|
|
|
+ // 确保输入为字符串格式,保持精度
|
|
|
+ $numStr = (string) $number;
|
|
|
+
|
|
|
+ // 处理负数
|
|
|
+ $isNegative = bccomp($numStr, '0', $scale) < 0;
|
|
|
+ if ($isNegative) {
|
|
|
+ $numStr = bcmul($numStr, '-1', $scale);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分离整数和小数部分
|
|
|
+ $parts = explode('.', $numStr);
|
|
|
+ $integerPart = $parts[0];
|
|
|
+ $decimalPart = isset($parts[1]) ? $parts[1] : '';
|
|
|
+
|
|
|
+ $result = '';
|
|
|
+ $remainingNumber = $integerPart;
|
|
|
+
|
|
|
+ // 按照从大到小的顺序处理各个单位
|
|
|
+ foreach (self::CHINESE_UNITS as $unitKey => $unitInfo) {
|
|
|
+ $unitValue = $unitInfo['value'];
|
|
|
+ $unitName = $unitInfo['name'];
|
|
|
+
|
|
|
+ if (bccomp($remainingNumber, $unitValue, 0) >= 0) {
|
|
|
+ $unitCount = bcdiv($remainingNumber, $unitValue, 0);
|
|
|
+ $remainingNumber = bcmod($remainingNumber, $unitValue);
|
|
|
+
|
|
|
+ $result .= $unitCount . $unitName;
|
|
|
+
|
|
|
+ // 如果是简化模式,只显示最大单位
|
|
|
+ if ($simplify) {
|
|
|
+ // 如果还有余数,显示余数
|
|
|
+ if (bccomp($remainingNumber, '0', 0) > 0) {
|
|
|
+ $result .= $remainingNumber;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有匹配到任何单位,或者还有余数,直接显示数字
|
|
|
+ if (empty($result) || (!$simplify && bccomp($remainingNumber, '0', 0) > 0)) {
|
|
|
+ $result .= $remainingNumber;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理小数部分(如果有)
|
|
|
+ if (!empty($decimalPart)) {
|
|
|
+ $decimalPart = rtrim($decimalPart, '0');
|
|
|
+ if (!empty($decimalPart)) {
|
|
|
+ $result .= '.' . $decimalPart;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加负号
|
|
|
+ if ($isNegative) {
|
|
|
+ $result = '-' . $result;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 智能中文数位单位转换
|
|
|
+ *
|
|
|
+ * 根据数字大小自动选择合适的中文单位显示
|
|
|
+ *
|
|
|
+ * @param int|float|string $number 要转换的数字
|
|
|
+ * @param int $scale 小数精度,默认20位
|
|
|
+ * @return string 智能选择的中文数位单位表示
|
|
|
+ */
|
|
|
+ public static function formatToSmartChineseUnits($number, int $scale = self::DEFAULT_SCALE): string
|
|
|
+ {
|
|
|
+ $numStr = (string) $number;
|
|
|
+
|
|
|
+ // 获取绝对值进行判断
|
|
|
+ $absNum = str_replace('-', '', $numStr);
|
|
|
+ $parts = explode('.', $absNum);
|
|
|
+ $integerPart = $parts[0];
|
|
|
+
|
|
|
+ // 根据数字大小选择显示方式
|
|
|
+ foreach (self::CHINESE_UNITS as $unitKey => $unitInfo) {
|
|
|
+ if (bccomp($integerPart, $unitInfo['value'], 0) >= 0) {
|
|
|
+ // 使用简化模式显示最大单位
|
|
|
+ return self::formatToChineseUnits($number, $scale, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 小于万的数字直接显示
|
|
|
+ return $numStr;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 万分位数据表示转换(兼容版本)
|
|
|
*
|
|
|
@@ -209,6 +321,79 @@ class Number
|
|
|
return $result;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 将中文数位单位表示转换回数字(高精度版本)
|
|
|
+ *
|
|
|
+ * 支持万、亿、兆、京、垓等中文数位单位的反向转换
|
|
|
+ * 例如:"1234垓5678京9012兆3456亿7890万" -> "12345678901234567890000000000000"
|
|
|
+ *
|
|
|
+ * @param string $chineseString 中文数位单位表示的字符串
|
|
|
+ * @param int $scale 小数精度,默认20位
|
|
|
+ * @return string 转换后的数字字符串(保持高精度)
|
|
|
+ */
|
|
|
+ public static function parseFromChineseUnits(string $chineseString, int $scale = self::DEFAULT_SCALE): string
|
|
|
+ {
|
|
|
+ // 处理负数
|
|
|
+ $isNegative = str_starts_with($chineseString, '-');
|
|
|
+ if ($isNegative) {
|
|
|
+ $chineseString = substr($chineseString, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分离整数和小数部分
|
|
|
+ $parts = explode('.', $chineseString);
|
|
|
+ $integerPart = $parts[0];
|
|
|
+ $decimalPart = isset($parts[1]) ? $parts[1] : '';
|
|
|
+
|
|
|
+ $result = '0';
|
|
|
+ $remainingString = $integerPart;
|
|
|
+
|
|
|
+ // 按照从大到小的顺序处理各个单位
|
|
|
+ foreach (self::CHINESE_UNITS as $unitInfo) {
|
|
|
+ $unitValue = $unitInfo['value'];
|
|
|
+ $unitName = $unitInfo['name'];
|
|
|
+
|
|
|
+ $unitPos = mb_strpos($remainingString, $unitName);
|
|
|
+ if ($unitPos !== false) {
|
|
|
+ // 提取单位前的数字
|
|
|
+ $beforeUnit = mb_substr($remainingString, 0, $unitPos);
|
|
|
+ $afterUnit = mb_substr($remainingString, $unitPos + 1);
|
|
|
+
|
|
|
+ if (!empty($beforeUnit)) {
|
|
|
+ // 计算该单位的值
|
|
|
+ $unitTotal = bcmul($beforeUnit, $unitValue, $scale);
|
|
|
+ $result = bcadd($result, $unitTotal, $scale);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新剩余字符串
|
|
|
+ $remainingString = $afterUnit;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理剩余的数字(没有单位的部分)
|
|
|
+ if (!empty($remainingString) && is_numeric($remainingString)) {
|
|
|
+ $result = bcadd($result, $remainingString, $scale);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理小数部分
|
|
|
+ if (!empty($decimalPart)) {
|
|
|
+ // 去除末尾的0
|
|
|
+ $decimalPart = rtrim($decimalPart, '0');
|
|
|
+ if (!empty($decimalPart)) {
|
|
|
+ // 确保结果是正确的小数格式
|
|
|
+ if (strpos($result, '.') === false) {
|
|
|
+ $result .= '.' . $decimalPart;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理负数
|
|
|
+ if ($isNegative) {
|
|
|
+ $result = bcmul($result, '-1', $scale);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 将万分位表示转换回数字(兼容版本)
|
|
|
*
|
|
|
@@ -311,17 +496,27 @@ class Number
|
|
|
* @param int|float|string $number 要格式化的数字
|
|
|
* @param bool $useWan 是否使用万分位表示,默认true
|
|
|
* @param int $scale 小数精度,默认20位
|
|
|
+ * @param bool $useChineseUnits 是否使用中文数位单位(万、亿、兆、京、垓),默认false
|
|
|
* @return string 格式化后的字符串
|
|
|
*/
|
|
|
- public static function smartFormatPrecision($number, bool $useWan = true, int $scale = self::DEFAULT_SCALE): string
|
|
|
+ public static function smartFormatPrecision($number, bool $useWan = true, int $scale = self::DEFAULT_SCALE, bool $useChineseUnits = false): string
|
|
|
{
|
|
|
$numStr = (string) $number;
|
|
|
|
|
|
// 获取整数部分进行比较
|
|
|
$parts = explode('.', $numStr);
|
|
|
- $integerPart = $parts[0];
|
|
|
+ $integerPart = str_replace('-', '', $parts[0]);
|
|
|
+
|
|
|
+ // 如果启用中文数位单位,优先使用
|
|
|
+ if ($useChineseUnits) {
|
|
|
+ // 检查是否达到万的级别
|
|
|
+ if (bccomp($integerPart, (string) self::WAN_BASE, 0) >= 0) {
|
|
|
+ return self::formatToSmartChineseUnits($number, $scale);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if ($useWan && bccomp(str_replace('-', '', $integerPart), (string) self::WAN_BASE, 0) >= 0) {
|
|
|
+ // 使用万分位表示
|
|
|
+ if ($useWan && bccomp($integerPart, (string) self::WAN_BASE, 0) >= 0) {
|
|
|
return self::formatToWanPrecision($number, $scale);
|
|
|
}
|
|
|
|