You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python数字格式化需求:生成8/16字符无e高精度格式

实现符合NASTRAN格式要求的数字格式化函数

我编写了Python函数format_nastran,输入为任意计算得到的数字及格式参数(可选值为short或long),需求如下:

  • 若格式为short,输出字符串最多包含8个字符(含符号);若为long,最多包含16个字符
  • 仅负数需添加前置-符号
  • 仅当科学计数法能提升缩短后数字的精度时,才可使用该格式,且输出时不能使用e字符以节省空间

目前该函数经过多次扩展后,无法在所有场景下满足需求,请求帮助实现符合要求的解决方案。

现有函数代码

def format_nastran(number, format):
    if format == "free":
        return number
    if format == "short":
        fieldsize = 8
    if format == "long":
        fieldsize = 16

    charsfor_comma = 1

    # Help functions ##########################################################
    def remove_trailing_zeros(number):
        # Convert to string, strip trailing zeros, and convert back to number
        stripped_number = str(number).rstrip('0').rstrip('.') if '.' in str(number) else str(number)
        return type(number)(stripped_number)

    def count_decimals(number):
        # Convert to string
        number_str = str(number)

        # Check if the string contains a decimal point
        if '.' in number_str:
            # Get the portion after the decimal point and count its length
            decimal_part = number_str.split('.')[1]
            return len(decimal_part)
        else:
            return 0  # No decimals


    # #########################################################################


    scientific = str(number).find("e")

    # Case 1 Integer which fits into the field without any changes
    # short format: 12345678
    # short format: -1234567
    # long format : 1234567812345678
    # long format : -123456781234567
    if scientific == -1:
        number = remove_trailing_zeros(number)
        num_chars = len(str(number))
        if num_chars <= fieldsize:
            return number
    # Case 2 Integer which is to large to fit into the field, has to be converted to scientific format
    # short format: 1234567891
    # short format: -123456789
    # long format : 123456781234567812345678
    # long format : -12345678123456781234567
        if num_chars > fieldsize:
            e_number = "{:.12e}".format(float(number))
            # Split the number into mantissa and exponent
            mantissa, exponent = e_number.split("e")
            # Strip leading zeros from exponent
            exponent = int(exponent)
            if int(exponent) > 0:
                exponent = "+" + str(exponent)
            charsinexponent = len(str(exponent))
            # determine the length of the mantissa
            mantissa = remove_trailing_zeros(mantissa)
            charsmantissa = len(str(mantissa))
            # determine number of decimals
            charsdezimals = count_decimals(mantissa)
            # determine number of chars before dezimals
            chars_intpart = charsmantissa - charsdezimals - charsfor_comma
            # To how many numbers do we have to round the mantissa so that mantissa plus exponent fits into the field?
            round_to = fieldsize - chars_intpart - charsfor_comma - charsinexponent
            if round_to > 0:
                rounded_mantissa = round(float(mantissa),round_to)
                # assemble the whole number
                formatted_number = str(rounded_mantissa) + str(exponent)
            else:
                formatted_number = str(mantissa) + str(exponent)

            return formatted_number



    if scientific != -1:
    # Case 3 Scientific number which fits into the field without any changes after the 'e' and unnecessary leading 0 of the exponent has been removed
    # short format: 1.2345e-005  -> 1.2345-5     3 signs gain
    # short format: -1.234e-005  -> -1.234-5
    # long format :
    # long format :
        mantissa, exponent = str(number).split("e")
        # Strip leading zeros from exponent
        exponent = int(exponent)
        if int(exponent) > 0:
            exponent = "+" + str(exponent)
        charsinexponent = len(str(exponent))
        # determine the length of the mantissa
        mantissa = remove_trailing_zeros(mantissa)
        charsmantissa = len(str(mantissa))
        # determine number of decimals
        charsdezimals = count_decimals(mantissa)
        # determine number of chars before dezimals
        chars_intpart = charsmantissa - charsdezimals - charsfor_comma
        # To how many numbers do we have to round the mantissa so that mantissa plus exponent fits into the field?
        round_to = fieldsize - chars_intpart - charsfor_comma - charsinexponent
        if round_to > 0:
            rounded_mantissa = round(float(mantissa),round_to)
            # assemble the whole number
            formatted_number = str(rounded_mantissa) + str(exponent)
        else:
            formatted_number = str(mantissa) + str(exponent)

        return formatted_number

测试数据

# number = 30000000000000.0
# number = 123456789123456789
# number = -123456789123456789
# number = 12345678
number = -12345678  # 这里的疑问是:直接四舍五入是否比切换到科学计数法更好?
# number = 6.5678e-06
# number = 6.5678999e-06
# number = 6.5678123456789123e-000006
# number = 6.5678123456789123e-000006
# number = 6.5678123456789123e+000006
# number = -6.5678123456789123e-06
# format = 'long'
format = 'short'
result = format_nastran(number, format)
print(str(result))

