{"id":17113002,"url":"https://github.com/emmt/unitless.jl","last_synced_at":"2025-09-10T08:06:57.695Z","repository":{"id":44457598,"uuid":"499420068","full_name":"emmt/Unitless.jl","owner":"emmt","description":"Strip units from quantities","archived":false,"fork":false,"pushed_at":"2024-05-15T09:17:44.000Z","size":73,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-23T22:34:35.274Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emmt.png","metadata":{"files":{"readme":"README.md","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-03T07:29:42.000Z","updated_at":"2024-05-15T08:51:01.000Z","dependencies_parsed_at":"2024-01-17T15:02:46.363Z","dependency_job_id":"e4332f66-b244-428c-b52b-f6cd3f4e8ced","html_url":"https://github.com/emmt/Unitless.jl","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FUnitless.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FUnitless.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FUnitless.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FUnitless.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emmt","download_url":"https://codeload.github.com/emmt/Unitless.jl/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248107875,"owners_count":21049025,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T17:02:17.099Z","updated_at":"2025-04-09T20:33:47.478Z","avatar_url":"https://github.com/emmt.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Unitless\n\n[![License](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](./LICENSE.md) [![Build Status](https://github.com/emmt/Unitless.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/emmt/Unitless.jl/actions/workflows/CI.yml?query=branch%3Amain) [![Build Status](https://ci.appveyor.com/api/projects/status/github/emmt/Unitless.jl?svg=true)](https://ci.appveyor.com/project/emmt/Unitless-jl) [![Coverage](https://codecov.io/gh/emmt/Unitless.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/emmt/Unitless.jl)\n\n`Unitless` is now completely superseded by\n[`TypeUtils`](https://github.com/emmt/TypeUtils.jl) and only exists for\nbackward compatibility.\n\n`Unitless` is a small [Julia](https://julialang.org/) package to facilitate\ncoding with numbers whether they have units or not. The package provides\nmethods to strip units from numbers or numeric types, convert the numeric type\nof quantities (not their units), determine appropriate numeric type to carry\ncomputations mixing numbers with different types and/or units. These methods\nmake it easy to write code that works consistently for numbers with any units\n(including none). The intention is that the `Unitless` package automatically\nextends its exported methods when packages such as\n[`Unitful`](https://github.com/PainterQubits/Unitful.jl) are loaded.\n\nThe `Unitless` package exports a few methods:\n\n* `unitless(x)` yields `x` without its units, if any. `x` can be a number or a\n  numeric type. In the latter case, `unitless` behaves like `bare_type`\n  described below.\n\n* `bare_type(x)` yields the bare numeric type of `x` (a numeric value or type).\n  If this method is not extended for a specific type, the fallback\n  implementation yields `typeof(one(x))`. With more than one argument,\n  `bare_type(args...)` yields the type resulting from promoting the bare\n  numeric types of `args...`. With no argument, `bare_type()` yields\n  `Unitless.BareNumber` the union of bare numeric types that may be returned by\n  this method.\n\n* `real_type(x)` yields the bare real type of `x` (a numeric value or type). If\n  this method is not extended for a specific type, the fallback implementation\n  yields `typeof(one(real(x))`. With more than one argument,\n  `real_type(args...)` yields the type resulting from promoting the bare real\n  types of `args...`. With no argument, `real_type()` yields `Real` the\n  super-type of types that may be returned by this method.\n\n* `floating_point_type(args...)` yields a floating-point type appropriate to\n  represent the bare real type of `args...`. With no argument,\n  `floating_point_type()` yields `AbstractFloat` the super-type of types that\n  may be returned by this method. You may consider\n  `floating_point_type(args...)` as an equivalent to\n  to`float(real_type(args...))`.\n\n* `convert_bare_type(T,x)` converts the bare numeric type of `x` to the bare\n  numeric type of `T` while preserving the units of `x` if any. Argument `x`\n  may be a number or a numeric type, while argument `T` must be a numeric type.\n  If `x` is one of `missing`, `nothing`, `undef`, or the type of one of these\n  singletons, `x` is returned.\n\n* `convert_real_type(T,x)` converts the bare real type of `x` to the bare real\n  type of `T` while preserving the units of `x` if any. Argument `x` may be a\n  number or a numeric type, while argument `T` must be a numeric type. If `x`\n  is one of `missing`, `nothing`, `undef`, or the type of one of these\n  singletons, `x` is returned.\n\n* `convert_floating_point_type(T,x)` converts the bare real type of `x` to the\n  suitable floating-point type for type `T` while preserving the units of `x`\n  if any. Argument `x` may be a number or a numeric type, while argument `T`\n  must be a numeric type. If `x` is one of `missing`, `nothing`, `undef`, or\n  the type of one of these singletons, `x` is returned. You may consider\n  `convert_floating_point_type(T,x)` as an equivalent to\n  to `convert_real_type(float(real_type(T)),x)`.\n\nThe only difference between `bare_type` and `real_type` is how they treat\ncomplex numbers. The former preserves the complex kind of its argument while\nthe latter always returns a real type. You may assume that `real_type(x) =\nreal(bare_type(x))`. Conversely, `convert_bare_type(T,x)` yields a complex\nresult if `T` is complex and a real result if `T` is real whatever `x`, while\n`convert_real_type(T,x)` yields a complex result if `x` is complex and a real\nresult if `x` is real, only the real part of `T` matters for\n`convert_real_type(T,x)`. See examples below.\n\n\n## Examples\n\nThe following examples illustrate the result of the methods provided by\n`Unitful`, first with bare numbers and bare numeric types, then with\nquantities:\n\n```julia\njulia\u003e using Unitless\n\njulia\u003e map(unitless, (2.1, Float64, true, ComplexF32))\n(2.1, Float64, true, ComplexF32)\n\njulia\u003e map(bare_type, (1, 3.14f0, true, 1//3, π, 1.0 - 2.0im))\n(Int64, Float32, Bool, Rational{Int64}, Irrational{:π}, Complex{Float64})\n\njulia\u003e map(real_type, (1, 3.14f0, true, 1//3, π, 1.0 - 2.0im))\n(Int64, Float32, Bool, Rational{Int64}, Irrational{:π}, Float64)\n\njulia\u003e map(x -\u003e convert_bare_type(Float32, x), (2, 1 - 0im, 1//2, Bool, Complex{Float64}))\n(2.0f0, 1.0f0, 0.5f0, Float32, Float32)\n\njulia\u003e map(x -\u003e convert_real_type(Float32, x), (2, 1 - 0im, 1//2, Bool, Complex{Float64}))\n(2.0f0, 1.0f0 + 0.0f0im, 0.5f0, Float32, ComplexF32)\n\njulia\u003e using Unitful\n\njulia\u003e map(unitless, (u\"2.1GHz\", typeof(u\"2.1GHz\")))\n(2.1, Float64)\n\njulia\u003e map(bare_type, (u\"3.2km/s\", u\"5GHz\", typeof((0+1im)*u\"Hz\")))\n(Float64, Int64, Complex{Int64})\n\njulia\u003e map(real_type, (u\"3.2km/s\", u\"5GHz\", typeof((0+1im)*u\"Hz\")))\n(Float64, Int64, Int64)\n```\n\n\n## Rationale\n\nThe following example shows a first attempt to use `bare_type` to implement\nefficient in-place multiplication of an array (whose element may have units) by\na real factor (which must be unitless in this context):\n\n```julia\nfunction scale!(A::AbstractArray, α::Number)\n    alpha = convert_bare_type(eltype(A), α)\n    @inbounds @simd for i in eachindex(A)\n        A[i] *= alpha\n    end\n    return A\nend\n```\n\nAn improvement is to realize that when `α` is a real while the entries of `A`\nare complexes, it is more efficient to multiply the entries of `A` by a\nreal-valued multiplier rather than by a complex one. Implementing this is as\nsimple as replacing `convert_bare_type` by `convert_real_type` to only convert\nthe bare real type of the multiplier while preserving its complex/real kind:\n\n```julia\nfunction scale!(A::AbstractArray, α::Number)\n    alpha = convert_real_type(eltype(A), α)\n    @inbounds @simd for i in eachindex(A)\n        A[i] *= alpha\n    end\n    return A\nend\n```\n\nThis latter version consistently and efficiently deals with `α` being real\nwhile the entries of `A` are reals or complexes, and with `α` and the entries\nof `A` being complexes. If `α` is a complex and the entries of `A` are reals,\nthe statement `A[i] *= alpha` will throw an `InexactConversion` if the\nimaginary part of `α` is not zero. This check is probably optimized out of the\nloop by Julia but, to handle this with guaranteed no loss of efficiency, the\ncode can be written as:\n\n```julia\nfunction scale!(A::AbstractArray, α::Union{Real,Complex})\n    alpha = if α isa Complex \u0026\u0026 bare_type(eltype(A)) isa Real\n        convert(real_type(eltype(A)), α)\n    else\n        convert_real_type(eltype(A), α)\n    end\n    @inbounds @simd for i in eachindex(A)\n        A[i] *= alpha\n    end\n    return A\nend\n```\n\nThe restriction `α::Union{Real,Complex}` accounts for the fact that in-place\nmultiplication imposes a unitless multiplier. Since the test leading to the\nexpression used for `alpha` is based on the types of the arguments, the branch\nis eliminated at compile time and the type of `alpha` is known by the compiler.\nThe `InexactConversion` exception may then only be thrown by the call to\n`convert` in the first branch of the test.\n\nThis seemingly very specific case was in fact the key point to allow for\npackages such as [LazyAlgebra](https://github.com/emmt/LazyAlgebra.jl) or\n[LinearInterpolators](https://github.com/emmt/LinearInterpolators.jl) to work\nseamlessly on arrays whose entries may have units. The `Unitless` package was\ncreated to cover this need as transparently as possible.\n\n\n## Installation\n\n`Unitless` can be installed as any other official Julia packages. For example:\n\n```julia\nusing Pkg\nPkg.add(\"Unitless\")\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femmt%2Funitless.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femmt%2Funitless.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femmt%2Funitless.jl/lists"}