Every NumberF function on one page. All examples are real output.

Installation

mix.exs

def deps do
  [{:number_f, "~> 0.3.0"}]
end

Usage

No configuration, no setup, no supervision tree entry. Call everything off NumberF:

NumberF.currency(1234.5)
# => "ZMW 1,234.50"

Currency & number formatting

Currency

NumberF.currency(1234.567)
# => "ZMW 1,234.57"

NumberF.currency(1234.567, "USD")
# => "USD 1,234.57"

NumberF.currency(1234.567, "USD", 0)
# => "USD 1,235"

Delimiters

NumberF.comma_separated(1234567.89)
# => "1,234,567.89"

NumberF.number_to_delimited(1234567.89,
  delimiter: ".", separator: ",")
# => "1.234.567,89"

Abbreviation

NumberF.abbreviate_number(1_234_567)
# => "1.2M"

NumberF.abbreviate_number(1_234_567, 2)
# => "1.23M"

NumberF.abbreviate_number(1_500_000_000_000)
# => "1.5T"

NumberF.abbreviate_number(-5000)
# => "-5.0K"

Percentages

NumberF.percentage(25, 200)
# => 12.5

NumberF.percentage(25, 200, 2)
# => 12.5

Numbers to text

Words (currency style)

NumberF.to_words(42)
# => "Forty Two Kwacha"

NumberF.to_words(42.75, "Dollars", "Cents")
# => "Forty Two Dollars And Seventy Five Cents"

Words (multilingual)

NumberF.spell_number(42, "en")
# => "Forty-two"

NumberF.spell_number(42, "fr")
# => "Quarante-deux"

# Fractions are read digit by digit, so a
# leading zero survives:
NumberF.spell_number(1.05, "en")
# => "One point zero five"

NumberF.spelling_languages() => ["de", "en", "es", "fr"]. Any other language raises ArgumentError rather than quietly returning English.

Ordinals

NumberF.ordinal(1)    # => "1st"
NumberF.ordinal(21)   # => "21st"
NumberF.ordinal(112)  # => "112th"

Roman numerals

NumberF.to_roman(1999)     # => "MCMXCIX"
NumberF.from_roman("MCMXCIX")  # => 1999

Internationalization

Locale-aware numbers

NumberF.format_number(1234567.89, "en-US")
# => "1,234,567.89"

NumberF.format_number(1234567.89, "fr-FR")
# => "1 234 567,89"

NumberF.format_number(1234567.89, "de-DE")
# => "1.234.567,89"

Locale-aware currency

NumberF.format_currency(1234.56, "en-US")
# => "$1,234.56"

NumberF.format_currency(1234.56, "fr-FR")
# => "1 234,56 €"

NumberF.format_currency(1234.56, "de-DE",
  currency_code: "USD")
# => "1.234,56 $"

Currency metadata

NumberF.get_currency("USD")
# => %{name: "US Dollar", symbol: "$", ...}

NumberF.currency_data()
# => %{"USD" => %{symbol: "$", ...}, ...}

Explicit currency codes

NumberF.format_with_currency(1234.56, "JPY")
# => "¥1,235"    (JPY has 0 decimal places)

Financial calculations

Interest

NumberF.simple_interest(1000, 0.05, 2)
# => 100.0

NumberF.compound_interest(1000, 0.05, 2)
# => 102.5

# 12 compounding periods per year
NumberF.compound_interest(1000, 0.05, 2, 12)
# => 104.94

Loan repayment

# principal, annual rate, months
NumberF.calculate_emi(100_000, 0.10, 12)
# => 8791.59

Currency conversion

# amount, from_rate, to_rate
NumberF.convert_currency(100, 1.0, 0.85)
# => 85.0

Decimal-safe sums

NumberF.sum_decimal([
  Decimal.new("1.10"),
  Decimal.new("2.20")
])
# => Decimal.new("3.30")

Tax

VAT

NumberF.calculate_vat(100, 0.2)
# => %{net: 100.0, vat: 20.0, gross: 120.0}

# VAT-inclusive: extract tax from gross
NumberF.calculate_vat(120, 0.2, true)
# => %{net: 100.0, vat: 20.0, gross: 120.0}

Sales tax

NumberF.calculate_sales_tax(100, 0.06)
# => %{subtotal: 100.0, tax: 6.0, total: 106.0}

NumberF.calculate_sales_tax(100, 0.0625, round_to: 0.05)
# => %{subtotal: 100.0, tax: 6.25, total: 106.25}

Income tax (progressive)

brackets = NumberF.Tax.income_tax_brackets()["US"]

