2017-09-18 3 views
1

나는 숙제를 할 때 친구를 돕고 있는데, 사용자가 임의의 초를 입력하고 주, 일, 시간, 분, 초 단위로 그 시간을 나타내는 문자열을 표시해야합니다.왜 일부 큰 정수를 나눠서 파이썬에서 이상한 결과를 반환합니까?

나는 int으로부터 상속받은 TimeUnit 클래스를 가지고 있으며 음수의 시간 단위 생성을 허용하지 않습니다. 그런 다음 문자열을 표시하는 TimeUnits으로 구성된 TimePeriod 클래스를가집니다.

class TimeUnit(int): 
    """A class that defines the semantics of a unit of time i.e. seconds, minutes, hours etc.""" 

    def __new__(cls, x): 
     """Ensure no negative units are created.""" 
     if x < 0: 
      raise ValueError(f'Must be greater than zero') 
     return super().__new__(cls, x) 

    def __eq__(self, other): 
     if isinstance(other, TimeUnit): 
      return int(self.to_seconds()) == other.to_seconds() 
     return super().__eq__(other) 

    @classmethod 
    def from_seconds(cls, seconds): 
     raise NotImplementedError 

    def to_seconds(self): 
     raise NotImplementedError 


class Seconds(TimeUnit): 
    @classmethod 
    def from_seconds(cls, seconds): 
     return cls(seconds) 

    def to_seconds(self): 
     return self 


class Weeks(TimeUnit): 
    @classmethod 
    def from_seconds(cls, seconds): 
     return cls(seconds/60/60/24/7) 

    def to_seconds(self): 
     return Seconds(self * 60 * 60 * 24 * 7) 

x = 249129847219749821374782498 

# Wat? 
x - (Weeks.from_seconds(x).to_seconds()) # -> -2491687902 

방법 249129847219749821374782498 - (Weeks.from_seconds(249129847219749821374782498).to_seconds()) == -2491687902입니다 :

특히, 그것은 나를 혼란이 현상은입니까? 내 TimePeriod 클래스를 사용하여 문자열 형식의 초 수를 나타낼 때 오류가 발생합니다.

class TimePeriod: 
    def __init__(self, *units): 
     self.seconds = Seconds(sum(unit.to_seconds() for unit in units)) 

    def __repr__(self): 
     seconds = self.seconds 

     weeks = Weeks.from_seconds(seconds) 
     seconds -= weeks.to_seconds() 

     days = Days.from_seconds(seconds) 
     seconds -= days.to_seconds() 

     hours = Hours.from_seconds(seconds) 
     seconds -= hours.to_seconds() 

     minutes = Minutes.from_seconds(seconds) 
     seconds -= minutes.to_seconds() 

     seconds = Seconds(seconds) 

     return ' '.join(f'{unit} {unit.__class__.__name__}' for unit in (weeks, days, hours, minutes, seconds) if unit) 

    def __str__(self): 
     return repr(self) 
+1

획기적인 오버플로 소리가납니다. –

답변

1

문제는 당신이 from_seconds에 분할 있다는 사실이다, 이것은 부동 소수점 값으로 정수를 설정합니다. 수레는 제한된 정밀도를 가지므로 일 수 있습니다.은 유효 자릿수를 잃어 버릴 수 있습니다. 그러나 하위 클래스 int을 사용하기 때문에 부동 소수점 수는 아니며 부동 소수점을 저장하기 때문에 정수 값이 그다지 크지 않아도 소수점 부분 만 버려집니다 (예 : Weeks.from_seconds(x)이 반환하는 내용 참조). 결국 to_seconds에 곱 해집니다. 부서의 핵심 부분입니다.

의이 단계로 단계를 통해 가자 : 당신은, 예를 들어, 서브 클래스 또는 단순히 fractions.Fraction을 사용할 수

>>> 249129847219749821374782498/(60*60*24*7) 
4.119210436834488e+20 

>>> int(_) 
411921043683448782848 

>>> _ * (60*60*24*7) 
249129847219749823866470400 

가 올바르게 작동하려면. 이 변환이 제대로이 작동합니다

>>> from fractions import Fraction 
>>> Fraction(x, 60*60*24*7) * 60 * 60 * 24 * 7 
Fraction(249129847219749821374782498, 1) 
>>> int(_) 
249129847219749821374782498 
0

귀하의 부서가 float 반환 "true division"입니다. int은 임의 정밀도 (사용중인 Python 3)이지만 float은 아니며 많은 자릿수를 유지할 수 없습니다 (다른 int에 반올림 됨).