float를 위치 형식의 문자열로 변환 (과학 표기법 및 잘못된 정밀도 없음)
부동 소수점 숫자를 인쇄하여 항상 10 진수 형식 (예 : 과학 표기법이 아닌 12345000000000000000000.0
또는 ,하지만 IEEE 754 double의 유효 숫자 가 최대 15.7 개)이되도록하고 싶습니다 . 그리고 더 이상.0.000000000000012345
내가 원하는 것은 이상적 그래서 결과는 것을 짧은 같은 값에 아직 결과가로 변환하는 것이 위치 진수 형식의 문자열float
.
지수가 15보다 크거나 -4보다 작 으면 repr
of a float
가 과학적 표기법으로 작성 된다는 것은 잘 알려져 있습니다 .
>>> n = 0.000000054321654321
>>> n
5.4321654321e-08 # scientific notation
경우에 str
사용되며, 결과 문자열은 다시 과학적 표기법입니다 :
>>> str(n)
'5.4321654321e-08'
내가 사용할 수있는 제안되었다 format
과 f
과학 표기법을 제거하는 플래그와 충분한 정밀도 :
>>> format(0.00000005, '.20f')
'0.00000005000000000000'
추가 후행 0이 있지만 해당 숫자에 대해 작동합니다. 그러나 동일한 형식이에서 실패하여 .1
float의 실제 기계 정밀도를 초과하는 십진수를 제공합니다.
>>> format(0.1, '.20f')
'0.10000000000000000555'
내 번호가 4.5678e-20
이면 .20f
여전히 상대 정밀도를 잃을 것입니다.
>>> format(4.5678e-20, '.20f')
'0.00000000000000000005'
따라서 이러한 접근 방식은 내 요구 사항과 일치하지 않습니다 .
이것은 질문으로 이어집니다 : 임의의 부동 소수점 숫자를 10 진수 형식으로 인쇄하는 가장 쉽고 효과적인 방법은 무엇입니까? repr(n)
(또는 str(n)
Python 3) 와 동일한 숫자를 가지지 만 항상 과학 표기법이 아닌 10 진수 형식을 사용합니다. .
즉, 예를 들어 float 값 0.00000005
을 문자열 로 변환하는 함수 또는 연산입니다 '0.00000005'
. 0.1
에 '0.1'
; 420000000000000000.0
로 '420000000000000000.0'
또는 420000000000000000
부동 소수점 값 -4.5678e-5
을 '-0.000045678'
.
현상금 기간 이후 : Karin이 문자열 조작을 사용하면 Python 2의 초기 알고리즘에 비해 상당한 속도 향상을 달성 할 수 있음을 입증했듯이 적어도 두 가지 실행 가능한 접근 방식이있는 것 같습니다.
그러므로,
- 성능이 중요하고 Python 2 호환성이 필요한 경우; 또는
decimal
어떤 이유로 모듈을 사용할 수없는 경우 문자열 조작을 사용하는 Karin의 접근 방식이이를 수행하는 방법입니다. - Python 3에서는 다소 짧은 코드도 더 빠릅니다 .
나는 주로 Python 3에서 개발 중이므로 내 답변을 수락하고 Karin에게 현상금을 수여 할 것입니다.
불행히도 새로운 스타일의 서식조차도 float.__format__
이것을 지원 하지 않는 것 같습니다 . float
s 의 기본 형식은 with와 동일합니다 repr
. 와 함께 f
플래그 기본적으로 6 소수 자릿수가있다 :
>>> format(0.0000000005, 'f')
'0.000000'
그러나 원하는 결과를 얻기위한 해킹이 있습니다. 가장 빠른 것은 아니지만 비교적 간단합니다.
- 먼저 float는
str()
or를 사용하여 문자열로 변환됩니다.repr()
- 그런 다음
Decimal
해당 문자열에서 새 인스턴스가 생성됩니다. Decimal.__format__
f
원하는 결과를 제공하는 플래그를 지원하며float
s 와 달리 기본 정밀도 대신 실제 정밀도를 인쇄합니다.
따라서 간단한 유틸리티 함수를 만들 수 있습니다 float_to_str
.
import decimal
# create a new context for this task
ctx = decimal.Context()
# 20 digits should be enough for everyone :D
ctx.prec = 20
def float_to_str(f):
"""
Convert the given float to a string,
without resorting to scientific notation
"""
d1 = ctx.create_decimal(repr(f))
return format(d1, 'f')
전역 십진 컨텍스트를 사용하지 않도록주의해야하므로이 함수에 대한 새 컨텍스트가 구성됩니다. 이것이 가장 빠른 방법입니다. 다른 방법은 사용하는 decimal.local_context
것이지만 속도가 느려 각 변환에 대해 새로운 스레드 로컬 컨텍스트와 컨텍스트 관리자를 생성합니다.
이제이 함수는 가수에서 가능한 모든 자릿수가있는 문자열을 가장 짧은 등가 표현으로 반올림하여 반환합니다 .
>>> float_to_str(0.1)
'0.1'
>>> float_to_str(0.00000005)
'0.00000005'
>>> float_to_str(420000000000000000.0)
'420000000000000000'
>>> float_to_str(0.000000000123123123123123123123)
'0.00000000012312312312312313'
마지막 결과는 마지막 자리에서 반올림됩니다.
@Karin이 언급했듯이 float_to_str(420000000000000000.0)
는 예상 한 형식과 엄격하게 일치하지 않습니다. 420000000000000000
후행없이 반환 됩니다 .0
.
과학적 표기법의 정확성에 만족한다면 간단한 문자열 조작 접근 방식을 취할 수 있습니까? 끔찍하게 영리하지는 않지만 작동하는 것처럼 보이며 (제시 한 모든 사용 사례를 통과 함) 상당히 이해할 수 있다고 생각합니다.
def float_to_str(f):
float_string = repr(f)
if 'e' in float_string: # detect scientific notation
digits, exp = float_string.split('e')
digits = digits.replace('.', '').replace('-', '')
exp = int(exp)
zero_padding = '0' * (abs(int(exp)) - 1) # minus 1 for decimal point in the sci notation
sign = '-' if f < 0 else ''
if exp > 0:
float_string = '{}{}{}.0'.format(sign, digits, zero_padding)
else:
float_string = '{}0.{}{}'.format(sign, zero_padding, digits)
return float_string
n = 0.000000054321654321
assert(float_to_str(n) == '0.000000054321654321')
n = 0.00000005
assert(float_to_str(n) == '0.00000005')
n = 420000000000000000.0
assert(float_to_str(n) == '420000000000000000.0')
n = 4.5678e-5
assert(float_to_str(n) == '0.000045678')
n = 1.1
assert(float_to_str(n) == '1.1')
n = -4.5678e-5
assert(float_to_str(n) == '-0.000045678')
성능 :
이 접근 방식이 너무 느릴 수 있다고 걱정했기 때문에 실행 timeit
하여 OP의 십진 컨텍스트 솔루션과 비교했습니다. 문자열 조작이 실제로 훨씬 더 빠릅니다. 편집 : 파이썬 2에서만 훨씬 더 빠른 것 같습니다. 파이썬 3에서도 결과는 비슷했지만 십진법으로 약간 더 빠릅니다.
결과 :
Python 2 : 사용
ctx.create_decimal()
:2.43655490875
Python 2 : 문자열 조작 사용 :
0.305557966232
Python 3 : 사용
ctx.create_decimal()
:0.19519368198234588
Python 3 : 문자열 조작 사용 :
0.2661344590014778
타이밍 코드는 다음과 같습니다.
from timeit import timeit
CODE_TO_TIME = '''
float_to_str(0.000000054321654321)
float_to_str(0.00000005)
float_to_str(420000000000000000.0)
float_to_str(4.5678e-5)
float_to_str(1.1)
float_to_str(-0.000045678)
'''
SETUP_1 = '''
import decimal
# create a new context for this task
ctx = decimal.Context()
# 20 digits should be enough for everyone :D
ctx.prec = 20
def float_to_str(f):
"""
Convert the given float to a string,
without resorting to scientific notation
"""
d1 = ctx.create_decimal(repr(f))
return format(d1, 'f')
'''
SETUP_2 = '''
def float_to_str(f):
float_string = repr(f)
if 'e' in float_string: # detect scientific notation
digits, exp = float_string.split('e')
digits = digits.replace('.', '').replace('-', '')
exp = int(exp)
zero_padding = '0' * (abs(int(exp)) - 1) # minus 1 for decimal point in the sci notation
sign = '-' if f < 0 else ''
if exp > 0:
float_string = '{}{}{}.0'.format(sign, digits, zero_padding)
else:
float_string = '{}0.{}{}'.format(sign, zero_padding, digits)
return float_string
'''
print(timeit(CODE_TO_TIME, setup=SETUP_1, number=10000))
print(timeit(CODE_TO_TIME, setup=SETUP_2, number=10000))
NumPy 1.14.0부터는 numpy.format_float_positional
. 예를 들어 질문의 입력에 대해 실행합니다.
>>> numpy.format_float_positional(0.000000054321654321)
'0.000000054321654321'
>>> numpy.format_float_positional(0.00000005)
'0.00000005'
>>> numpy.format_float_positional(0.1)
'0.1'
>>> numpy.format_float_positional(4.5678e-20)
'0.000000000000000000045678'
numpy.format_float_positional
Dragon4 알고리즘을 사용하여 원래 float 입력으로 다시 왕복하는 위치 형식으로 가장 짧은 10 진수 표현을 생성합니다. 이 또한의 numpy.format_float_scientific
과학 표기하고, 두 함수는 반올림 1과 0의 트리밍 등의 작업을 사용자 정의하는 선택적 인수를 제공합니다.
str()
float 숫자 를 호출 하여 임의의 정밀도를 잃을 준비가 되었다면 다음과 같이 할 수 있습니다 .
import decimal
def float_to_string(number, precision=20):
return '{0:.{prec}f}'.format(
decimal.Context(prec=100).create_decimal(str(number)),
prec=precision,
).rstrip('0').rstrip('.') or '0'
It doesn't include global variables and allows you to choose the precision yourself. Decimal precision 100 is chosen as an upper bound for str(float)
length. The actual supremum is much lower. The or '0'
part is for the situation with small numbers and zero precision.
Note that it still has its consequences:
>> float_to_string(0.10101010101010101010101010101)
'0.10101010101'
Otherwise, if the precision is important, format
is just fine:
import decimal
def float_to_string(number, precision=20):
return '{0:.{prec}f}'.format(
number, prec=precision,
).rstrip('0').rstrip('.') or '0'
It doesn't miss the precision being lost while calling str(f)
. The or
>> float_to_string(0.1, precision=10)
'0.1'
>> float_to_string(0.1)
'0.10000000000000000555'
>>float_to_string(0.1, precision=40)
'0.1000000000000000055511151231257827021182'
>>float_to_string(4.5678e-5)
'0.000045678'
>>float_to_string(4.5678e-5, precision=1)
'0'
Anyway, maximum decimal places are limited, since the float
type itself has its limits and cannot express really long floats:
>> float_to_string(0.1, precision=10000)
'0.1000000000000000055511151231257827021181583404541015625'
Also, whole numbers are being formatted as-is.
>> float_to_string(100)
'100'
Interesting question, to add a little bit more of content to the question, here's a litte test comparing @Antti Haapala and @Harold solutions outputs:
import decimal
import math
ctx = decimal.Context()
def f1(number, prec=20):
ctx.prec = prec
return format(ctx.create_decimal(str(number)), 'f')
def f2(number, prec=20):
return '{0:.{prec}f}'.format(
number, prec=prec,
).rstrip('0').rstrip('.')
k = 2*8
for i in range(-2**8,2**8):
if i<0:
value = -k*math.sqrt(math.sqrt(-i))
else:
value = k*math.sqrt(math.sqrt(i))
value_s = '{0:.{prec}E}'.format(value, prec=10)
n = 10
print ' | '.join([str(value), value_s])
for f in [f1, f2]:
test = [f(value, prec=p) for p in range(n)]
print '\t{0}'.format(test)
Neither of them gives "consistent" results for all cases.
- With Anti's you'll see strings like '-000' or '000'
- With Harolds's you'll see strings like ''
I'd prefer consistency even if I'm sacrificing a little bit of speed. Depends which tradeoffs you want to assume for your use-case.
I think rstrip
can get the job done.
a=5.4321654321e-08
'{0:.40f}'.format(a).rstrip("0") # float number and delete the zeros on the right
# '0.0000000543216543210000004442039220863003' # there's roundoff error though
Let me know if that works for you.
ReferenceURL : https://stackoverflow.com/questions/38847690/convert-float-to-string-in-positional-format-without-scientific-notation-and-fa
'Development Tip' 카테고리의 다른 글
Alamofire를 사용하여 각 요청 / 응답을 어떻게 기록 할 수 있습니까? (0) | 2021.01.07 |
---|---|
Angular 2에서 한 구성 요소에서 다른 구성 요소로 객체를 전달하는 방법은 무엇입니까? (0) | 2021.01.07 |
try .. catch .. finally로 Javascript 오류 처리 (0) | 2021.01.07 |
array.contains의 jquery 버전 (0) | 2021.01.07 |
.NET은 Java Properties 클래스에 해당하는 속성 파일을로드하고 구문 분석 할 수 있습니까? (0) | 2021.01.07 |