将金额 (int) 转换为 BCD
Convert amount (int) to BCD
我需要将 Int 左填充 6 个字节(数量)转换为 Python 中的 BCD。
int = 145
expect = "\x00\x00\x00\x00\x01\x45"
我最接近的是这段代码(但它需要以字节对循环):
def TO_BCD(value):
return chr((((value / 10) << 4) & 0xF0) + ((value % 10) & 0x0F))
int = 145
TO_BCD(int) # => "\x00\x00\x00\x00\x01\x45" (expected)
这是一个例子。
script0.py:
#!/usr/bin/env python3
import sys
def bcd(value, length=0, pad='\x00'):
ret = ""
while value:
value, ls4b = divmod(value, 10)
value, ms4b = divmod(value, 10)
ret = chr((ms4b << 4) + ls4b) + ret
return pad * (length - len(ret)) + ret
def bcd_str(value, length=0, pad='\x00'):
value_str = str(value)
value_str = ("0" if len(value_str) % 2 else "") + value_str
ret = ""
for i in range(0, len(value_str), 2):
ms4b = ord(value_str[i]) - 0x30
ls4b = ord(value_str[i + 1]) - 0x30
ret += chr((ms4b << 4) + ls4b)
return pad * (length - len(ret)) + ret
def main():
values = [
145,
5,
123456,
]
for value in values:
print("{0:d} - [{1:s}] - [{2:s}]".format(value, repr(bcd(value, length=6)), repr(bcd_str(value, length=6))))
# Bonus
speed_test = 1
if speed_test:
import timeit # Anti pattern: only import at the beginning of the file
print("\nTesting speed:")
stmt = "bcd({0:d})".format(1234567890 ** 32)
count = 100000
for func_name in ["bcd", "bcd_str"]:
print(" {0:s}: {1:.03f} secs".format(func_name, timeit.timeit(stmt, setup="from __main__ import {0:s} as bcd".format(func_name), number=count)))
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main()
print("\nDone.")
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\Whosebug\q057476837]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" script0.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
145 - ['\x00\x00\x00\x00\x01E'] - ['\x00\x00\x00\x00\x01E']
5 - ['\x00\x00\x00\x00\x00\x05'] - ['\x00\x00\x00\x00\x00\x05']
123456 - ['\x00\x00\x00\x124V'] - ['\x00\x00\x00\x124V']
Testing speed:
bcd: 17.107 secs
bcd_str: 8.021 secs
Done.
备注:
- 由于您使用的是 packed BCD,每个数字将以 4 位存储,因此 2 位数字将占用一个字节
- 算法很简单:将数字分成2个数字组,在每个组中第1st (Most Significant)位会左移4位,然后是2nd(Least Significant) 将添加一个 - 这将是 char的ASCII码
- 输出看起来可能与您预期的有点不同,但这只是显示格式的原因:例如大写字母 'E' char 具有 ASCII 代码 0x45 ( 69),也可以写成'\x45',所以输出是正确的
有2个实现:
- bcd - 使用算术运算
- bcd_str - 使用字符串操作
速度测试(在 main 的末尾)产生了令人惊讶的结果:2nd(字符串)变体更快 (~2 倍)。一个简短的解释是(在 Python 中)modulo 操作在大量情况下是昂贵的(缓慢的)。
这看起来相当简单,并且得到了您正在寻找的答案。只需隔离每对数字并转换为 ASCII。
如果我大量执行此操作,那么我可能会为每个字节构建所有可能的 100 个值的 table(可能在 numpy 中),并使用输入中的每对数字对其进行索引。
m = 145
print(''.join(f"\x{m // 10**i % 10}{m // 10**(i-1) % 10}" for i in range(11, -1, -2)))
输出,虽然它只是一个字符串,而不是任何内部 BCD 表示
\x00\x00\x00\x00\x01\x45
同理,可以将BCD打包成字节串。打印时,Python 会将 BCD 45
解释为大写 E
import struct
m = 145
packed = struct.pack('6B', *[(m // 10**i % 10 << 4) + (m // 10**(i-1) % 10) for i in range(11, -1, -2)])
print(packed)
print(''.join(f"\{p:02x}" for p in packed))
输出
b'\x00\x00\x00\x00\x01E'
[=13=][=13=][=13=][=13=]
我需要将 Int 左填充 6 个字节(数量)转换为 Python 中的 BCD。
int = 145
expect = "\x00\x00\x00\x00\x01\x45"
我最接近的是这段代码(但它需要以字节对循环):
def TO_BCD(value):
return chr((((value / 10) << 4) & 0xF0) + ((value % 10) & 0x0F))
int = 145
TO_BCD(int) # => "\x00\x00\x00\x00\x01\x45" (expected)
这是一个例子。
script0.py:
#!/usr/bin/env python3
import sys
def bcd(value, length=0, pad='\x00'):
ret = ""
while value:
value, ls4b = divmod(value, 10)
value, ms4b = divmod(value, 10)
ret = chr((ms4b << 4) + ls4b) + ret
return pad * (length - len(ret)) + ret
def bcd_str(value, length=0, pad='\x00'):
value_str = str(value)
value_str = ("0" if len(value_str) % 2 else "") + value_str
ret = ""
for i in range(0, len(value_str), 2):
ms4b = ord(value_str[i]) - 0x30
ls4b = ord(value_str[i + 1]) - 0x30
ret += chr((ms4b << 4) + ls4b)
return pad * (length - len(ret)) + ret
def main():
values = [
145,
5,
123456,
]
for value in values:
print("{0:d} - [{1:s}] - [{2:s}]".format(value, repr(bcd(value, length=6)), repr(bcd_str(value, length=6))))
# Bonus
speed_test = 1
if speed_test:
import timeit # Anti pattern: only import at the beginning of the file
print("\nTesting speed:")
stmt = "bcd({0:d})".format(1234567890 ** 32)
count = 100000
for func_name in ["bcd", "bcd_str"]:
print(" {0:s}: {1:.03f} secs".format(func_name, timeit.timeit(stmt, setup="from __main__ import {0:s} as bcd".format(func_name), number=count)))
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main()
print("\nDone.")
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\Whosebug\q057476837]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" script0.py Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32 145 - ['\x00\x00\x00\x00\x01E'] - ['\x00\x00\x00\x00\x01E'] 5 - ['\x00\x00\x00\x00\x00\x05'] - ['\x00\x00\x00\x00\x00\x05'] 123456 - ['\x00\x00\x00\x124V'] - ['\x00\x00\x00\x124V'] Testing speed: bcd: 17.107 secs bcd_str: 8.021 secs Done.
备注:
- 由于您使用的是 packed BCD,每个数字将以 4 位存储,因此 2 位数字将占用一个字节
- 算法很简单:将数字分成2个数字组,在每个组中第1st (Most Significant)位会左移4位,然后是2nd(Least Significant) 将添加一个 - 这将是 char的ASCII码
- 输出看起来可能与您预期的有点不同,但这只是显示格式的原因:例如大写字母 'E' char 具有 ASCII 代码 0x45 ( 69),也可以写成'\x45',所以输出是正确的
有2个实现:
- bcd - 使用算术运算
- bcd_str - 使用字符串操作
速度测试(在 main 的末尾)产生了令人惊讶的结果:2nd(字符串)变体更快 (~2 倍)。一个简短的解释是(在 Python 中)modulo 操作在大量情况下是昂贵的(缓慢的)。
这看起来相当简单,并且得到了您正在寻找的答案。只需隔离每对数字并转换为 ASCII。
如果我大量执行此操作,那么我可能会为每个字节构建所有可能的 100 个值的 table(可能在 numpy 中),并使用输入中的每对数字对其进行索引。
m = 145
print(''.join(f"\x{m // 10**i % 10}{m // 10**(i-1) % 10}" for i in range(11, -1, -2)))
输出,虽然它只是一个字符串,而不是任何内部 BCD 表示
\x00\x00\x00\x00\x01\x45
同理,可以将BCD打包成字节串。打印时,Python 会将 BCD 45
解释为大写 E
import struct
m = 145
packed = struct.pack('6B', *[(m // 10**i % 10 << 4) + (m // 10**(i-1) % 10) for i in range(11, -1, -2)])
print(packed)
print(''.join(f"\{p:02x}" for p in packed))
输出
b'\x00\x00\x00\x00\x01E'
[=13=][=13=][=13=][=13=]