def __init__(self, amount, unit=None, unit_numer=(), unit_denom=()): if isinstance(amount, Number): assert not unit and not unit_numer and not unit_denom self.value = amount.value self.unit_numer = amount.unit_numer self.unit_denom = amount.unit_denom return if not isinstance(amount, (int, float)): raise TypeError("Expected number, got %r" % (amount,)) if unit is not None: unit_numer = unit_numer + (unit.lower(),) # Cancel out any convertable units on the top and bottom numerator_base_units = count_base_units(unit_numer) denominator_base_units = count_base_units(unit_denom) # Count which base units appear both on top and bottom cancelable_base_units = {} for unit, count in numerator_base_units.items(): cancelable_base_units[unit] = min( count, denominator_base_units.get(unit, 0)) # Actually remove the units numer_factor, unit_numer = cancel_base_units(unit_numer, cancelable_base_units) denom_factor, unit_denom = cancel_base_units(unit_denom, cancelable_base_units) # And we're done self.unit_numer = tuple(unit_numer) self.unit_denom = tuple(unit_denom) self.value = amount * (numer_factor / denom_factor)
def __init__(self, amount, unit=None, unit_numer=(), unit_denom=()): if isinstance(amount, Number): assert not unit and not unit_numer and not unit_denom self.value = amount.value self.unit_numer = amount.unit_numer self.unit_denom = amount.unit_denom return if not isinstance(amount, (int, float)): raise TypeError("Expected number, got %r" % (amount, )) if unit is not None: unit_numer = unit_numer + (unit.lower(), ) # Cancel out any convertable units on the top and bottom numerator_base_units = count_base_units(unit_numer) denominator_base_units = count_base_units(unit_denom) # Count which base units appear both on top and bottom cancelable_base_units = {} for unit, count in numerator_base_units.items(): cancelable_base_units[unit] = min( count, denominator_base_units.get(unit, 0)) # Actually remove the units numer_factor, unit_numer = cancel_base_units(unit_numer, cancelable_base_units) denom_factor, unit_denom = cancel_base_units(unit_denom, cancelable_base_units) # And we're done self.unit_numer = tuple(unit_numer) self.unit_denom = tuple(unit_denom) self.value = amount * (numer_factor / denom_factor)
def __init__(self, amount, unit=None, unit_numer=(), unit_denom=()): if isinstance(amount, Number): assert not unit and not unit_numer and not unit_denom self.value = amount.value self.unit_numer = amount.unit_numer self.unit_denom = amount.unit_denom return # Numbers with units are stored internally as a "base" unit, which can # involve float division, which can lead to precision errors in obscure # cases. Storing the original units would only partially solve this # problem, because there'd still be a possible loss of precision when # converting in Sass-land. Almost all of the conversion factors are # simple ratios of small whole numbers, so using Fraction across the # board preserves as much precision as possible. # TODO in fact, i wouldn't mind parsing Sass values as fractions of a # power of ten! # TODO this slowed the test suite down by about 10%, ha if isinstance(amount, (int, float)): amount = Fraction.from_float(amount) elif isinstance(amount, Fraction): pass else: raise TypeError("Expected number, got %r" % (amount, )) if unit is not None: unit_numer = unit_numer + (unit.lower(), ) # Cancel out any convertable units on the top and bottom numerator_base_units = count_base_units(unit_numer) denominator_base_units = count_base_units(unit_denom) # Count which base units appear both on top and bottom cancelable_base_units = {} for unit, count in numerator_base_units.items(): cancelable_base_units[unit] = min( count, denominator_base_units.get(unit, 0)) # Actually remove the units numer_factor, unit_numer = cancel_base_units(unit_numer, cancelable_base_units) denom_factor, unit_denom = cancel_base_units(unit_denom, cancelable_base_units) # And we're done self.unit_numer = tuple(unit_numer) self.unit_denom = tuple(unit_denom) self.value = amount * (numer_factor / denom_factor)
def __init__(self, amount, unit=None, unit_numer=(), unit_denom=()): if isinstance(amount, Number): assert not unit and not unit_numer and not unit_denom self.value = amount.value self.unit_numer = amount.unit_numer self.unit_denom = amount.unit_denom return # Numbers with units are stored internally as a "base" unit, which can # involve float division, which can lead to precision errors in obscure # cases. Storing the original units would only partially solve this # problem, because there'd still be a possible loss of precision when # converting in Sass-land. Almost all of the conversion factors are # simple ratios of small whole numbers, so using Fraction across the # board preserves as much precision as possible. # TODO in fact, i wouldn't mind parsing Sass values as fractions of a # power of ten! # TODO this slowed the test suite down by about 10%, ha if isinstance(amount, (int, float)): amount = Fraction.from_float(amount) elif isinstance(amount, Fraction): pass else: raise TypeError("Expected number, got %r" % (amount,)) if unit is not None: unit_numer = unit_numer + (unit.lower(),) # Cancel out any convertable units on the top and bottom numerator_base_units = count_base_units(unit_numer) denominator_base_units = count_base_units(unit_denom) # Count which base units appear both on top and bottom cancelable_base_units = {} for unit, count in numerator_base_units.items(): cancelable_base_units[unit] = min( count, denominator_base_units.get(unit, 0)) # Actually remove the units numer_factor, unit_numer = cancel_base_units(unit_numer, cancelable_base_units) denom_factor, unit_denom = cancel_base_units(unit_denom, cancelable_base_units) # And we're done self.unit_numer = tuple(unit_numer) self.unit_denom = tuple(unit_denom) self.value = amount * (numer_factor / denom_factor)