jbe/bsw@0: --[[-- jbe/bsw@0: text = -- text with the value formatted as decimal number jbe/bsw@0: format.decimal( jbe/bsw@0: value, -- a number, a fraction or nil jbe/bsw@0: { jbe/bsw@0: nil_as = nil_text, -- text to be returned for a nil value jbe/bsw@0: digits = digits, -- digits before decimal point jbe/bsw@0: precision = precision, -- digits after decimal point jbe/bsw@0: decimal_shift = decimal_shift -- divide the value by 10^decimal_shift (setting true uses precision) jbe/bsw@0: } jbe/bsw@0: ) jbe/bsw@0: jbe/bsw@0: Formats a (floating point) number or a fraction as a decimal number. If a 'digits' option is set, the number of digits before the decimal point is increased up to the given count by preceding it with zeros. The digits after the decimal point are adjusted by the 'precision' parameter. The 'decimal_shift' parameter is useful, when fixed precision decimal numbers are stored as integers, as the given value will be divided by 10 to the power of the 'decimal_shift' value prior to formatting. Setting 'decimal_shift' to true will copy the value for 'precision'. jbe/bsw@0: jbe/bsw@0: --]]-- jbe/bsw@0: jbe/bsw@0: function format.decimal(value, options) jbe/bsw@0: -- TODO: more features jbe/bsw@0: local options = options or {} jbe/bsw@0: local special_chars = charset.get_data().special_chars jbe/bsw@0: local f jbe/bsw@0: if value == nil then jbe/bsw@0: return options.nil_as or "" jbe/bsw@0: elseif atom.has_type(value, atom.number) then jbe/bsw@0: f = value jbe/bsw@0: elseif atom.has_type(value, atom.fraction) then jbe/bsw@0: f = value.float jbe/bsw@0: else jbe/bsw@0: error("Value passed to format.decimal(...) is neither a number nor a fraction nor nil.") jbe/bsw@0: end jbe/bsw@0: local digits = options.digits jbe/bsw@0: local precision = options.precision or 0 jbe/bsw@0: local decimal_shift = options.decimal_shift or 0 jbe/bsw@0: if decimal_shift == true then jbe/bsw@0: decimal_shift = precision jbe/bsw@0: end jbe/bsw@0: f = f / 10 ^ decimal_shift jbe/bsw@0: local negative jbe/bsw@0: local absolute jbe/bsw@0: if f < 0 then jbe/bsw@0: absolute = -f jbe/bsw@0: negative = true jbe/bsw@0: else jbe/bsw@0: absolute = f jbe/bsw@0: negative = false jbe/bsw@0: end jbe/bsw@0: absolute = absolute + 0.5 / 10 ^ precision jbe/bsw@0: local int = math.floor(absolute) jbe/bsw@0: if not atom.is_integer(int) then jbe/bsw@0: if f > 0 then jbe/bsw@0: return "+" .. special_chars.inf_sign jbe/bsw@0: elseif f < 0 then jbe/bsw@0: return minus_sign .. special_chars.inf_sign jbe/bsw@0: else jbe/bsw@0: return "NaN" jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: local int_str = tostring(int) jbe/bsw@0: if digits then jbe/bsw@0: while #int_str < digits do jbe/bsw@0: int_str = "0" .. int_str jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: if precision > 0 then jbe/bsw@0: local decimal_point = jbe/bsw@0: options.decimal_point or jbe/bsw@0: locale.get('decimal_point') or '.' jbe/bsw@0: local frac_str = tostring(math.floor((absolute - int) * 10 ^ precision)) jbe/bsw@0: while #frac_str < precision do jbe/bsw@0: frac_str = "0" .. frac_str jbe/bsw@0: end jbe/bsw@0: assert(#frac_str == precision, "Assertion failed in format.float(...).") -- should not happen jbe/bsw@0: if negative then jbe/bsw@0: return special_chars.minus_sign .. int_str .. decimal_point .. frac_str jbe/bsw@0: elseif options.show_plus then jbe/bsw@0: return "+" .. int_str .. decimal_point .. frac_str jbe/bsw@0: else jbe/bsw@0: return int_str .. decimal_point .. frac_str jbe/bsw@0: end jbe/bsw@0: else jbe/bsw@0: if negative then jbe/bsw@0: return special_chars.minus_sign .. int jbe/bsw@0: elseif options.show_plus then jbe/bsw@0: return "+" .. int_str jbe/bsw@0: else jbe/bsw@0: return int_str jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: end