NumberF.Tax.calculate_income_tax(75_000, brackets)
# => %{tax: 12248.5, effective_rate: 0.1633}

Other taxes

NumberF.Tax.calculate_capital_gains_tax(50_000, 0.15)
# => %{gain: 50000.0, taxable_gain: 50000.0,
#      tax: 7500.0, net: 42500.0}

NumberF.Tax.calculate_corporate_tax(500_000, 0.21)
NumberF.Tax.calculate_withholding_tax(1000, 0.15)
NumberF.Tax.calculate_payroll_tax(60_000, 0.062, 0.0145)

NumberF.vat_rates()          # VAT rate by country
NumberF.Tax.income_tax_brackets()
# => %{"US" => [...], "UK" => [...],
#      "Germany" => [...], "Zambia" => [...]}

Statistics

Central tendency

NumberF.mean([1, 2, 3, 4, 5])      # => 3.0
NumberF.median([1, 3, 5, 7, 9])    # => 5
NumberF.mode([1, 2, 2, 3, 3, 3])   # => [3]

Spread

variance/1 and standard_deviation/1 are the population forms (÷N). Use the sample_* variants (÷N-1) when the list is a sample — which is usually what you want.

NumberF.variance([1, 2, 3, 4, 5])
# => 2.0

NumberF.standard_deviation([2,4,4,4,5,5,7,9])
# => 2.0

NumberF.sample_variance([2,4,4,4,5,5,7,9])
# => 4.571428571428571

NumberF.sample_standard_deviation([2,4,4,4,5,5,7,9])
# => 2.138089935299395

NumberF.range([1, 5, 9])   # => 8.0

Precision & rounding

Bankers rounding (round half to even)

NumberF.bankers_round(2.5, 0)   # => 2.0
NumberF.bankers_round(3.5, 0)   # => 4.0

Use this for financial totals — it avoids the upward bias of always rounding .5 away from zero.

Directional rounding

ceiling/2 and floor/2 take decimal places; round_to/3 takes an increment.

NumberF.ceiling(2.111, 2)         # => 2.12
NumberF.floor(2.999, 2)           # => 2.99

NumberF.round_to(127, 5)          # => 125.0
NumberF.round_to(2.567, 0.05)     # => 2.55
NumberF.round_to(2.5, 1, :up)     # => 3.0

NumberF.round_to_nearest(127, 5)  # => 125.0

Guards

NumberF.clamp(15, 0, 10)         # => 10
NumberF.safe_divide(10, 0)       # => 0.0
NumberF.safe_divide(10, 0, :none) # => :none
NumberF.sign(-4.2)               # => -1

Float comparison

NumberF.approximately_equal(0.1 + 0.2, 0.3)
# => true

NumberF.Precision.sanitize_float(:nan)

Never compare floats with ==. Use approximately_equal/3 with an explicit epsilon.

Precision helpers

NumberF.round_with_precision(3.14159, 2)
# => 3.14

NumberF.Precision.custom_round(2.565, 2, :half_down)
# => 2.56

NumberF.Precision.truncate(3.999, 2)
# => 3.99

NumberF.Precision.precise_format(3.14159, 3)
# => "3.142"

Validation

Credit cards (Luhn)

NumberF.is_valid_credit_card?("4111111111111111")
# => true

NumberF.is_valid_credit_card?("4111-1111-1111-1111")
# => true

Number formats

NumberF.is_valid_number?("1234.56")   # => true
NumberF.is_valid_integer?("1234")     # => true
NumberF.in_range?(5, 1, 10)           # => true

Unit conversion

Temperature

NumberF.celsius_to_fahrenheit(25)   # => 77.0
NumberF.fahrenheit_to_celsius(77)   # => 25.0

Length & distance

NumberF.km_to_miles(100)
NumberF.miles_to_km(62)
NumberF.cm_to_inches(30)
NumberF.inches_to_cm(12)

Weight & volume

NumberF.kg_to_pounds(70)
NumberF.Metrics.pounds_to_kg(154)
NumberF.Metrics.ml_to_oz(500)
NumberF.Metrics.oz_to_ml(16)

Generic conversion

Unit keys are strings, not atoms.

NumberF.Metrics.convert_units(100, "km", "mi",
  NumberF.Metrics.length_units())
# => 62.137...

Unit tables: length_units/0, weight_units/0, volume_units/0, area_units/0.

Dates

Age & spans

NumberF.calculate_age(~D[1990-01-15])
NumberF.days_between(~D[2025-01-01], ~D[2025-12-31])

Business days

NumberF.is_business_day?(~D[2025-06-14])
NumberF.business_days_between(
  ~D[2025-01-01], ~D[2025-01-31])

