class SassC::Script::Value::Number
def self.basically_equal?(num1, num2)
-
(Boolean)
-
def self.basically_equal?(num1, num2) (num1 - num2).abs < epsilon end
def self.epsilon
numbers within an `epsilon` of each other are considered functionally equal.
Used in checking equality of floating point numbers. Any
def self.epsilon Thread.current[:sass_numeric_epsilon] ||= 1 / (precision_factor * 10) end
def self.precision
def self.precision Thread.current[:sass_numeric_precision] || Thread.main[:sass_numeric_precision] || 10 end
def self.precision=(digits)
The numeric precision is stored as a thread local for thread safety reasons.
`3.1415926` will be printed as `3.142`.
For example, if this is `3`,
Sets the number of digits of precision
def self.precision=(digits) Thread.current[:sass_numeric_precision] = digits.round Thread.current[:sass_numeric_precision_factor] = nil Thread.current[:sass_numeric_epsilon] = nil end
def self.precision_factor
the precision factor used in numeric output
def self.precision_factor Thread.current[:sass_numeric_precision_factor] ||= 10.0**precision end
def self.round(num)
- Private: -
def self.round(num) if num.is_a?(Float) && (num.infinite? || num.nan?) num elsif basically_equal?(num % 1, 0.0) num.round else ((num * precision_factor).round / precision_factor).to_f end end
def basically_equal?(num1, num2)
- See: Sass::Script::Number.basically_equal? -
Other tags:
- Private: -
def basically_equal?(num1, num2) self.class.basically_equal?(num1, num2) end
def coerce(num_units, den_units)
-
(Sass::UnitConversionError)
- if the given units are incompatible with the number's
Returns:
-
(Number)
- The number with the new units
Parameters:
-
den_units
(Array
) -- The denominator units to coerce this number into. -
num_units
(Array
) -- The numerator units to coerce this number into.
def coerce(num_units, den_units) Number.new(if unitless? value else value * coercion_factor(@numerator_units, num_units) / coercion_factor(@denominator_units, den_units) end, num_units, den_units) end
def coercion_factor(from_units, to_units)
def coercion_factor(from_units, to_units) # get a list of unmatched units from_units, to_units = sans_common_units(from_units, to_units) if from_units.size != to_units.size || !convertable?(from_units | to_units) raise Sass::UnitConversionError.new( "Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.") end from_units.zip(to_units).inject(1) {|m, p| m * conversion_factor(p[0], p[1])} end
def comparable_to?(other)
-
(Boolean)
- Whether or not this number can be compared with the other.
Parameters:
-
other
(Number
) -- A number to decide if it can be compared with this number.
def comparable_to?(other) operate(other, :+) true rescue Sass::UnitConversionError false end
def compute_units(this, other, operation)
def compute_units(this, other, operation) case operation when :* [this.numerator_units + other.numerator_units, this.denominator_units + other.denominator_units] when :/ [this.numerator_units + other.denominator_units, this.denominator_units + other.numerator_units] else [this.numerator_units, this.denominator_units] end end
def conversion_factor(from_unit, to_unit)
def conversion_factor(from_unit, to_unit) CONVERSION_TABLE[from_unit][to_unit] end
def convertable?(units)
def convertable?(units) units = Array(units).to_set return true if units.empty? return false unless (mutually_convertible = MUTUALLY_CONVERTIBLE[units.first]) units.subset?(mutually_convertible) end
def eql?(other)
Hash-equality must be transitive, so it just compares the exact value,
Hash-equality works differently than `==` equality for numbers.
def eql?(other) basically_equal?(value, other.value) && numerator_units == other.numerator_units && denominator_units == other.denominator_units end
def hash
def hash [value, numerator_units, denominator_units].hash end
def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
-
denominator_units
(::String, Array<::String>
) -- See \{#denominator\_units} -
numerator_units
(::String, Array<::String>
) -- See \{#numerator\_units} -
value
(Numeric
) -- The value of the number
def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS) numerator_units = [numerator_units] if numerator_units.is_a?(::String) denominator_units = [denominator_units] if denominator_units.is_a?(::String) super(value) @numerator_units = numerator_units @denominator_units = denominator_units @options = nil normalize! end
def inspect(opts = {})
-
(String)
- The representation
def inspect(opts = {}) return original if original value = self.class.round(self.value) str = value.to_s # Ruby will occasionally print in scientific notation if the number is # small enough. That's technically valid CSS, but it's not well-supported # and confusing. str = ("%0.#{self.class.precision}f" % value).gsub(/0*$/, '') if str.include?('e') # Sometimes numeric formatting will result in a decimal number with a trailing zero (x.0) if str =~ /(.*)\.0$/ str = $1 end # We omit a leading zero before the decimal point in compressed mode. if @options && options[:style] == :compressed str.sub!(/^(-)?0\./, '\1.') end unitless? ? str : "#{str}#{unit_str}" end
def int?
-
(Boolean)
- Whether or not this number is an integer.
def int? basically_equal?(value % 1, 0.0) end
def is_unit?(unit)
- See: Number#unitless? - The unitless? method may be more readable.
Parameters:
-
unit
(::String, nil
) -- The unit the number should have or nil if the number
def is_unit?(unit) if unit denominator_units.size == 0 && numerator_units.size == 1 && numerator_units.first == unit else unitless? end end
def legal_units?
-
(Boolean)
- Whether or not this number has units that can be represented in CSS
def legal_units? (@numerator_units.empty? || @numerator_units.size == 1) && @denominator_units.empty? end
def normalize!
def normalize! return if unitless? @numerator_units, @denominator_units = sans_common_units(@numerator_units, @denominator_units) @denominator_units.each_with_index do |d, i| next unless convertable?(d) && (u = @numerator_units.find {|n| convertable?([n, d])}) @value /= conversion_factor(d, u) @denominator_units.delete_at(i) @numerator_units.delete_at(@numerator_units.index(u)) end end
def operate(other, operation)
def operate(other, operation) this = self if OPERATIONS.include?(operation) if unitless? this = this.coerce(other.numerator_units, other.denominator_units) else other = other.coerce(@numerator_units, @denominator_units) end end # avoid integer division value = :/ == operation ? this.value.to_f : this.value result = value.send(operation, other.value) if result.is_a?(Numeric) Number.new(result, *compute_units(this, other, operation)) else # Boolean op Bool.new(result) end end
def sans_common_units(units1, units2)
def sans_common_units(units1, units2) units2 = units2.dup # Can't just use -, because we want px*px to coerce properly to px*mm units1 = units1.map do |u| j = units2.index(u) next u unless j units2.delete_at(j) nil end units1.compact! return units1, units2 end
def to_i
-
(Sass::SyntaxError)
- if the number isn't an integer
Returns:
-
(Integer)
- The integer value of the number
def to_i super unless int? value.to_i end
def to_s(opts = {})
-
(Sass::SyntaxError)
- if this number has units that can't be used in CSS
Returns:
-
(String)
- The CSS representation of this number
def to_s(opts = {}) return original if original raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units? inspect end
def unit_str
-
(String)
- a string that represents the units in this number
def unit_str rv = @numerator_units.sort.join("*") if @denominator_units.any? rv << "/" rv << @denominator_units.sort.join("*") end rv end
def unitless?
-
(Boolean)
- Whether or not this number has no units.
def unitless? @numerator_units.empty? && @denominator_units.empty? end