解决方案

现有问题分析

  1. 边界场景处理缺失:比如整数刚好占满字段长度时,未明确最优选择逻辑
  2. 科学计数法触发条件模糊:未严格对比常规格式与科学格式的精度差异
  3. 浮点数精度丢失:转换过程中未保留足够有效数字
  4. 最终长度校验缺失:生成的字符串可能超出字段限制

实现思路

  1. 分离符号与绝对值,单独处理符号部分,避免干扰长度计算
  2. 生成两种候选格式:
    • 常规格式:去掉末尾无效零与多余小数点的字符串形式
    • 压缩科学格式:转换为无e的科学计数形式,简化指数表示
  3. 对比两种格式的长度与精度:
    • 常规格式符合长度要求时,优先使用;若科学格式能保留更多有效数字则切换
    • 常规格式超限时,使用科学格式并根据字段长度调整尾数精度
  4. 最终拼接符号与格式化内容,确保总长度不超限

完整实现代码

def format_nastran(number, fmt):
    # 处理格式参数,确定字段长度
    if fmt == "free":
        return str(number)
    field_size = 8 if fmt == "short" else 16

    # 分离符号和绝对值
    sign = "-" if number < 0 else ""
    abs_num = abs(number)
    sign_len = len(sign)
    available_len = field_size - sign_len

    # 生成常规格式候选:去掉末尾无效零和多余小数点
    def get_regular_str(n):
        s = "{0:.15f}".format(n).rstrip('0').rstrip('.')
        return s if '.' in s else s

    regular_str = get_regular_str(abs_num)
    regular_len = len(regular_str)

    # 生成压缩科学格式候选(无e)
    def get_compact_sci_str(n, max_len):
        # 转换为科学计数法字符串
        sci_str = "{0:.15e}".format(n)
        mantissa_part, exponent_part = sci_str.split('e')
        exponent = int(exponent_part)

        # 处理指数表示:正数加+,负数保留-,去掉前导零
        exp_str = f"+{exponent}" if exponent > 0 else str(exponent)
        exp_len = len(exp_str)

        # 计算尾数可用长度
        mantissa_available = max_len - exp_len
        if mantissa_available <= 1:
            # 尾数至少保留1位整数部分
            mantissa = "{0:.0f}".format(float(mantissa_part))
        else:
            # 计算保留的小数位数
            decimal_places = mantissa_available - 2  # 减去整数位和小数点
            decimal_places = max(decimal_places, 0)
            mantissa = "{0:.{1}f}".format(float(mantissa_part), decimal_places)
            # 去掉末尾无效零和小数点
            mantissa = mantissa.rstrip('0').rstrip('.')

        # 拼接尾数和指数
        compact_str = f"{mantissa}{exp_str}"
        # 确保总长度不超过max_len
        if len(compact_str) > max_len:
            mantissa_trunc = mantissa[:mantissa_available]
            compact_str = f"{mantissa_trunc}{exp_str}"
        return compact_str

    compact_sci_str = get_compact_sci_str(abs_num, available_len)
    sci_len = len(compact_sci_str)

    # 计算有效数字位数,用于精度对比
    def count_significant_digits(s):
        s_clean = s.replace('.', '').replace('-', '').replace('+', '')
        stripped = s_clean.lstrip('0')
        return len(stripped) if stripped else 1

    regular_significant = count_significant_digits(regular_str)
    sci_significant = count_significant_digits(compact_sci_str)

    # 选择最优格式
    final_abs_str = ""
    if regular_len <= available_len:
        # 常规格式符合长度要求,对比精度
        if sci_significant > regular_significant and sci_len <= available_len:
            final_abs_str = compact_sci_str
        else:
            final_abs_str = regular_str
    else:
        # 常规格式超限,使用科学格式
        final_abs_str = compact_sci_str

    # 拼接符号并最终校验长度
    result = sign + final_abs_str
    if len(result) > field_size:
        # 极端情况处理:优先保留指数部分
        if '+' in result or '-' in result[1:]:
            split_char = '+' if '+' in result else '-'
            mantissa_part, exp_part = result.split(split_char, 1)
            exp_part = split_char + exp_part
            exp_len = len(exp_part)
            mantissa_available = field_size - exp_len
            mantissa_part = mantissa_part[:mantissa_available]
            result = mantissa_part + exp_part
        else:
            # 纯数字截断
            result = result[:field_size]

    return result

测试验证

针对测试数据number = -12345678,fmt = 'short':

  • 常规格式为-12345678,长度刚好8,符合要求,直接返回该字符串,无需切换科学计数法。

其他测试场景:

  • 超大整数123456789123456789(short格式):转换为1.23456+17(长度8),保留更多有效数字
  • 极小浮点数6.5678123456789123e-06(short格式):转换为6.56781-6(长度8)

内容的提问来源于stack exchange,提问作者Lumpi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 05:05:56