파이썬에서 부동 소수점 자르기


110

점 다음에 고정 된 자릿수를 갖도록 부동 소수점에서 자릿수를 제거하고 싶습니다.

1.923328437452 -> 1.923

인쇄가 아닌 다른 함수에 문자열로 출력해야합니다.

또한 잃어버린 숫자를 반올림하지 않고 무시하고 싶습니다.


4
-1.233은 -1.23 또는 -1.24로 잘 려야합니까?
Antony Hatchkins 2014 년

답변:


116

첫째, 복사하여 붙여 넣기 코드를 원하는 사람들을위한 함수 :

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파이썬 프로그램 컴파일러는 비트의 시퀀스 (64- 비트 부동 가정)에 IEEE 부동 소수점 포맷을 사용하여 인코딩

0011111111010011001100110011001100110011001100110011001100110011

이것은 IEEE 부동 소수점으로 정확하게 표현할 수있는 0.3에 가장 가까운 값입니다. 그러나 0.29999999999999998Python 프로그램으로 작성하면 컴파일러는 정확히 동일한 값 으로 변환합니다 . 한 경우에는으로 잘 리도록 (한 자리로) 0.3의미했지만 다른 경우에는으로 잘리는 것을 의미 0.2했지만 Python은 하나의 답변 만 제공 할 수 있습니다. 이것은 Python 또는 실제로 지연 평가가없는 모든 프로그래밍 언어의 근본적인 제한입니다. 자르기 함수는 소스 코드에 실제로 입력 한 문자열이 아니라 컴퓨터 메모리에 저장된 이진 값에만 액세스 할 수 있습니다. 1

IEEE 64 비트 부동 소수점 형식을 사용하여 비트 시퀀스를 10 진수로 다시 디코딩하면

0.2999999999999999888977697537484345957637...

그래서 0.2당신이 원하는 것이 아닐지라도 순진한 구현이 나올 것입니다 . 부동 소수점 표현 오류에 대한 자세한 내용은 Python 가이드를 참조하세요 .

라운드 수에 너무 가깝지만 의도적 으로 해당 라운드 수와 같지 않은 부동 소수점 값으로 작업하는 것은 매우 드뭅니다 . 따라서자를 때 메모리의 값에 해당 할 수있는 모든 것 중에서 "가장 가까운"십진수 표현을 선택하는 것이 좋습니다. Python 2.7 이상 (3.0은 아님)에는이 를 수행 하는 정교한 알고리즘이 포함되어 있으며 기본 문자열 형식화 작업을 통해 액세스 할 수 있습니다.

'{}'.format(f)

유일한주의 사항은 숫자가 충분히 크거나 작은 경우 g지수 표기법 ( 1.23e+4)을 사용한다는 점에서 형식 사양 처럼 작동 한다는 것 입니다. 따라서 메서드는이 경우를 포착하고 다르게 처리해야합니다. f대신 형식 사양을 사용 3e-10하여 28 자리의 정밀도 로 자르는 것과 같이 문제가 발생 하는 몇 가지 경우가 있습니다 (생성됨 0.0000000002999999999999999980).

실제로 반올림 숫자에 매우 가깝지만 의도적으로 같지 않은 s 작업하는 경우 float(예 : 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

변수를 전달하는 경우를 처리하기 위해 이것을 일반화하면 변수에 값을 부여한 부동 소수점 리터럴을 찾을 때까지 프로그램 실행을 거꾸로 추적해야하기 때문에 손실 된 원인처럼 보입니다. 하나라도 있다면. 대부분의 변수는 사용자 입력 또는 수학 식에서 초기화되며,이 경우 이진 표현이 전부입니다.


이 함수를 데이터 프레임에 어떻게 적용 할 수 있습니까?
codeslord

@RohithRNair 내 머리 위로, 개별 요소에서 작동하는 다른 기능을 적용하는 것과 같은 방식 (예 :) applymap(). 전체 작업을보다 효율적으로 만드는 방법이있을 수 있지만 별도의 질문이 필요합니다.
David Z

내 데이터 프레임이 정말 크기 때문에 applymap ()은 많은 시간이 걸립니다. 두 데이터 프레임의 차이점을 비교하려고하는데 부동 소수점 정밀도가 원하는 출력을 왜곡하고 있습니다. 당신이 말했듯이, 나는 같은 것에 대해 별도의 질문을 제기 할 것입니다. 감사.
codeslord

@RohithRNair 아, 두 데이터 프레임의 차이점을 비교하려는 경우 대신 물어보십시오. 값을 자르는 것은 (이 질문의 내용입니다) 최선의 방법이 아닙니다.
David Z

그냥 노트, 코드는 ... 혼란을 얻을 수있는 부의 제로에 음수를 잘라 것
user541686

152
round(1.923328437452, 3)

표준 유형에 대한 Python의 문서를 참조하십시오 . round 함수를 사용하려면 약간 아래로 스크롤해야합니다. 기본적으로 두 번째 숫자는 반올림 할 소수 자릿수를 나타냅니다.


49
반올림이 필요하지 않다는 것을 의미했습니다. 잘림이 필요합니다.
Joan Venge

1
Ahhh, 충분히 공평합니다. 미안 해요.
Teifion 2009

22
그것은 잘못된 솔루션에 대한 많은 찬성입니다! 그 이상한 Stackoverflow 희귀 성 중 하나입니다. ... 그것을 위해 배지가 있는지 궁금
tumultous_rooster

5
이 질문에 대해 얼마나 많은 오답 (그리고 오답에 찬성)이 있는지는 놀랍습니다.
nullstellensatz

6
많은 사람들이 라운딩을 찾고 해당 페이지로 올 것이다)
janjackson

