hutool-python 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hutool/__init__.py +174 -0
- hutool/cache/__init__.py +7 -0
- hutool/cache/cache_util.py +47 -0
- hutool/cache/fifo_cache.py +87 -0
- hutool/cache/lfu_cache.py +129 -0
- hutool/cache/lru_cache.py +93 -0
- hutool/cache/timed_cache.py +115 -0
- hutool/captcha/__init__.py +3 -0
- hutool/captcha/captcha_util.py +215 -0
- hutool/core/__init__.py +23 -0
- hutool/core/_base.py +61 -0
- hutool/core/bean.py +214 -0
- hutool/core/codec.py +111 -0
- hutool/core/coll.py +635 -0
- hutool/core/date.py +1024 -0
- hutool/core/exceptions.py +66 -0
- hutool/core/io/__init__.py +0 -0
- hutool/core/io/data_size_util.py +79 -0
- hutool/core/io/file_name_util.py +111 -0
- hutool/core/io/file_util.py +650 -0
- hutool/core/io/io_util.py +133 -0
- hutool/core/io/path_util.py +247 -0
- hutool/core/io/resource_util.py +137 -0
- hutool/core/map.py +933 -0
- hutool/core/math_util.py +105 -0
- hutool/core/net.py +288 -0
- hutool/core/text/__init__.py +0 -0
- hutool/core/text/csv_util.py +54 -0
- hutool/core/text/str_builder.py +224 -0
- hutool/core/text/unicode_util.py +58 -0
- hutool/core/tree.py +242 -0
- hutool/core/util/__init__.py +63 -0
- hutool/core/util/array_util.py +503 -0
- hutool/core/util/boolean_util.py +124 -0
- hutool/core/util/charset_util.py +60 -0
- hutool/core/util/class_util.py +136 -0
- hutool/core/util/coordinate_util.py +186 -0
- hutool/core/util/credit_code_util.py +110 -0
- hutool/core/util/desensitized_util.py +194 -0
- hutool/core/util/enum_util.py +94 -0
- hutool/core/util/escape_util.py +97 -0
- hutool/core/util/hash_util.py +243 -0
- hutool/core/util/hex_util.py +140 -0
- hutool/core/util/id_util.py +147 -0
- hutool/core/util/idcard_util.py +300 -0
- hutool/core/util/number_util.py +720 -0
- hutool/core/util/object_util.py +294 -0
- hutool/core/util/page_util.py +61 -0
- hutool/core/util/phone_util.py +140 -0
- hutool/core/util/random_util.py +112 -0
- hutool/core/util/re_util.py +231 -0
- hutool/core/util/reflect_util.py +135 -0
- hutool/core/util/runtime_util.py +89 -0
- hutool/core/util/str_util.py +2320 -0
- hutool/core/util/system_util.py +62 -0
- hutool/core/util/url_util.py +232 -0
- hutool/core/util/version_util.py +41 -0
- hutool/core/util/xml_util.py +158 -0
- hutool/core/util/zip_util.py +126 -0
- hutool/cron/__init__.py +4 -0
- hutool/cron/cron_pattern.py +123 -0
- hutool/cron/cron_util.py +115 -0
- hutool/crypto/__init__.py +5 -0
- hutool/crypto/digest_util.py +167 -0
- hutool/crypto/secure_util.py +311 -0
- hutool/crypto/sign_util.py +74 -0
- hutool/dfa/__init__.py +3 -0
- hutool/dfa/sensitive_util.py +114 -0
- hutool/extra/__init__.py +6 -0
- hutool/extra/emoji_util.py +90 -0
- hutool/extra/pinyin_util.py +44 -0
- hutool/extra/qr_code_util.py +58 -0
- hutool/extra/template_util.py +41 -0
- hutool/http/__init__.py +6 -0
- hutool/http/html_util.py +88 -0
- hutool/http/http_request.py +188 -0
- hutool/http/http_response.py +139 -0
- hutool/http/http_util.py +237 -0
- hutool/json_util.py +251 -0
- hutool/jwt_util.py +57 -0
- hutool/setting/__init__.py +5 -0
- hutool/setting/props_util.py +79 -0
- hutool/setting/setting_util.py +80 -0
- hutool/setting/yaml_util.py +45 -0
- hutool_python-1.0.0.dist-info/LICENSE +127 -0
- hutool_python-1.0.0.dist-info/METADATA +438 -0
- hutool_python-1.0.0.dist-info/RECORD +89 -0
- hutool_python-1.0.0.dist-info/WHEEL +5 -0
- hutool_python-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python port of Java Hutool's NumberUtil.
|
|
3
|
+
|
|
4
|
+
提供精确的加减乘除运算、四舍五入、数字格式化、数字判断等常用数字工具方法。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import decimal
|
|
8
|
+
import math
|
|
9
|
+
import random
|
|
10
|
+
import re
|
|
11
|
+
from decimal import Decimal, InvalidOperation
|
|
12
|
+
from typing import Final, List, Literal, Optional, Union
|
|
13
|
+
|
|
14
|
+
from .._base import DefaultParam
|
|
15
|
+
from .str_util import StrUtil
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"NumberUtil",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
# 数字工具类默认参数
|
|
22
|
+
DEFAULT_NUMBER_PARAM = DefaultParam()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class NumberUtil:
|
|
26
|
+
"""
|
|
27
|
+
数字工具类
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# 默认除法运算精度
|
|
31
|
+
DEFAULT_DIV_SCALE: Final[int] = 10
|
|
32
|
+
# 零
|
|
33
|
+
ZERO: Final[Decimal] = Decimal("0")
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def add(*values: Union[int, float, str, None, Decimal]) -> Decimal:
|
|
37
|
+
"""
|
|
38
|
+
提供精确的加法运算
|
|
39
|
+
如果传入多个值为None或者空,则返回0
|
|
40
|
+
:param values: 多个被加值
|
|
41
|
+
:return: 和
|
|
42
|
+
"""
|
|
43
|
+
if not values:
|
|
44
|
+
return NumberUtil.ZERO
|
|
45
|
+
result = NumberUtil.ZERO
|
|
46
|
+
for value in values:
|
|
47
|
+
result += NumberUtil.to_decimal(value)
|
|
48
|
+
return result
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def sub(*values: Union[int, float, str, None, Decimal]) -> Decimal:
|
|
52
|
+
"""
|
|
53
|
+
提供精确的减法运算
|
|
54
|
+
如果传入多个值为None或者空,则返回0
|
|
55
|
+
:param values: 多个被减值
|
|
56
|
+
:return: 差
|
|
57
|
+
"""
|
|
58
|
+
if not values:
|
|
59
|
+
return NumberUtil.ZERO
|
|
60
|
+
result = NumberUtil.to_decimal(values[0])
|
|
61
|
+
for i in range(1, len(values)):
|
|
62
|
+
result -= NumberUtil.to_decimal(values[i])
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def mul(*values: Union[int, float, str, None, Decimal]) -> Decimal:
|
|
67
|
+
"""
|
|
68
|
+
提供精确的乘法运算
|
|
69
|
+
|
|
70
|
+
如果传入多个值为None或者空,则返回0
|
|
71
|
+
|
|
72
|
+
:param values: 多个被乘值
|
|
73
|
+
:return: 积
|
|
74
|
+
"""
|
|
75
|
+
if not values or any(v is None for v in values):
|
|
76
|
+
return NumberUtil.ZERO
|
|
77
|
+
result = NumberUtil.to_decimal(values[0])
|
|
78
|
+
for i in range(1, len(values)):
|
|
79
|
+
result *= NumberUtil.to_decimal(values[i])
|
|
80
|
+
return result
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def div(
|
|
84
|
+
v1: Union[int, float, str, None, Decimal],
|
|
85
|
+
v2: Union[int, float, str, Decimal],
|
|
86
|
+
scale: Union[int, Decimal] = DEFAULT_DIV_SCALE,
|
|
87
|
+
rounding: str = decimal.ROUND_HALF_UP,
|
|
88
|
+
) -> Decimal:
|
|
89
|
+
"""
|
|
90
|
+
提供(相对)精确的除法运算,当发生除不尽的情况时,由scale指定精确度
|
|
91
|
+
:param v1: 被除数
|
|
92
|
+
:param v2: 除数
|
|
93
|
+
:param scale: 精确度,如果为负值,取绝对值, 如果为Decimal, 需要为Decimal('0.00')格式
|
|
94
|
+
:param rounding: 保留小数的模式
|
|
95
|
+
:return: 两个参数的商
|
|
96
|
+
"""
|
|
97
|
+
assert v2 is not None, "Divisor must be not none!"
|
|
98
|
+
if v1 is None:
|
|
99
|
+
return NumberUtil.ZERO
|
|
100
|
+
|
|
101
|
+
v1 = NumberUtil.to_decimal(v1)
|
|
102
|
+
v2 = NumberUtil.to_decimal(v2)
|
|
103
|
+
return NumberUtil.round(v1 / v2, scale, rounding=rounding)
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def ceil_div(v1: int, v2: int) -> int:
|
|
107
|
+
"""
|
|
108
|
+
除法, 向上取整
|
|
109
|
+
:param v1: 被除数
|
|
110
|
+
:param v2: 除数
|
|
111
|
+
:return: 两个参数的商
|
|
112
|
+
"""
|
|
113
|
+
return math.ceil(v1 / v2)
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def round(
|
|
117
|
+
number: Union[int, float, str, None, Decimal],
|
|
118
|
+
scale: Union[int, Decimal],
|
|
119
|
+
rounding: str = decimal.ROUND_HALF_UP,
|
|
120
|
+
) -> Decimal:
|
|
121
|
+
"""
|
|
122
|
+
保留固定位数小数
|
|
123
|
+
:param number: 数字值
|
|
124
|
+
:param scale: 精确度,如果为负值,取绝对值, 如果为Decimal, 需要为Decimal('0.00')格式
|
|
125
|
+
:param rounding: 保留小数的模式
|
|
126
|
+
:return: 新值
|
|
127
|
+
"""
|
|
128
|
+
number = NumberUtil.to_decimal(number)
|
|
129
|
+
|
|
130
|
+
if isinstance(scale, int):
|
|
131
|
+
if scale < 0:
|
|
132
|
+
scale = -scale
|
|
133
|
+
scale_num = scale
|
|
134
|
+
scale = Decimal("0." + "0" * scale)
|
|
135
|
+
else:
|
|
136
|
+
scale_num = len(str(scale)) - 2
|
|
137
|
+
|
|
138
|
+
if decimal.getcontext().prec < scale_num:
|
|
139
|
+
decimal.getcontext().prec = scale_num
|
|
140
|
+
|
|
141
|
+
return number.quantize(scale, rounding=rounding)
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def round_str(
|
|
145
|
+
number: Union[int, float, str, None, Decimal],
|
|
146
|
+
scale: Union[int, Decimal],
|
|
147
|
+
rounding: str = decimal.ROUND_HALF_UP,
|
|
148
|
+
) -> str:
|
|
149
|
+
"""
|
|
150
|
+
保留固定位数小数
|
|
151
|
+
:param number: 数字值
|
|
152
|
+
:param scale: 精确度,如果为负值,取绝对值, 如果为Decimal, 需要为Decimal('0.00')格式
|
|
153
|
+
:param rounding: 保留小数的模式
|
|
154
|
+
:return: 新值
|
|
155
|
+
"""
|
|
156
|
+
return f"{NumberUtil.round(number, scale, rounding=rounding).normalize():f}"
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def round_half_even(
|
|
160
|
+
number: Union[int, float, str, None, Decimal],
|
|
161
|
+
scale: Union[int, Decimal],
|
|
162
|
+
) -> Decimal:
|
|
163
|
+
"""
|
|
164
|
+
四舍六入五成双计算法
|
|
165
|
+
四舍六入五成双是一种比较精确比较科学的计数保留法,是一种数字修约规则。
|
|
166
|
+
算法规则:
|
|
167
|
+
四舍六入五考虑,
|
|
168
|
+
五后非零就进一,
|
|
169
|
+
五后皆零看奇偶,
|
|
170
|
+
五前为偶应舍去,
|
|
171
|
+
五前为奇要进一。
|
|
172
|
+
:param number: 需要科学计算的数据
|
|
173
|
+
:param scale: 精确度,如果为负值,取绝对值, 如果为Decimal, 需要为Decimal('0.00')格式
|
|
174
|
+
:return: 结果
|
|
175
|
+
"""
|
|
176
|
+
return NumberUtil.round(number, scale, rounding=decimal.ROUND_HALF_EVEN)
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def round_down(
|
|
180
|
+
number: Union[int, float, str, None, Decimal],
|
|
181
|
+
scale: Union[int, Decimal],
|
|
182
|
+
) -> Decimal:
|
|
183
|
+
"""
|
|
184
|
+
保留固定小数位数,舍去多余位数
|
|
185
|
+
:param number: 需要科学计算的数据
|
|
186
|
+
:param scale: 保留的小数位
|
|
187
|
+
:return: 结果
|
|
188
|
+
"""
|
|
189
|
+
return NumberUtil.round(number, scale, rounding=decimal.ROUND_DOWN)
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def decimal_format(pattern: str, value: Union[int, float, str, None, Decimal]) -> str:
|
|
193
|
+
"""格式化
|
|
194
|
+
|
|
195
|
+
:param pattern: 格式,例如:
|
|
196
|
+
|
|
197
|
+
- ``'{:.2f}'`` 保留两位小数
|
|
198
|
+
- ``'{:.0f}'`` 取所有整数部分
|
|
199
|
+
- ``'{:.2%}'`` 以百分比方式计数,并取两位小数
|
|
200
|
+
- ``'{:.5e}'`` 显示为科学计数法,并取五位小数
|
|
201
|
+
- ``'{:,}'`` 每三位以逗号进行分隔
|
|
202
|
+
|
|
203
|
+
:param value: 值
|
|
204
|
+
:return: 格式化后的值
|
|
205
|
+
"""
|
|
206
|
+
assert NumberUtil.is_valid(value), "value is NaN or Infinite!"
|
|
207
|
+
return pattern.format(NumberUtil.to_decimal(value))
|
|
208
|
+
|
|
209
|
+
@staticmethod
|
|
210
|
+
def decimal_format_money(value: float) -> str:
|
|
211
|
+
"""
|
|
212
|
+
格式化金额输出,每三位用逗号分隔
|
|
213
|
+
:param value: 金额
|
|
214
|
+
:return: 格式化后的值
|
|
215
|
+
"""
|
|
216
|
+
return NumberUtil.decimal_format("{:,.2f}", value)
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def format_percent(number: float, scale: int) -> str:
|
|
220
|
+
"""
|
|
221
|
+
格式化百分比,小数采用四舍五入方式
|
|
222
|
+
:param number: 值
|
|
223
|
+
:param scale: 保留小数位数
|
|
224
|
+
:return: 百分比
|
|
225
|
+
"""
|
|
226
|
+
return NumberUtil.decimal_format("{:." + str(scale) + "%}", NumberUtil.round(number, scale + 2))
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def is_number(string: str) -> bool:
|
|
230
|
+
"""
|
|
231
|
+
是否为数字,支持包括:
|
|
232
|
+
1、10进制
|
|
233
|
+
2、16进制数字(0x开头, 0x1aF)
|
|
234
|
+
3、科学计数法形式(1234E3)
|
|
235
|
+
4、正负数标识形式(+123、-234)
|
|
236
|
+
:param string: 字符串值
|
|
237
|
+
:return: 是否为数字
|
|
238
|
+
"""
|
|
239
|
+
if StrUtil.is_blank(string):
|
|
240
|
+
return False
|
|
241
|
+
pattern = r"^[+-]?((0[xX][0-9a-fA-F]+)|((\d+\.?\d*|\.\d+)([eE][+-]?\d+)?))$"
|
|
242
|
+
return re.match(pattern, string) is not None
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def is_int(string: Union[str, None]) -> bool:
|
|
246
|
+
"""
|
|
247
|
+
判断string是否是整数
|
|
248
|
+
:param string: 字符串值
|
|
249
|
+
:return: 是否为整数
|
|
250
|
+
"""
|
|
251
|
+
if StrUtil.is_blank(string):
|
|
252
|
+
return False
|
|
253
|
+
try:
|
|
254
|
+
int(string)
|
|
255
|
+
except ValueError:
|
|
256
|
+
return False
|
|
257
|
+
return True
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def is_float(string: str) -> bool:
|
|
261
|
+
"""
|
|
262
|
+
判断字符串是否是浮点数
|
|
263
|
+
:param string: 字符串值
|
|
264
|
+
:return: 是否是浮点数
|
|
265
|
+
"""
|
|
266
|
+
if StrUtil.is_blank(string):
|
|
267
|
+
return False
|
|
268
|
+
try:
|
|
269
|
+
float(string)
|
|
270
|
+
except ValueError:
|
|
271
|
+
return False
|
|
272
|
+
return "." in string
|
|
273
|
+
|
|
274
|
+
@staticmethod
|
|
275
|
+
def is_primes(n: int) -> bool:
|
|
276
|
+
"""
|
|
277
|
+
是否是质数(素数)
|
|
278
|
+
质数表的质数又称素数。指整数在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。
|
|
279
|
+
:param n: 数字
|
|
280
|
+
:return: 是否是质数
|
|
281
|
+
"""
|
|
282
|
+
assert n > 1, "The number must be > 1"
|
|
283
|
+
for i in range(2, int(math.sqrt(n)) + 1):
|
|
284
|
+
if n % i == 0:
|
|
285
|
+
return False
|
|
286
|
+
return True
|
|
287
|
+
|
|
288
|
+
@staticmethod
|
|
289
|
+
def generate_random_number(begin: int, end: int, size: int, seed: Optional[int] = None) -> List[int]:
|
|
290
|
+
"""
|
|
291
|
+
生成不重复随机数 根据给定的最小数字和最大数字,以及随机数的个数,产生指定的不重复的列表
|
|
292
|
+
:param begin: 最小数字(包含该数)
|
|
293
|
+
:param end: 最大数字(不包含该数)
|
|
294
|
+
:param size: 指定产生随机数的个数
|
|
295
|
+
:param seed: 随机种子
|
|
296
|
+
:return: 随机int列表
|
|
297
|
+
"""
|
|
298
|
+
return random.Random(seed).sample(list(range(begin, end)), size)
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def factorial(start: int, end: int = 0) -> int:
|
|
302
|
+
"""
|
|
303
|
+
计算阶乘
|
|
304
|
+
factorial(start, end) = start * (start - 1) * ... * (end + 1)
|
|
305
|
+
:param start: 阶乘起始(包含)
|
|
306
|
+
:param end: 阶乘结束,必须小于起始(不包括)
|
|
307
|
+
:return: 结果
|
|
308
|
+
"""
|
|
309
|
+
assert start is not None, "Factorial start must be not null!"
|
|
310
|
+
assert end is not None, "Factorial end must be not null!"
|
|
311
|
+
if start < 0 or end < 0:
|
|
312
|
+
raise ValueError(f"Factorial start and end both must be > 0, but got start={start}, end={end}")
|
|
313
|
+
|
|
314
|
+
if start == 0 or start == end:
|
|
315
|
+
return 1
|
|
316
|
+
if start < end:
|
|
317
|
+
return 0
|
|
318
|
+
if end < 1:
|
|
319
|
+
end = 1
|
|
320
|
+
|
|
321
|
+
result = start
|
|
322
|
+
end += 1
|
|
323
|
+
while start > end:
|
|
324
|
+
start -= 1
|
|
325
|
+
result *= start
|
|
326
|
+
return result
|
|
327
|
+
|
|
328
|
+
@staticmethod
|
|
329
|
+
def divisor(m: int, n: int) -> int:
|
|
330
|
+
"""
|
|
331
|
+
最大公约数
|
|
332
|
+
:param m: 第一个值
|
|
333
|
+
:param n: 第二个值
|
|
334
|
+
:return: 最大公约数
|
|
335
|
+
"""
|
|
336
|
+
while m % n != 0:
|
|
337
|
+
temp = m % n
|
|
338
|
+
m = n
|
|
339
|
+
n = temp
|
|
340
|
+
return n
|
|
341
|
+
|
|
342
|
+
@staticmethod
|
|
343
|
+
def multiple(m: int, n: int) -> int:
|
|
344
|
+
"""
|
|
345
|
+
最小公倍数
|
|
346
|
+
:param m: 第一个值
|
|
347
|
+
:param n: 第二个值
|
|
348
|
+
:return: 最小公倍数
|
|
349
|
+
"""
|
|
350
|
+
return m * n // NumberUtil.divisor(m, n)
|
|
351
|
+
|
|
352
|
+
@staticmethod
|
|
353
|
+
def get_binary_str(number: int) -> str:
|
|
354
|
+
"""
|
|
355
|
+
获得数字对应的二进制字符串
|
|
356
|
+
:param number: 数字
|
|
357
|
+
:return: 二进制字符串
|
|
358
|
+
"""
|
|
359
|
+
return bin(number)
|
|
360
|
+
|
|
361
|
+
@staticmethod
|
|
362
|
+
def binary_to_int(binary_str: str) -> int:
|
|
363
|
+
"""
|
|
364
|
+
二进制转int
|
|
365
|
+
:param binary_str: 二进制字符串
|
|
366
|
+
:return: int
|
|
367
|
+
"""
|
|
368
|
+
return int(binary_str, 2)
|
|
369
|
+
|
|
370
|
+
@staticmethod
|
|
371
|
+
def compare(num1: Union[int, float, str, Decimal], num2: Union[int, float, str, Decimal]) -> int:
|
|
372
|
+
"""
|
|
373
|
+
比较两个值的大小
|
|
374
|
+
:param num1: 第一个值
|
|
375
|
+
:param num2: 第二个值
|
|
376
|
+
:return: x==y返回0,x<y返回小于0的数,x>y返回大于0的数
|
|
377
|
+
"""
|
|
378
|
+
assert num1 is not None, "first value must not be none!"
|
|
379
|
+
assert num2 is not None, "second value must not be none!"
|
|
380
|
+
return int(NumberUtil.to_decimal(num1).compare(NumberUtil.to_decimal(num2)))
|
|
381
|
+
|
|
382
|
+
@staticmethod
|
|
383
|
+
def is_greater(num1: Union[int, float, str, Decimal], num2: Union[int, float, str, Decimal]) -> bool:
|
|
384
|
+
"""
|
|
385
|
+
比较大小,参数1 > 参数2 返回True
|
|
386
|
+
:param num1: 数字1
|
|
387
|
+
:param num2: 数字2
|
|
388
|
+
:return: 是否大于
|
|
389
|
+
"""
|
|
390
|
+
return NumberUtil.compare(num1, num2) > 0
|
|
391
|
+
|
|
392
|
+
@staticmethod
|
|
393
|
+
def is_greater_or_equal(num1: Union[int, float, str, Decimal], num2: Union[int, float, str, Decimal]) -> bool:
|
|
394
|
+
"""
|
|
395
|
+
比较大小,参数1 >= 参数2 返回True
|
|
396
|
+
:param num1: 数字1
|
|
397
|
+
:param num2: 数字2
|
|
398
|
+
:return: 是否大于等于
|
|
399
|
+
"""
|
|
400
|
+
return NumberUtil.compare(num1, num2) >= 0
|
|
401
|
+
|
|
402
|
+
@staticmethod
|
|
403
|
+
def is_less(num1: Union[int, float, str, Decimal], num2: Union[int, float, str, Decimal]) -> bool:
|
|
404
|
+
"""
|
|
405
|
+
比较大小,参数1 < 参数2 返回True
|
|
406
|
+
:param num1: 数字1
|
|
407
|
+
:param num2: 数字2
|
|
408
|
+
:return: 是否小于
|
|
409
|
+
"""
|
|
410
|
+
return NumberUtil.compare(num1, num2) < 0
|
|
411
|
+
|
|
412
|
+
@staticmethod
|
|
413
|
+
def is_less_or_equal(num1: Union[int, float, str, Decimal], num2: Union[int, float, str, Decimal]) -> bool:
|
|
414
|
+
"""
|
|
415
|
+
比较大小,参数1 <= 参数2 返回True
|
|
416
|
+
:param num1: 数字1
|
|
417
|
+
:param num2: 数字2
|
|
418
|
+
:return: 是否小于等于
|
|
419
|
+
"""
|
|
420
|
+
return NumberUtil.compare(num1, num2) <= 0
|
|
421
|
+
|
|
422
|
+
@staticmethod
|
|
423
|
+
def is_in(
|
|
424
|
+
value: Union[int, float, str, Decimal],
|
|
425
|
+
min_include: Union[int, float, str, Decimal],
|
|
426
|
+
max_include: Union[int, float, str, Decimal],
|
|
427
|
+
) -> bool:
|
|
428
|
+
"""
|
|
429
|
+
检查值是否在指定范围内
|
|
430
|
+
:param value: 值
|
|
431
|
+
:param min_include: 最小值(包含)
|
|
432
|
+
:param max_include: 最大值(包含)
|
|
433
|
+
:return: 经过检查后的值
|
|
434
|
+
"""
|
|
435
|
+
assert value is not None, "value must be not none!"
|
|
436
|
+
assert min_include is not None, "min_include must be not none!"
|
|
437
|
+
assert max_include is not None, "max_include must be not none!"
|
|
438
|
+
return NumberUtil.is_greater_or_equal(value, min_include) and NumberUtil.is_less_or_equal(value, max_include)
|
|
439
|
+
|
|
440
|
+
@staticmethod
|
|
441
|
+
def equals(num1: Union[int, float, str, Decimal], num2: Union[int, float, str, Decimal]) -> bool:
|
|
442
|
+
"""
|
|
443
|
+
比较大小,值相等 返回True
|
|
444
|
+
:param num1: 数字1
|
|
445
|
+
:param num2: 数字2
|
|
446
|
+
:return: 是否相等
|
|
447
|
+
"""
|
|
448
|
+
return NumberUtil.compare(num1, num2) == 0
|
|
449
|
+
|
|
450
|
+
@staticmethod
|
|
451
|
+
def min(*numbers: Union[int, float, str, Decimal]) -> Decimal:
|
|
452
|
+
"""
|
|
453
|
+
取最小值
|
|
454
|
+
:param numbers: 数字数组
|
|
455
|
+
:return: 最小值
|
|
456
|
+
"""
|
|
457
|
+
assert bool(numbers), "Number array must not empty!"
|
|
458
|
+
nums = [NumberUtil.to_decimal(num) for num in numbers]
|
|
459
|
+
return min(*nums)
|
|
460
|
+
|
|
461
|
+
@staticmethod
|
|
462
|
+
def max(*numbers: Union[int, float, str, Decimal]) -> Decimal:
|
|
463
|
+
"""
|
|
464
|
+
取最大值
|
|
465
|
+
:param numbers: 数字数组
|
|
466
|
+
:return: 最大值
|
|
467
|
+
"""
|
|
468
|
+
assert bool(numbers), "Number array must not empty!"
|
|
469
|
+
nums = [NumberUtil.to_decimal(num) for num in numbers]
|
|
470
|
+
return max(*nums)
|
|
471
|
+
|
|
472
|
+
@staticmethod
|
|
473
|
+
def to_str(
|
|
474
|
+
number: Union[int, float, Decimal],
|
|
475
|
+
normalize: bool = True,
|
|
476
|
+
default_value: Optional[str] = None,
|
|
477
|
+
) -> str:
|
|
478
|
+
"""
|
|
479
|
+
数字转字符串
|
|
480
|
+
:param number: 数字
|
|
481
|
+
:param normalize: 是否去除末尾多余0,例如5.0返回5
|
|
482
|
+
:param default_value: 如果number参数为None,返回此默认值
|
|
483
|
+
:return: 字符串
|
|
484
|
+
"""
|
|
485
|
+
if number is None:
|
|
486
|
+
if default_value is None:
|
|
487
|
+
raise ValueError("Number is None!")
|
|
488
|
+
else:
|
|
489
|
+
return default_value
|
|
490
|
+
|
|
491
|
+
assert NumberUtil.is_valid(number), "Number is non-finite!"
|
|
492
|
+
if normalize:
|
|
493
|
+
return f"{NumberUtil.to_decimal(number).normalize():f}"
|
|
494
|
+
return str(number)
|
|
495
|
+
|
|
496
|
+
@staticmethod
|
|
497
|
+
def to_decimal(number: Union[int, float, str, None, Decimal]) -> Decimal:
|
|
498
|
+
"""
|
|
499
|
+
转成Decimal
|
|
500
|
+
float有精度问题,转换为字符串后再转换
|
|
501
|
+
None或""或空白符转换为0
|
|
502
|
+
:param number: 数字
|
|
503
|
+
:return: Decimal
|
|
504
|
+
"""
|
|
505
|
+
if number is None:
|
|
506
|
+
return NumberUtil.ZERO
|
|
507
|
+
|
|
508
|
+
if isinstance(number, int):
|
|
509
|
+
return Decimal(number)
|
|
510
|
+
elif isinstance(number, Decimal):
|
|
511
|
+
return number
|
|
512
|
+
|
|
513
|
+
number = str(number)
|
|
514
|
+
if StrUtil.is_blank(number):
|
|
515
|
+
return NumberUtil.ZERO
|
|
516
|
+
|
|
517
|
+
try:
|
|
518
|
+
return Decimal(number)
|
|
519
|
+
except InvalidOperation:
|
|
520
|
+
# 忽略解析错误
|
|
521
|
+
pass
|
|
522
|
+
|
|
523
|
+
return NumberUtil.to_decimal(str(NumberUtil.parse_float(number)))
|
|
524
|
+
|
|
525
|
+
@staticmethod
|
|
526
|
+
def count(total: int, part: int) -> int:
|
|
527
|
+
"""
|
|
528
|
+
计算等份个数
|
|
529
|
+
:param total: 总数
|
|
530
|
+
:param part: 每份的个数
|
|
531
|
+
:return: 分成了几份
|
|
532
|
+
"""
|
|
533
|
+
return (total // part) if total % part == 0 else (total // part + 1)
|
|
534
|
+
|
|
535
|
+
@staticmethod
|
|
536
|
+
def none_to_zero(number: Union[int, float, str, Decimal]) -> Union[int, float, str, Decimal]:
|
|
537
|
+
"""
|
|
538
|
+
空转0
|
|
539
|
+
:param number: 参数
|
|
540
|
+
:return: 参数为空时返回0的值
|
|
541
|
+
"""
|
|
542
|
+
if number is not None:
|
|
543
|
+
return number
|
|
544
|
+
|
|
545
|
+
if isinstance(number, int):
|
|
546
|
+
return 0
|
|
547
|
+
elif isinstance(number, float):
|
|
548
|
+
return 0.0
|
|
549
|
+
elif isinstance(number, str):
|
|
550
|
+
return "0"
|
|
551
|
+
elif isinstance(number, Decimal):
|
|
552
|
+
return NumberUtil.ZERO
|
|
553
|
+
return 0
|
|
554
|
+
|
|
555
|
+
@staticmethod
|
|
556
|
+
def zero2one(value: int) -> int:
|
|
557
|
+
"""
|
|
558
|
+
如果给定值为0,返回1,否则返回原值
|
|
559
|
+
:param value: 值
|
|
560
|
+
:return: 1或非0值
|
|
561
|
+
"""
|
|
562
|
+
return 1 if value == 0 else value
|
|
563
|
+
|
|
564
|
+
@staticmethod
|
|
565
|
+
def is_beside(number1: Union[int, float, str, Decimal], number2: Union[int, float, str, Decimal]) -> bool:
|
|
566
|
+
"""
|
|
567
|
+
判断两个数字是否相邻,例如1和2相邻,1和3不相邻
|
|
568
|
+
判断方法为做差取绝对值判断是否为1
|
|
569
|
+
:param number1: 数字1
|
|
570
|
+
:param number2: 数字2
|
|
571
|
+
:return: 是否相邻
|
|
572
|
+
"""
|
|
573
|
+
return math.fabs(NumberUtil.to_decimal(number1) - NumberUtil.to_decimal(number2)) == 1
|
|
574
|
+
|
|
575
|
+
@staticmethod
|
|
576
|
+
def part_value(total: int, part_count: int, is_plus_one_when_has_rem: bool = True) -> int:
|
|
577
|
+
"""
|
|
578
|
+
把给定的总数平均分成N份,返回每份的个数
|
|
579
|
+
如果is_plus_one_when_has_rem为True,则当除以分数有余数时每份+1,否则丢弃余数部分
|
|
580
|
+
:param total: 总数
|
|
581
|
+
:param part_count: 份数
|
|
582
|
+
:param is_plus_one_when_has_rem: 在有余数时是否每份+1
|
|
583
|
+
:return: 每份的个数
|
|
584
|
+
"""
|
|
585
|
+
pv = total // part_count
|
|
586
|
+
if is_plus_one_when_has_rem and total % part_count > 0:
|
|
587
|
+
pv += 1
|
|
588
|
+
return pv
|
|
589
|
+
|
|
590
|
+
@staticmethod
|
|
591
|
+
def pow(number: Union[int, float, str, Decimal], n: int) -> Decimal:
|
|
592
|
+
"""
|
|
593
|
+
提供精确的幂运算
|
|
594
|
+
:param number: 底数
|
|
595
|
+
:param n: 指数
|
|
596
|
+
:return: 幂的积
|
|
597
|
+
"""
|
|
598
|
+
return NumberUtil.to_decimal(number) ** n
|
|
599
|
+
|
|
600
|
+
@staticmethod
|
|
601
|
+
def is_power_of_two(n: int) -> bool:
|
|
602
|
+
"""
|
|
603
|
+
判断一个整数是否是2的幂
|
|
604
|
+
:param n: 待验证的整数
|
|
605
|
+
:return: 如果n是2的幂返回True, 反之返回False
|
|
606
|
+
"""
|
|
607
|
+
return n > 0 and n & (n - 1) == 0
|
|
608
|
+
|
|
609
|
+
@staticmethod
|
|
610
|
+
def parse_int(number_str: str, default_value: Union[int, DefaultParam, None] = DEFAULT_NUMBER_PARAM) -> int:
|
|
611
|
+
"""
|
|
612
|
+
解析转换数字字符串为int型数字,规则如下:
|
|
613
|
+
1、0x开头的视为16进制数字
|
|
614
|
+
2、0开头的忽略开头的0
|
|
615
|
+
3、其它情况按照10进制转换
|
|
616
|
+
4、空串返回默认值
|
|
617
|
+
5、.123形式返回0(按照小于0的小数对待)
|
|
618
|
+
6、123.56截取小数点之前的数字,忽略小数部分
|
|
619
|
+
7、返回默认值
|
|
620
|
+
:param number_str: 数字,支持0x开头、0开头和普通十进制
|
|
621
|
+
:param default_value: 返回默认值
|
|
622
|
+
:return: int
|
|
623
|
+
"""
|
|
624
|
+
is_default = isinstance(default_value, DefaultParam)
|
|
625
|
+
if StrUtil.is_blank(number_str):
|
|
626
|
+
return 0 if is_default else default_value
|
|
627
|
+
|
|
628
|
+
try:
|
|
629
|
+
if StrUtil.start_with_ignore_case(number_str, "0x"):
|
|
630
|
+
return int(number_str, 16)
|
|
631
|
+
if StrUtil.contains(number_str, "."):
|
|
632
|
+
number_str = float(number_str)
|
|
633
|
+
return int(number_str)
|
|
634
|
+
except ValueError as ve:
|
|
635
|
+
if is_default:
|
|
636
|
+
raise ve
|
|
637
|
+
else:
|
|
638
|
+
return default_value
|
|
639
|
+
|
|
640
|
+
@staticmethod
|
|
641
|
+
def parse_float(number_str: str, default_value: Union[int, DefaultParam, None] = DEFAULT_NUMBER_PARAM) -> float:
|
|
642
|
+
"""
|
|
643
|
+
解析转换数字字符串为float型数字,规则如下:
|
|
644
|
+
1、0开头的忽略开头的0
|
|
645
|
+
2、空串返回默认值
|
|
646
|
+
3、其它情况按照10进制转换
|
|
647
|
+
:param number_str: 数字,支持0x开头、0开头和普通十进制
|
|
648
|
+
:param default_value: 返回默认值
|
|
649
|
+
:return: float
|
|
650
|
+
"""
|
|
651
|
+
is_default = isinstance(default_value, DefaultParam)
|
|
652
|
+
if StrUtil.is_blank(number_str):
|
|
653
|
+
return 0 if is_default else default_value
|
|
654
|
+
try:
|
|
655
|
+
if StrUtil.start_with_ignore_case(number_str, "0x"):
|
|
656
|
+
return float.fromhex(number_str)
|
|
657
|
+
return float(number_str)
|
|
658
|
+
except ValueError as ve:
|
|
659
|
+
if is_default:
|
|
660
|
+
raise ve
|
|
661
|
+
else:
|
|
662
|
+
return default_value
|
|
663
|
+
|
|
664
|
+
@staticmethod
|
|
665
|
+
def to_bytes(
|
|
666
|
+
value: int,
|
|
667
|
+
length: Optional[int] = None,
|
|
668
|
+
byteorder: Literal["little", "big"] = "big",
|
|
669
|
+
signed: bool = True,
|
|
670
|
+
) -> bytes:
|
|
671
|
+
"""
|
|
672
|
+
int值转bytes
|
|
673
|
+
:param value: 值
|
|
674
|
+
:param length: bytes长度
|
|
675
|
+
:param byteorder: 默认大端字节序(高位字节在前,低位字节在后)
|
|
676
|
+
:param signed: 是否带符号
|
|
677
|
+
:return: bytes
|
|
678
|
+
"""
|
|
679
|
+
if length is None:
|
|
680
|
+
length = (value.bit_length() + 7) // 8
|
|
681
|
+
return value.to_bytes(length, byteorder, signed=signed)
|
|
682
|
+
|
|
683
|
+
@staticmethod
|
|
684
|
+
def to_int(value: bytes, byteorder: Literal["little", "big"] = "big", signed: bool = True) -> int:
|
|
685
|
+
"""
|
|
686
|
+
bytes转int
|
|
687
|
+
:param value: bytes
|
|
688
|
+
:param byteorder: 默认大端字节序(高位字节在前,低位字节在后)
|
|
689
|
+
:param signed: 是否带符号
|
|
690
|
+
:return: int
|
|
691
|
+
"""
|
|
692
|
+
return int.from_bytes(value, byteorder, signed=signed)
|
|
693
|
+
|
|
694
|
+
@staticmethod
|
|
695
|
+
def is_valid(number: Union[int, float, Decimal]) -> bool:
|
|
696
|
+
"""
|
|
697
|
+
检查是否为有效的数字
|
|
698
|
+
检查是否为无限大,或者Not a Number
|
|
699
|
+
:param number: 被检查的数字
|
|
700
|
+
:return: 检查结果,非数字类型和None将返回False
|
|
701
|
+
"""
|
|
702
|
+
return not (number is None or math.isinf(number) or math.isnan(number))
|
|
703
|
+
|
|
704
|
+
@staticmethod
|
|
705
|
+
def is_odd(num: int) -> bool:
|
|
706
|
+
"""
|
|
707
|
+
检查是否为奇数
|
|
708
|
+
:param num: 被判断的数值
|
|
709
|
+
:return: 是否是奇数
|
|
710
|
+
"""
|
|
711
|
+
return (num & 1) == 1
|
|
712
|
+
|
|
713
|
+
@staticmethod
|
|
714
|
+
def is_even(num: int) -> bool:
|
|
715
|
+
"""
|
|
716
|
+
检查是否为偶数
|
|
717
|
+
:param num: 被判断的数值
|
|
718
|
+
:return: 是否是偶数
|
|
719
|
+
"""
|
|
720
|
+
return not NumberUtil.is_odd(num)
|