如何截断浮点值?

2025-01-03 08:41:00
admin
原创
131
摘要:问题描述:我想从浮点数中删除数字,使点后的数字固定,例如:1.923328437452 → 1.923 我需要将其作为字符串输出到另一个函数,而不是打印。另外我想忽略丢失的数字,而不是对它们进行四舍五入。解决方案 1:首先,对于那些只想要一些复制粘贴代码的人来说,这个函数是:def truncate(f, n...

问题描述:

我想从浮点数中删除数字,使点后的数字固定,例如:

1.923328437452 → 1.923

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

另外我想忽略丢失的数字,而不是对它们进行四舍五入。


解决方案 1:

首先,对于那些只想要一些复制粘贴代码的人来说,这个函数是:

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 程序中编写,编译器会将其转换为完全相同的值。在一种情况下,你想将其截断为(一位数字)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.299999999999999998 或 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

如果您的情况是这样的,如果您确实想使用“好的”十进制表示进行截断,那么您所能做的(据我所知)就是选择一些数字,小于 a 可表示的完整精度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

将此概括为处理传入变量的情况似乎毫无意义,因为您必须追溯程序的执行过程,直到找到赋予变量值的浮点文字。如果有的话。大多数变量将根据用户输入或数学表达式进行初始化,在这种情况下,二进制表示就是全部。

解决方案 2:

round(1.923328437452, 3)

请参阅Python 标准类型的文档。您需要向下滚动一点才能找到 round 函数。本质上,第二个数字表示将其四舍五入到小数点后几位。

解决方案 3:

的结果round是一个浮点数,所以要小心(示例来自 Python 2.6):

>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001

使用格式化的字符串会更好:

>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'

解决方案 4:

n = 1.923328437452
str(n)[:4]

解决方案 5:

在我的 Python 2.7 提示符下:

>>> int(1.923328437452 * 1000)/1000.0
1.923

解决方案 6:

简单的 Python 脚本 -

n = 1.923328437452
n = float(int(n * 1000))
n /=1000

解决方案 7:

真正 Python 式的做法是

from decimal import *

with localcontext() as ctx:
    ctx.rounding = ROUND_DOWN
    print Decimal('1.923328437452').quantize(Decimal('0.001'))

或更短:

from decimal import Decimal as D, ROUND_DOWN

D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)

更新

通常问题不在于截断浮点数本身,而在于舍入之前浮点数的不当使用。

例如:int(0.7*3*100)/100 == 2.09

如果您被迫使用浮点数(例如,您正在使用 加速您的代码numba),那么最好使用美分作为价格的“内部表示”:(70*3 == 210)并乘以/除以输入/输出。

解决方案 8:

def trunc(num, digits):
   sp = str(num).split('.')
   return '.'.join([sp[0], sp[1][:digits]])

这应该有效。它应该会给你你想要的截断。

解决方案 9:

这个问题给出的很多答案都是完全错误的。它们要么对浮点数进行舍入(而不是截断),要么不适用于所有情况。

这是我在 Google 上搜索“Python 截断浮点数”时得到的最佳结果,这个概念非常简单,值得更好的答案。我同意 Hatchkins 的观点,即使用decimal模块是实现此目的的 Python 方式,因此我在此提供了一个函数,我认为该函数可以正确回答问题,并且在所有情况下都能按预期工作。

顺便说一下,一般来说,小数值不能用二进制浮点变量精确表示(有关此问题的讨论请参见此处),这就是我的函数返回字符串的原因。

from decimal import Decimal, localcontext, ROUND_DOWN

def truncate(number, places):
    if not isinstance(places, int):
        raise ValueError("Decimal places must be an integer.")
    if places < 1:
        raise ValueError("Decimal places must be at least 1.")
    # If you want to truncate to 0 decimal places, just do int(number).

    with localcontext() as context:
        context.rounding = ROUND_DOWN
        exponent = Decimal(str(10 ** - places))
        return Decimal(str(number)).quantize(exponent).to_eng_string()

解决方案 10:

>>> from math import floor
>>> floor((1.23658945) * 10**4) / 10**4
1.2365

除以 10 并乘以 所需数字的数量

解决方案 11:

如果你喜欢一些数学魔法,这对 +ve 数字有效:

>>> v = 1.923328437452
>>> v - v % 1e-3
1.923

解决方案 12:

我做了这样的事情:

from math import trunc


def truncate(number, decimals=0):
    if decimals < 0:
        raise ValueError('truncate received an invalid value of decimals ({})'.format(decimals))
    elif decimals == 0:
        return trunc(number)
    else:
        factor = float(10**decimals)
        return trunc(number*factor)/factor

解决方案 13:

您可以执行以下操作:

def truncate(f, n):
    return math.floor(f * 10 ** n) / 10 ** n

测试:

>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]

解决方案 14:

只是想提一下旧的“用 floor() 制作 round()”技巧

round(f) = floor(f+0.5)

可以反转为由 round() 变为 floor()

floor(f) = round(f-0.5)

尽管这两条规则都不适用于负数,但使用它们并不理想:

def trunc(f, n):
    if f > 0:
        return "%.*f" % (n, (f - 0.5*10**-n))
    elif f == 0:
        return "%.*f" % (n, f)
    elif f < 0:
        return "%.*f" % (n, (f + 0.5*10**-n))

解决方案 15:

int(1.923328437452 * 1000) / 1000
>>> 1.923

int(1.9239 * 1000) / 1000
>>> 1.923