33

의 결과 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'

8
내 파이썬에서 그 라운드 : '% .3f'% 1.23456 == '1.235'
데이비드 Z

이것은 말도 안되는, 좋은 게시물을 수동으로 형식화하는 것보다 훨씬 우아합니다!
rsethc

round(1.23456, 3)입니다 1.235하지1.2350000000000001
아마드

1
@Ahmad 반드시 그런 것은 아닙니다. 여기의 예는 Python 2.6에서 가져온 것입니다 (답변 날짜에 유의하십시오). Python 2.7 / 3.1에서 문자열 형식이 개선 되었기 때문에 다른 결과를 얻을 수 있습니다. 그럼에도 불구하고 부동 소수점 숫자는 종종 예상치 못한 문자열 표현을 갖습니다. docs.python.org/3.6/tutorial/floatingpoint.html
Ferdinand Beyer

21
n = 1.923328437452
str(n)[:4]

3
간단하고 비단뱀 적입니다. 4는 점 뒤의 숫자뿐만 아니라 정수의 크기입니다.
GaTTaCa 2011

4
따라서 사용자가 예를 들어 입력 하면 문자열 끝에 2소수점이있을 .것입니다. 제 생각에는 좋은 해결책이 아닙니다.
Zelphir Kaltstahl

이것은이 번호의 경우에만 해당됩니다. 11.923328437452로 일반화하는 방법은 무엇입니까?
양극화

베스트 답변! 또한 float ()를 추가하여 숫자를 반환 할 수 있습니다. float (str (n) [: 4])
justSaid

14

내 Python 2.7 프롬프트에서 :

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


11

간단한 파이썬 스크립트-

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

3
깨끗한 대답. 1000으로 나누기 전에 float로 다시 변환하려면 한 단계 만 놓치면됩니다. 그렇지 않으면 1이됩니다.
Yohan Obadia

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

작동합니다. 찾고있는 잘림을 제공해야합니다.


9

정말 비단뱀적인 방법은

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)

최신 정보

일반적으로 문제는 float 자체를 자르는 것이 아니라 반올림 하기 전에 float 숫자 부적절하게 사용하는 것입니다 .

예 : int(0.7*3*100)/100 == 2.09.

실수 를 사용 하도록 강요받는 경우 (예 :를 사용하여 코드를 가속화 numba하는 경우) 센트를 가격의 "내부 표현"으로 사용 70*3 == 210하고 입력 / 출력을 곱하거나 나누는 것이 좋습니다.


이걸 물어봐서 파슨하지만 ... 왜?
markroxor

@markroxor, 정확히 무엇에 대해 묻는 지 확실하지 않습니다. 부수적으로, 일반적으로 문제는 반올림 자체가 아니라 반올림 전에 부동 소수점 숫자 부적절하게 사용하는 것입니다 . 예 int(0.7*3*100)/100 == 2.09. 내 1 센트는 어디로 갔습니까?
안토니 Hatchkins

이 설명으로 답을 수정할 수 있습니까? 감사.
markroxor

점점 ImportError: cannot import name 'D', 난 당신이 명명 된 수입 없음을 확인하고 싶었 생각?
Overdrivr

8

이 질문에 대한 많은 답변이 완전히 잘못되었습니다. 그들은 (자르기보다는) 수레를 반올림하거나 모든 경우에 작동하지 않습니다.

