|
|
@@ -3,12 +3,12 @@
|
|
|
namespace UCore\Helper;
|
|
|
|
|
|
/**
|
|
|
- * 数字助手类
|
|
|
+ * 万分位数字助手类
|
|
|
*
|
|
|
- * 提供数字格式化、转换等功能
|
|
|
+ * 专注于万分位和中文数位单位的数字格式化、转换功能
|
|
|
* 支持高精度数值处理(20位整数 + 20位小数,共40位精度)
|
|
|
*/
|
|
|
-class Number
|
|
|
+class NumberWan
|
|
|
{
|
|
|
/** @var int 默认小数精度 */
|
|
|
const DEFAULT_SCALE = 20;
|
|
|
@@ -25,7 +25,7 @@ class Number
|
|
|
/** @var int 万分位转换的基数(向后兼容) */
|
|
|
const WAN_BASE = 10000;
|
|
|
/**
|
|
|
- * 万分位数据表示转换(高精度版本)
|
|
|
+ * 万分位数据表示转换
|
|
|
*
|
|
|
* 将数字转换为中文万分位表示法,支持40位精度(20位整数+20位小数)
|
|
|
* 例如:100020 -> "10万20"
|
|
|
@@ -34,7 +34,7 @@ class Number
|
|
|
* @param int $scale 小数精度,默认20位
|
|
|
* @return string 万分位表示的字符串
|
|
|
*/
|
|
|
- public static function formatToWanPrecision($number, int $scale = self::DEFAULT_SCALE): string
|
|
|
+ public static function formatToWan($number, int $scale = self::DEFAULT_SCALE): string
|
|
|
{
|
|
|
// 确保输入为字符串格式,保持精度
|
|
|
$numStr = (string) $number;
|
|
|
@@ -188,98 +188,12 @@ class Number
|
|
|
return $numStr;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 万分位数据表示转换(兼容版本)
|
|
|
- *
|
|
|
- * 将数字转换为中文万分位表示法
|
|
|
- * 例如:100020 -> "10万20"
|
|
|
- *
|
|
|
- * @param int|float|string $number 要转换的数字
|
|
|
- * @return string 万分位表示的字符串
|
|
|
- */
|
|
|
- public static function formatToWan($number): string
|
|
|
- {
|
|
|
- // 转换为数字类型
|
|
|
- $num = (float) $number;
|
|
|
-
|
|
|
- // 处理负数
|
|
|
- $isNegative = $num < 0;
|
|
|
- $num = abs($num);
|
|
|
-
|
|
|
- // 处理小数部分
|
|
|
- $integerPart = (int) $num;
|
|
|
- $decimalPart = $num - $integerPart;
|
|
|
-
|
|
|
- $result = '';
|
|
|
-
|
|
|
- // 处理整数部分的万分位转换
|
|
|
- if ($integerPart >= 10000) {
|
|
|
- $wan = (int) ($integerPart / 10000);
|
|
|
- $remainder = $integerPart % 10000;
|
|
|
-
|
|
|
- $result = $wan . '万';
|
|
|
-
|
|
|
- // 如果余数不为0,添加余数
|
|
|
- if ($remainder > 0) {
|
|
|
- $result .= $remainder;
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 小于1万的直接显示
|
|
|
- $result = (string) $integerPart;
|
|
|
- }
|
|
|
-
|
|
|
- // 处理小数部分(如果有)
|
|
|
- if ($decimalPart > 0) {
|
|
|
- // 保留2位小数,去除末尾的0
|
|
|
- $decimalStr = rtrim(sprintf('%.2f', $decimalPart), '0');
|
|
|
- $decimalStr = rtrim($decimalStr, '.');
|
|
|
- if ($decimalStr !== '0' && $decimalStr !== '') {
|
|
|
- $result .= substr($decimalStr, 1); // 去掉开头的"0"
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 添加负号
|
|
|
- if ($isNegative) {
|
|
|
- $result = '-' . $result;
|
|
|
- }
|
|
|
-
|
|
|
- return $result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 格式化数字为千分位表示
|
|
|
- *
|
|
|
- * @param int|float|string $number 要格式化的数字
|
|
|
- * @param int $decimals 小数位数,默认0
|
|
|
- * @return string 千分位格式的字符串
|
|
|
- */
|
|
|
- public static function formatThousands($number, int $decimals = 0): string
|
|
|
- {
|
|
|
- return number_format((float) $number, $decimals);
|
|
|
- }
|
|
|
+
|
|
|
|
|
|
- /**
|
|
|
- * 智能数字格式化
|
|
|
- *
|
|
|
- * 根据数字大小自动选择合适的格式化方式
|
|
|
- *
|
|
|
- * @param int|float|string $number 要格式化的数字
|
|
|
- * @param bool $useWan 是否使用万分位表示,默认true
|
|
|
- * @return string 格式化后的字符串
|
|
|
- */
|
|
|
- public static function smartFormat($number, bool $useWan = true): string
|
|
|
- {
|
|
|
- $num = (float) $number;
|
|
|
-
|
|
|
- if ($useWan && abs($num) >= 10000) {
|
|
|
- return self::formatToWan($number);
|
|
|
- }
|
|
|
-
|
|
|
- return self::formatThousands($number);
|
|
|
- }
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
- * 将万分位表示转换回数字(高精度版本)
|
|
|
+ * 将万分位表示转换回数字
|
|
|
*
|
|
|
* 例如:"10万20" -> "100020"
|
|
|
*
|
|
|
@@ -287,7 +201,7 @@ class Number
|
|
|
* @param int $scale 小数精度,默认20位
|
|
|
* @return string 转换后的数字字符串(保持高精度)
|
|
|
*/
|
|
|
- public static function parseFromWanPrecision(string $wanString, int $scale = self::DEFAULT_SCALE): string
|
|
|
+ public static function parseFromWan(string $wanString, int $scale = self::DEFAULT_SCALE): string
|
|
|
{
|
|
|
// 处理负数
|
|
|
$isNegative = str_starts_with($wanString, '-');
|
|
|
@@ -295,16 +209,21 @@ class Number
|
|
|
$wanString = substr($wanString, 1);
|
|
|
}
|
|
|
|
|
|
+ // 分离整数和小数部分
|
|
|
+ $parts = explode('.', $wanString);
|
|
|
+ $integerPart = $parts[0];
|
|
|
+ $decimalPart = isset($parts[1]) ? $parts[1] : '';
|
|
|
+
|
|
|
// 查找"万"字符
|
|
|
- $wanPos = mb_strpos($wanString, '万');
|
|
|
+ $wanPos = mb_strpos($integerPart, '万');
|
|
|
|
|
|
if ($wanPos === false) {
|
|
|
// 没有"万"字符,直接返回
|
|
|
- $result = $wanString;
|
|
|
+ $result = $integerPart;
|
|
|
} else {
|
|
|
// 有"万"字符,分别处理万和余数部分
|
|
|
- $wanPart = mb_substr($wanString, 0, $wanPos);
|
|
|
- $remainderPart = mb_substr($wanString, $wanPos + 1);
|
|
|
+ $wanPart = mb_substr($integerPart, 0, $wanPos);
|
|
|
+ $remainderPart = mb_substr($integerPart, $wanPos + 1);
|
|
|
|
|
|
// 使用BC数学函数进行高精度计算
|
|
|
$wanValue = bcmul($wanPart, (string) self::WAN_BASE, $scale);
|
|
|
@@ -313,6 +232,17 @@ class Number
|
|
|
$result = bcadd($wanValue, $remainder, $scale);
|
|
|
}
|
|
|
|
|
|
+ // 处理小数部分
|
|
|
+ if (!empty($decimalPart)) {
|
|
|
+ $decimalPart = rtrim($decimalPart, '0');
|
|
|
+ if (!empty($decimalPart)) {
|
|
|
+ // 确保结果没有重复的小数点
|
|
|
+ if (strpos($result, '.') === false) {
|
|
|
+ $result .= '.' . $decimalPart;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 处理负数
|
|
|
if ($isNegative) {
|
|
|
$result = bcmul($result, '-1', $scale);
|
|
|
@@ -322,7 +252,7 @@ class Number
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 将中文数位单位表示转换回数字(高精度版本)
|
|
|
+ * 将中文数位单位表示转换回数字
|
|
|
*
|
|
|
* 支持万、亿、兆、京、垓等中文数位单位的反向转换
|
|
|
* 例如:"1234垓5678京9012兆3456亿7890万" -> "12345678901234567890000000000000"
|
|
|
@@ -394,112 +324,21 @@ class Number
|
|
|
return $result;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 将万分位表示转换回数字(兼容版本)
|
|
|
- *
|
|
|
- * 例如:"10万20" -> 100020
|
|
|
- *
|
|
|
- * @param string $wanString 万分位表示的字符串
|
|
|
- * @return int|float 转换后的数字
|
|
|
- */
|
|
|
- public static function parseFromWan(string $wanString)
|
|
|
- {
|
|
|
- // 处理负数
|
|
|
- $isNegative = str_starts_with($wanString, '-');
|
|
|
- if ($isNegative) {
|
|
|
- $wanString = substr($wanString, 1);
|
|
|
- }
|
|
|
|
|
|
- // 查找"万"字符
|
|
|
- $wanPos = mb_strpos($wanString, '万');
|
|
|
|
|
|
- if ($wanPos === false) {
|
|
|
- // 没有"万"字符,直接转换为数字
|
|
|
- $result = (float) $wanString;
|
|
|
- } else {
|
|
|
- // 有"万"字符,分别处理万和余数部分
|
|
|
- $wanPart = mb_substr($wanString, 0, $wanPos);
|
|
|
- $remainderPart = mb_substr($wanString, $wanPos + 1);
|
|
|
-
|
|
|
- $wan = (float) $wanPart;
|
|
|
- $remainder = empty($remainderPart) ? 0 : (float) $remainderPart;
|
|
|
|
|
|
- $result = $wan * 10000 + $remainder;
|
|
|
- }
|
|
|
-
|
|
|
- return $isNegative ? -$result : $result;
|
|
|
- }
|
|
|
|
|
|
/**
|
|
|
- * 高精度千分位格式化
|
|
|
- *
|
|
|
- * @param int|float|string $number 要格式化的数字
|
|
|
- * @param int $decimals 小数位数,默认20位
|
|
|
- * @return string 千分位格式的字符串
|
|
|
- */
|
|
|
- public static function formatThousandsPrecision($number, int $decimals = self::DEFAULT_SCALE): string
|
|
|
- {
|
|
|
- $numStr = (string) $number;
|
|
|
-
|
|
|
- // 分离整数和小数部分
|
|
|
- $parts = explode('.', $numStr);
|
|
|
- $integerPart = $parts[0];
|
|
|
- $decimalPart = isset($parts[1]) ? $parts[1] : '';
|
|
|
-
|
|
|
- // 处理负数
|
|
|
- $isNegative = str_starts_with($integerPart, '-');
|
|
|
- if ($isNegative) {
|
|
|
- $integerPart = substr($integerPart, 1);
|
|
|
- }
|
|
|
-
|
|
|
- // 手动为整数部分添加千分位分隔符(避免精度丢失)
|
|
|
- $formattedInteger = self::addThousandsSeparator($integerPart);
|
|
|
-
|
|
|
- // 处理小数部分
|
|
|
- if (!empty($decimalPart) && $decimals > 0) {
|
|
|
- // 截取小数位
|
|
|
- if (strlen($decimalPart) > $decimals) {
|
|
|
- $decimalPart = substr($decimalPart, 0, $decimals);
|
|
|
- }
|
|
|
-
|
|
|
- // 去除末尾的0
|
|
|
- $decimalPart = rtrim($decimalPart, '0');
|
|
|
-
|
|
|
- if (!empty($decimalPart)) {
|
|
|
- $formattedInteger .= '.' . $decimalPart;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return $isNegative ? '-' . $formattedInteger : $formattedInteger;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 手动添加千分位分隔符(避免精度丢失)
|
|
|
- *
|
|
|
- * @param string $number 整数字符串
|
|
|
- * @return string 添加千分位分隔符的字符串
|
|
|
- */
|
|
|
- private static function addThousandsSeparator(string $number): string
|
|
|
- {
|
|
|
- // 从右到左每3位添加逗号
|
|
|
- $reversed = strrev($number);
|
|
|
- $chunks = str_split($reversed, 3);
|
|
|
- $formatted = implode(',', $chunks);
|
|
|
- return strrev($formatted);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 高精度智能格式化
|
|
|
+ * 智能格式化
|
|
|
*
|
|
|
* 根据数字大小自动选择合适的格式化方式
|
|
|
*
|
|
|
* @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, bool $useChineseUnits = false): string
|
|
|
+ public static function smartFormat($number, int $scale = self::DEFAULT_SCALE, bool $useChineseUnits = false): string
|
|
|
{
|
|
|
$numStr = (string) $number;
|
|
|
|
|
|
@@ -516,11 +355,12 @@ class Number
|
|
|
}
|
|
|
|
|
|
// 使用万分位表示
|
|
|
- if ($useWan && bccomp($integerPart, (string) self::WAN_BASE, 0) >= 0) {
|
|
|
- return self::formatToWanPrecision($number, $scale);
|
|
|
+ if (bccomp($integerPart, (string) self::WAN_BASE, 0) >= 0) {
|
|
|
+ return self::formatToWan($number, $scale);
|
|
|
}
|
|
|
|
|
|
- return self::formatThousandsPrecision($number, $scale);
|
|
|
+ // 小于万的数字直接返回
|
|
|
+ return $numStr;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -533,7 +373,7 @@ class Number
|
|
|
* @param int $decimalDigits 小数位数限制,默认20位
|
|
|
* @return bool 是否符合精度要求
|
|
|
*/
|
|
|
- public static function validatePrecision(string $number, int $integerDigits = 20, int $decimalDigits = 20): bool
|
|
|
+ public static function validate(string $number, int $integerDigits = 20, int $decimalDigits = 20): bool
|
|
|
{
|
|
|
// 移除负号进行验证
|
|
|
$numStr = ltrim($number, '-');
|
|
|
@@ -566,7 +406,7 @@ class Number
|
|
|
* @param int $decimalDigits 小数位数限制,默认20位
|
|
|
* @return string 截取后的数字
|
|
|
*/
|
|
|
- public static function truncatePrecision(string $number, int $integerDigits = 20, int $decimalDigits = 20): string
|
|
|
+ public static function truncate(string $number, int $integerDigits = 20, int $decimalDigits = 20): string
|
|
|
{
|
|
|
// 处理负数
|
|
|
$isNegative = str_starts_with($number, '-');
|