通过将数字乘以 1000(3 位数字为 10 ^ 3),我们将小数点向右移动 3 位,得到1923.3284374520001。当我们将其转换为 int 时,小数部分3284374520001将被丢弃。然后我们再次通过除以 1000 来撤消小数点的移动,从而返回1.923

解决方案 16:

一个通用且简单的函数:

def truncate_float(number, length):
    """Truncate float numbers, up to the number specified
    in length that must be an integer"""

    number = number * pow(10, length)
    number = int(number)
    number = float(number)
    number /= pow(10, length)
    return number

解决方案 17:

def precision(value, precision):
    """
    param: value: takes a float
    param: precision: int, number of decimal places
    returns a float
    """
    x = 10.0**precision
    num = int(value * x)/ x
    return num
precision(1.923328437452, 3)

1.923

解决方案 18:

简短易行的版本

def truncate_float(value, digits_after_point=2):
    pow_10 = 10 ** digits_after_point
    return (float(int(value * pow_10))) / pow_10

>>> truncate_float(1.14333, 2)
>>> 1.14

>>> truncate_float(1.14777, 2)
>>> 1.14


>>> truncate_float(1.14777, 4)
>>> 1.1477

解决方案 19:

当使用 pandas df 时,这对我有用

import math
def truncate(number, digits) -> float:
    stepper = 10.0 ** digits
    return math.trunc(stepper * number) / stepper

df['trunc'] = df['float_val'].apply(lambda x: truncate(x,1))
df['trunc']=df['trunc'].map('{:.1f}'.format)

解决方案 20:

int(16.5);这将给出一个整数值 16,即 trunc,将无法指定小数,但猜想你可以通过以下方式做到这一点

import math;

def trunc(invalue, digits):
    return int(invalue*math.pow(10,digits))/math.pow(10,digits);

解决方案 21:

这是一个简单的方法:

def truncate(num, res=3):
    return (floor(num*pow(10, res)+0.5))/pow(10, res)

对于 num = 1.923328437452,输出 1.923

解决方案 22:

def trunc(f,n):
  return ('%.16f' % f)[:(n-16)]

解决方案 23:

在 python 3 中有一个简单的解决方法。我使用帮助变量 decPlace 定义了在哪里剪切,以使其易于适应。

f = 1.12345
decPlace= 4
f_cut = int(f * 10**decPlace) /10**decPlace

输出:

f = 1.1234

希望有帮助。

解决方案 24:

在我看来,大多数答案都太复杂了,这个怎么样?

digits = 2  # Specify how many digits you want

fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])

>>> 122.48

只需扫描“。”的索引并根据需要截断(不舍入)。将字符串转换为浮点数作为最后一步。

或者在你的情况下如果你得到一个浮点数作为输入并想要一个字符串作为输出:

fnum = str(122.485221)  # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1]  # string output

解决方案 25:

在我看来,这里给出的核心思想是解决这个问题的最佳方法。不幸的是,它获得的投票较少,而后面获得更多投票的答案并不完整(如评论中所见)。希望下面的实现能为截断提供一个简短完整的解决方案。

def trunc(num, digits):
    l = str(float(num)).split('.')
    digits = min(len(l[1]), digits)
    return l[0] + '.' + l[1][:digits]

它应该处理这里和这里发现的所有极端情况。

解决方案 26:

我认为更好的版本就是找到index小数点.,然后相应地采取字符串切片:

def truncate(number, n_digits:int=1)->float:
    '''
    :param number: real number ℝ
    :param n_digits: Maximum number of digits after the decimal point after truncation
    :return: truncated floating point number with at least one digit after decimal point
    '''
    decimalIndex = str(number).find('.')
    if decimalIndex == -1:
        return float(number)
    else:
        return float(str(number)[:decimalIndex+n_digits+1])

解决方案 27:

我认为你正在寻找的是这个:

n = 1.923328437452
float(str(n)[:5])

注意 [:5] 不包括在内。打印浮动

1.923

或者,下面的代码会给出相同的结果,而且效率更高。例如,如果您需要两位小数,只需将 1000 改为 100:

int(n * 1000) / 1000.0

解决方案 28:

使用 numpy.round

import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)

解决方案 29:

足够简单,适合列表推导,没有库或其他外部依赖项。对于 Python >=3.6,使用 f 字符串编写非常简单。

这个想法是让字符串转换四舍五入到比您需要的多一位,然后砍掉最后一位数字。

>>> nout = 3  # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1] for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.800', '0.888', '1.125', '1.250', '1.500']

当然,这里发生舍入(即第四位数字),但在某些时候舍入是不可避免的。如果截断和舍入之间的转换是相关的,这里有一个稍微好一点的例子:

>>> nacc = 6  # desired accuracy (maximum 15!)
>>> nout = 3  # desired number of digits in output
>>> [f'{x:.{nacc}f}'[:-(nacc-nout)] for x in [2.9999, 2.99999, 2.999999, 2.9999999]]
>>> ['2.999', '2.999', '2.999', '3.000']

奖励:删除右边的零

>>> nout = 3  # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1].rstrip('0') for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.8', '0.888', '1.125', '1.25', '1.5']

解决方案 30:

我也是一个 Python 新手,在利用了这里的一些零碎知识后,我提供了我的意见

print str(int(time.time()))+str(datetime.now().microsecond)[:3]

str(int(time.time())) 将把时间纪元作为 int,并将其转换为字符串并与... str(datetime.now().microsecond)[:3] 连接,后者仅返回微秒,转换为字符串并截断为前 3 个字符

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3892  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2717  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   52  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   54  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   49  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用