'Python truncate float'를 검색했을 때 가장 많이 검색된 Google 검색 결과입니다.이 개념은 정말 간단하고 더 나은 답변을받을 가치가 있습니다. 나는 decimal모듈 을 사용하는 것이 비단뱀적인 방법이라는 Hatchkins에 동의한다 . 그래서 나는 여기서 질문에 올바르게 답하고 모든 경우에 예상대로 작동하는 함수를 제공한다.

참고로, 분수 값은 일반적으로 이진 부동 소수점 변수로 정확하게 표현할 수 없습니다 (이에 대한 설명 은 여기 참조 ). 이것이 내 함수가 문자열을 반환하는 이유입니다.

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()

4

나는 다음과 같이했다.

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

4

넌 할 수있어:

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]

이것은 양수로만 자릅니다. 음수는 내림합니다 (0에서 멀어짐).
Aaron D

3

수학 마법을 좋아한다면 + ve 숫자에 대해 작동합니다.

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

내가 이해했듯이 1e-3은 점 뒤 3 자리로 잘립니다. 이 답변이 마음에 들었지만 4와 5에서는 작동하지 않는 것 같습니다.
egvo

2

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)

1

그냥 "make round () with floor ()"트릭을 언급하고 싶었습니다.

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))

1

int (16.5); 이것은 16의 정수 값을 제공합니다. 즉, trunc는 소수를 지정할 수 없지만 다음과 같이 할 수 있습니다.

import math;

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

1

다음은 쉬운 방법입니다.

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

num = 1.923328437452 인 경우 1.923이 출력됩니다.



1

사용하기위한 일반적이고 간단한 기능 :

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

큰! int로 캐스트하면 양수와 음수가 모두 잘립니다.
Aaron D

1

파이썬 3에는 쉬운 해결 방법이 있습니다. 잘라낼 위치 나는 쉽게 적응할 수 있도록 도움말 변수 decPlace로 정의했습니다.

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

산출:

f = 1.1234

도움이되기를 바랍니다.


1
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


좋지만 반올림하지 않습니다.
Alex

1

짧고 쉬운 변형

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

1

제 생각에는 대부분의 답변이 너무 복잡합니다. 이건 어떨까요?

digits = 2  # Specify how many digits you want

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

>>> 122.48

'.'의 색인을 스캔하기 만하면됩니다. 원하는대로 자릅니다 (반올림 없음). 마지막 단계로 문자열을 부동 소수점으로 변환합니다.

또는 귀하의 경우에는 float를 입력으로 얻고 문자열을 출력으로 원할 경우 :

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

소수점 오른쪽에 선행 0을 사용하여 정밀도를 많이 낭비하기 때문에 잘리는 숫자가 작 으면 제안이 문제가됩니다. 그러나이 문제는 명시된대로 문제에 고유합니다. 제가 말하고자하는 것은 유효 숫자가 진정한 답이라는 것입니다.
오버 코일

1
>>> floor((1.23658945) * 10**4) / 10**4
1.2365

# 원하는 자릿수 10 **으로 나누고 곱하기


0

numpy.round 사용

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

0

라이브러리 나 다른 외부 종속성없이 목록 이해에 적합 할만큼 간단한 것. 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']

물론,이 된다 (즉, 네 번째 자리를 위해) 여기에서 일어나고 반올림하지만, 반올림 어떤 점에서 것은 unvoidable입니다. 잘림과 반올림 사이의 전환이 관련된 경우 다음은 약간 더 나은 예입니다.

>>> 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']

보너스 : 오른쪽에서 0 제거

>>> 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']

0

여기에 주어진 핵심 아이디어 는이 문제에 대한 최선의 접근 방식 인 것 같습니다. 불행히도 더 적은 표를 받았지만 더 많은 표를 얻은 나중 답변 은 완료되지 않았습니다 (댓글에서 관찰 됨). 바라 건데, 구현은 아래 짧은 제공 대한 완벽한 솔루션 잘림 .

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

여기여기 에있는 모든 코너 케이스를 처리해야 합니다 .


-1

파이썬 초보자이기도합니다. 여기에서 약간의 조각을 활용 한 후 2 센트를 제공합니다.

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

str (int (time.time ()))은 시간 epoch를 int로 취하여 문자열로 변환하고 다음과 결합합니다 ... str (datetime.now (). microsecond) [: 3] 이는 마이크로 초 만 반환합니다. 문자열을 만들고 처음 3 자까지 자릅니다.



-3

인쇄 할 때 의미하는 경우 다음이 작동합니다.

print '%.3f' % number

2
그것은 숫자를 반올림하고 잘리지 않습니다.
David Z
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.