Payment terms

NumberF.payment_due_date(~D[2025-01-01])
NumberF.payment_due_date(~D[2025-01-01], 60)

Advanced statistics

Percentiles and spread

NumberF.percentile([1,2,3,4,5,6,7,8,9,10], 25)
# => 3.25

NumberF.quartiles([1,2,3,4,5,6,7,8])
# => %{q1: 2.75, q2: 4.5, q3: 6.25}

NumberF.iqr([1,2,3,4,5,6,7,8])
# => 3.5

Relationships between two series

NumberF.correlation([1,2,3,4,5], [2,4,5,4,5])
# => 0.7745966692414833

NumberF.covariance([1,2,3,4,5], [2,4,5,4,5])
# => 1.2

NumberF.linear_regression([1,2,3,4,5], [2,4,5,4,5])
# => %{slope: 0.6, intercept: 2.2,
#      r_squared: 0.5999999999999999}

Other means

NumberF.weighted_mean([90,80,70], [0.5,0.3,0.2])
# => 83.0

# Growth rates: pass factors, not percentages
NumberF.geometric_mean([1, 4, 16])
# => 4.0

# Rates over a fixed distance
NumberF.harmonic_mean([40, 60])
# => 47.99999999999999  (48, modulo IEEE 754)

Describing and reshaping

NumberF.summary([1,2,3,4,5])
# => %{count: 5, min: 1, q1: 2.0, median: 3,
#      q3: 4.0, max: 5, mean: 3.0, stddev: 1.414...}

NumberF.z_scores([2, 4, 6])
# => [-1.2247448713915889, 0.0, 1.2247448713915889]

NumberF.normalize([10, 20, 30])   # => [0.0, 0.5, 1.0]
NumberF.moving_average([1,2,3,4,5], 3) # => [2.0, 3.0, 4.0]
NumberF.cumulative_sum([1,2,3,4])      # => [1, 3, 6, 10]
NumberF.outliers([1,2,3,4,1000])       # => [1000]
NumberF.trimmed_mean([1,2,3,4,100], 0.2) # => 3.0

Investment and loans

Time value of money

NumberF.present_value(1000, 0.05, 10)  # => 613.91
NumberF.future_value(1000, 0.05, 10)   # => 1628.89

NumberF.npv(0.1, [-1000, 300, 400, 500, 600])
# => 388.77

NumberF.irr([-1000, 300, 400, 500, 600])
# => {:ok, 0.24888...}

irr/2 returns {:error, :no_sign_change} when the cash flows never cross zero — there is no rate that zeroes an all-positive series.

Amortization

NumberF.amortization_schedule(100_000, 0.10, 12)
# => [%{period: 1, payment: 8791.59,
#       principal: 7958.26, interest: 833.33,
#       balance: 92041.74}, ...]

The last period absorbs accumulated rounding, so the balance closes at exactly 0.0.

Returns and break-even

NumberF.roi(1500, 1000)              # => 50.0
NumberF.cagr(1000, 2000, 5)          # => 14.87
NumberF.effective_annual_rate(0.12, 12) # => 0.1268
NumberF.break_even_point(10_000, 25, 15) # => 1000.0
NumberF.payback_period(1000, [300,400,500])
# => {:ok, 2.6}

Annuities and depreciation

NumberF.annuity_payment(10_000, 0.05, 10)    # => 1295.05
NumberF.annuity_present_value(100, 0.05, 10) # => 772.17
NumberF.annuity_future_value(100, 0.05, 10)  # => 1257.79

NumberF.depreciation_straight_line(10_000, 1_000, 5)
# => 1800.0

NumberF.depreciation_declining_balance(10_000, 1_000, 5)
# => [%{year: 1, depreciation: 4000.0,
#       book_value: 6000.0}, ...]

Identifiers and checksums

Financial identifiers

NumberF.valid_iban?("GB82 WEST 1234 5698 7654 32")
# => true

NumberF.valid_routing_number?("021000021")  # => true
NumberF.valid_currency_code?("USD")         # => true

Cards

NumberF.card_brand("4111111111111111")  # => :visa
NumberF.card_brand("5500000000000004")  # => :mastercard
NumberF.card_brand("378282246310005")   # => :amex

NumberF.valid_luhn?("4111111111111111")     # => true
NumberF.luhn_check_digit("411111111111111") # => 1

card_brand/1 identifies the network from the prefix and length. It does not validate — pair it with is_valid_credit_card?/1.

Products and publications

