当前位置: 首页 > 面试题库 >

如何截断浮点值?

景品
2023-03-14
问题内容

我想从浮点数中删除数字,以使小数点后的位数固定不变,例如:

1.923328437452 → 1.923

我需要作为字符串输出到另一个函数,而不是打印。

我也想忽略丢失的数字,而不是四舍五入。


问题答案:

首先,对于那些只需要复制和粘贴代码的人来说,该函数是:

def truncate(f, n):
    '''Truncates/pads a float f to n decimal places without rounding'''
    s = '{}'.format(f)
    if 'e' in s or 'E' in s:
        return '{0:.{1}f}'.format(f, n)
    i, p, d = s.partition('.')
    return '.'.join([i, (d+'0'*n)[:n]])

这在Python
2.7和3.1+中有效。对于较旧的版本,不可能获得相同的“智能舍入”效果(至少,并非没有很多复杂的代码),但是在截断前舍入到小数点后12位将在大多数时间起作用:

def truncate(f, n):
    '''Truncates/pads a float f to n decimal places without rounding'''
    s = '%.12f' % f
    i, p, d = s.partition('.')
    return '.'.join([i, (d+'0'*n)[:n]])

基本方法的核心是将值完全精确地转换为字符串,然后仅将超出所需数目的字符的所有内容都切掉。后面的步骤很容易;可以通过字符串操作来完成

i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])

decimal模块

str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))

第一步,转换为字符串非常困难,因为存在一些成对的浮点文字(即,您在源代码中编写的内容),它们均产生相同的二进制表示形式,但应以不同的方式截断。例如,考虑0.3和0.29999999999999998。如果您0.3使用Python程序编写,则编译器将使用IEEE浮点格式将其编码为位序列(假定为64位浮点数)

0011111111010011001100110011001100110011001100110011001100110011

这是最接近0.3的值,可以准确地表示为IEEE浮点数。但是,如果您0.29999999999999998使用Python程序编写代码,则编译器会将其转换为
完全相同的value
。在一种情况下,您希望将其截断为(一位)0.3,而在另一种情况下,您希望将其截断为0.2,但是Python只能给出一个答案。这是Python的根本限制,或者实际上是任何没有延迟评估的编程语言。截断功能只能访问存储在计算机内存中的二进制值,而不能访问您实际在源代码中键入的字符串。1个

如果您再次使用IEEE 64位浮点格式将位序列解码回十进制数,则会得到

0.2999999999999999888977697537484345957637...

因此0.2即使您可能并不想这样做,也会提出一个幼稚的实现。有关浮点表示错误的更多信息,请参见Python教程

使用非常接近整数但又 有意
不等于该整数的浮点值是非常罕见的。因此,在截断时,从所有可能对应于内存值的“十进制”十进制表示中选择是最有意义的。Python
2.7及更高版本(但不是3.0)提供了一种完善的算法来执行此操作,我们可以通过默认的字符串格式设置操作来访问该算法。

'{}'.format(f)

唯一需要注意的是,如果数字足够大或足够小g,就使用指数表示法(1.23e+4),这就像格式规范一样。因此,该方法必须抓住这种情况并以不同的方式处理它。在某些情况下,使用f格式规范会引起问题,例如试图将3e-10精度截断为28位(它会产生0.0000000002999999999999999980),但我不确定如何最好地处理这些问题。

如果你确实 正在
与工作floats表示非常接近圆形数字,但故意不等于他们(像0.29999999999999998或99.959999999999994),这会产生一些假阳性,即它会圆数字,你不想圆润。在这种情况下,解决方案是指定固定的精度。

'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)

此处使用的精度位数并不重要,它只需要足够大即可确保在字符串转换中执行的任何舍入操作都不会将值“累加”到其漂亮的十进制表示形式。我认为sys.float_info.dig + n + 2在所有情况下都足够了,但是如果没有2增加的话,这样做就没有什么害处。

在Python的早期版本(最高2.6或3.0)中,浮点数格式更加粗糙,并且会定期生成类似