NumberF.valid_isbn?("978-3-16-148410-0")  # => true
NumberF.valid_isbn?("0-306-40615-2")      # => true  (ISBN-10)
NumberF.valid_ean?("4006381333931")       # => true
NumberF.valid_upc?("036000291452")        # => true
NumberF.valid_imei?("490154203237518")    # => true

Number theory and bases

Sequences and factors

NumberF.fibonacci(10)            # => 55
NumberF.fibonacci_sequence(8)    # => [0,1,1,2,3,5,8,13]
NumberF.permutations(5, 2)       # => 20
NumberF.prime_factors(360)       # => [2,2,2,3,3,5]
NumberF.divisors(28)             # => [1,2,4,7,14,28]

fibonacci/1 and combinations/2 return exact integers at any size — fibonacci(100) is the true value, not a float approximation.

Digits

NumberF.digits(12345)         # => [1,2,3,4,5]
NumberF.digit_sum(12345)      # => 15
NumberF.digital_root(12345)   # => 6
NumberF.reverse_number(12345) # => 54321
NumberF.palindrome?(12321)    # => true
NumberF.perfect_square?(144)  # => true

Roots and logs

NumberF.nth_root(27, 3)     # => 3.0
NumberF.log_base(1024, 2)   # => 10.0

Base conversion

NumberF.to_base(255, 16)     # => "FF"
NumberF.from_base("FF", 16)  # => 255

NumberF.to_hex(255)      # => "FF"
NumberF.to_binary(5)     # => "101"
NumberF.to_octal(8)      # => "10"
NumberF.from_binary("101")  # => 5

Any base from 2 to 36.

Presentation

Accounting and signs

NumberF.accounting_format(-1234.56, unit: "$")
# => "($1,234.56)"

NumberF.with_sign(42)             # => "+42"
NumberF.with_sign(-42)            # => "-42"
NumberF.pad_number(42, 6, char: "0")  # => "000042"
NumberF.pluralize(3, "person", "people")
# => "3 people"

Sizes and durations

NumberF.format_bytes(1_500_000)          # => "1.5 MB"
NumberF.format_bytes(1_048_576, base: :binary)
# => "1.0 MiB"

NumberF.format_duration(3725)            # => "1h 2m 5s"
NumberF.format_duration(3725, format: :clock)
# => "01:02:05"
NumberF.format_duration(90, format: :long)
# => "1 minute, 30 seconds"

Significant figures

NumberF.significant_figures(1234.5678, 3)  # => 1230.0
NumberF.significant_figures(0.00012345, 3) # => 0.000123
NumberF.format_significant(1.5, 4)         # => "1.500"

Significant figures count from the first non-zero digit, so they are scale independent in a way that decimal places are not.

Calendar

Quarters and fiscal years

NumberF.quarter(~D[2024-08-22])       # => 3
NumberF.quarter_start(~D[2024-08-22]) # => ~D[2024-07-01]
NumberF.quarter_end(~D[2024-08-22])   # => ~D[2024-09-30]

# Named for the year the fiscal year ends in
NumberF.fiscal_year(~D[2024-08-22], 4) # => 2025

Months and weeks

NumberF.month_start(~D[2024-08-22]) # => ~D[2024-08-01]
NumberF.month_end(~D[2024-02-10])   # => ~D[2024-02-29]
NumberF.days_in_month(2024, 2)      # => 29
NumberF.leap_year?(1900)            # => false
NumberF.week_number(~D[2024-08-22]) # => 34
NumberF.age_in_months(~D[2020-01-15], ~D[2024-08-22])
# => 55

Conversion & utilities

Type coercion

NumberF.to_int("42")        # => 42
NumberF.to_float("3.14")    # => 3.14
NumberF.to_decimal("1.10")
NumberF.to_boolean("true")  # => true

Memory sizes

NumberF.memory_size_cal(1_048_576)
# => "1.0 MB"

NumberF.memory_size_cal(5_000_000_000_000)
# => "4.55 TB"

Phone numbers

NumberF.format_phone("260977123456", "ZM")
NumberF.format_phone("14155552671", "US")

Random strings

NumberF.randomizer(8)
# => "RCKkn94U"  (random; yours will differ)

NumberF.default_password()

Math

NumberF.is_prime?(17)          # => true
NumberF.factorial(5)           # => 120
NumberF.gcd(12, 18)            # => 6
NumberF.lcm(4, 6)              # => 12
NumberF.combinations(5, 2)     # => 10
NumberF.to_radians(180)
NumberF.to_degrees(3.14159)
NumberF.interpolate(0, 100, 0, 10, 5)

Introspection

NumberF.modules()             # all NumberF modules
NumberF.modules_by_type(:formatting)
NumberF.function_categories() # list of category maps
NumberF.documentation()       # generated reference