>>> 1.1
1.1000000000000001

如果这是你的情况,如果你
希望使用“好”十进制表示为截断,所有你能做的(据我所知)是挑选的数字一定数目,少于一个完整的精度表示的float,与轮截断之前,请先将其编号为那么多位数。典型的选择是12

'%.12f' % f

但是您可以对其进行调整以适合您使用的数字。

1好…我撒了谎。从技术上讲,您 可以
指示Python重新解析其自己的源代码,并提取与传递给截断函数的第一个参数相对应的部分。如果该参数是浮点文字,则可以将其在小数点后的特定位置截断并返回。但是,如果参数是变量,则此策略不起作用,这使其相当无用。以下内容仅供参考:

def trunc_introspect(f, n):
    '''Truncates/pads the float f to n decimal places by looking at the caller's source code'''
    current_frame = None
    caller_frame = None
    s = inspect.stack()
    try:
        current_frame = s[0]
        caller_frame = s[1]
        gen = tokenize.tokenize(io.BytesIO(caller_frame[4][caller_frame[5]].encode('utf-8')).readline)
        for token_type, token_string, _, _, _ in gen:
            if token_type == tokenize.NAME and token_string == current_frame[3]:
                next(gen) # left parenthesis
                token_type, token_string, _, _, _ = next(gen) # float literal
                if token_type == tokenize.NUMBER:
                    try:
                        cut_point = token_string.index('.') + n + 1
                    except ValueError: # no decimal in string
                        return token_string + '.' + '0' * n
                    else:
                        if len(token_string) < cut_point:
                            token_string += '0' * (cut_point - len(token_string))
                        return token_string[:cut_point]
                else:
                    raise ValueError('Unable to find floating-point literal (this probably means you called {} with a variable)'.format(current_frame[3]))
                break
    finally:
        del s, current_frame, caller_frame

将其通用化以处理您传入变量的情况似乎是一个失败的原因,因为您必须在程序的执行过程中向后追溯,直到找到为变量赋值的浮点文字。如果有一个。大多数变量将从用户输入或数学表达式初始化,在这种情况下,二进制表示就全部存在。



 类似资料:
  • 上面的代码将“浮点值”打印为12345678,我需要12345678.12 我怎样才能得到我的结果?请让我知道。

  • 问题内容: 我有一列字符串,每个字符串中都有一个城市,州和数字。 不仅限于此,但我想知道如何截断此列中的数字并仅显示城市和州,或者甚至更好地截断这些数字,然后将城市和州作为一个单独的列。 对于所有不同的记录,该列始终采用相同的格式。 谢谢! 问题答案: 没有为您做所有的工作… City:列的子字符串,从位置0到第一次出现逗号-1. State:列的子字符串,从第一次出现逗号后的2个位置到下一个空格

  • 功能限制拦截断点 bp EnableMenuItem 禁止或允许菜单项 bp EnableWindow 禁止或允许窗口

  • 有没有一种优雅的方法来断言数字相等而忽略它们的类?我想在JUnit测试框架中使用它,但例如 因Java . lang . assertion错误而失败:应为:java.lang.Integer 我希望在某个地方有一个很好的方法,只比较值,并与int,long,float,byte,double,BigDecimal,BigInteger一起工作...

  • 我用下面的代码创建了一个JFreeChart,但是y轴标记被截断了。即使数据点在y轴上重叠,我应该如何显示图表?基本上,我希望Y轴点从我的文件生成,一个适当的范围是填充和显示在图表中。 下面是创建的图像,很明显你可以看到Y轴有一个重叠显示。

  • 问题内容: 为什么没有一个 TRUNCATE 上工作?即使我知道了: 错误1701(42000):无法截断在外键约束中引用的表(。,CONSTRAINT FOREIGN KEY()参考。()) 问题答案: 您不能在上面应用FK约束的表(与相同)。 要变通解决此问题,使用这些解决方案之一。两者都存在破坏数据完整性的风险。 选项1: 消除约束 执行 手动删除现在 无处* 引用的行 * 创建约束 选项2