214 comments

[ 3.0 ms ] story [ 235 ms ] thread
> Like many similar programs, it can handle multiplicative scale changes. It can also handle nonlinear conversions such as Fahrenheit to Celsius

how is

  C = (5/9)(F-32)
not linear?
In certain contexts (such as linear algebra), linear refers to the fact that the mapping must cross the origin.

This follows from the constraint that for a linear map f, f(a + b) = f(a) + f(b), which is not true for the farenheit-celsius example.

EDIT: See https://en.wikipedia.org/wiki/Linear_function#As_a_linear_ma...

Also: a linear function plus a constant is said to be affine!

Reference: https://mathworld.wolfram.com/AffineFunction.html

I just learned something. Is it correct to call both of the first order? Or is that term really only to be used for the underlying polynomials?
I think of nth order as mostly just applying to approximations. But I guess first order would be ok here.
(comment deleted)
If f is linear, then f(a)=f(a+0)=f(a)+f(0). So f(0)=0.
Because 0°C is not 0°F
(comment deleted)
As I understood it: C to K would be linear since they are always offset by the same amount, but 0°C is not 0°K. C to F is not linear since they are not offset by the same amount.
It seems to me C to K is affine as well. True, it's somewhat special in that the factor is exactly one, but if there's an offset it's not linear in the sense of the sentence under discussion.
That's not how it works. The definition of linear used in this context requires f(a+b) = f(a) + f(b) which is not the case with the C to K translation which is f(x) = x + 273.15 Only functions in the form y=mx+0 meet the criteria.
It's affine. It's not linear because the offset means superposition doesn't hold.
It’s linear in terms of its order but it’s not a linear transformation, which must satisfy:

    f(c*(a+b)) = c*f(a) + c*f(b)
Where c is a scalar. A and b are abstract objects. Examples include vectors, real numbers, and functions themselves.
Linear does not mean "graph is a line". It means "graph is a line through origin" (in the 2d case).

Somewhat counterintuitive, but that's how it's defined.

The Wikipedia article someone else posted already seems to disagree: https://en.wikipedia.org/wiki/Linear_function. This confuses me.
The article actually agrees.

> In mathematics, the term linear function refers to two distinct but related notions (...) [proceeds to specify them using incomprehensible math words]

GNU Units documentation uses the second notion, common with linear algebra (and thus arguably more common in any computing) - the notion that calls y=ax a linear function, and y=ax+b an affine function.

So, rather than calling it non-linear, which is clearly somewhat confusing, perhaps it would be more clear to state that it handles affine functions like F to C...?
Perhaps. That's what I myself did when adding handling for affine unit conversions in a project I worked on.
Agreed. Why they wasting our time pointing out it’s “non-linear” when this statement is only true for some definitions of linear? Just pick a non-ambiguous word, or add an asterisk and footnote.
Frim jxy's 6th edition man page:

"Units only does multiplicative scale changes. Thus it can convert Kelvin to Rankine, but not Centigrade to Fahrenheit."

How does GNU Units compare to Frink ( https://frinklang.org/ )?
(comment deleted)
Units is older, frink is a lot bigger, it's a whole programming environment.

Frink started from and extended the units defined by Units, I don't know if anything has gone in the other direction.

Later: for simpler calculations an interesting feature frink adds is the ability to do interval arithmetic. Terms can be specified as ranges that are then carried through the calculations to the result.

Looks like it is comparable to javax.measure (https://jcp.org/en/jsr/detail?id=363). I used an implementation of that a few years ago: https://github.com/unitsofmeasurement/uom-se

It's a complex domain, especially if you get down to having more complex units composed of other units and want to do conversions between metric and imperial. In our case we were dealing with material properties (chemical, physical, magnetic, etc.) and different countries and companies using different ways to record characteristics for materials.

The author of Frink takes particular pride in doing unit maths correctly.

For example, if the conversion between two units is defined by statute to be some weird ratio, Frink will use that, and not merely the best f64 approximation of that ratio.

As indicated by a sibling comment, Frink also handles interval maths, and in general it just has a remarkable amount of features, being a fully-fledged programming language.

It's a pity it isn't open-source, the author says he doesn't want the aggravation of having to deal with pull requests and so on, and just wants to work on his software project on his own. Which, ok, that's fair: but at least Lua and SQLite don't accept contributions from outside the team, so there's a model for releasing open-source software without having to collaborate with the general public.

No question that it would increase the size of his inbox though, so I get it.

I love being able to convert from millilightseconds to miles, and vice versa. You can figure minimum latency for a given distance or maximum distance for a given latency.
Is this a covert reference to the 500-mile e-mail?
Probably not. Knowing the speed-of-light round trip time to a network location gives you a bound on how much you can improve the performance of remote operations.

For example, I'm in Austin, querying a database hosted in Amazon's us-east-1 data center ("Northern Virginia"). Call it 1000 miles away, 2000 mile round trip.

    You have: 2000 miles
    You want: millilightseconds
            2000 miles = 10.736388 millilightseconds
If I have a query that's taking < 1ms, no optimization of how that query is executed by the database can possibly improve the overall performance.
> You have: furlongs per fortnight

We need to see this ^ more often in real life!

It's a very slow velocity, though. It's best used for things like snails.
You could count it in increments of some number, like 1280... so you'd have a car speedometer which would measure your speed in x*1280 furlongs/forthnight.

(yes i'm joking, but considering all the other freedom units, and rules measuring in 1/64", it's not far from the truth)

I wish someone would make a spreadsheet that tracks units like this.
Not sure what you want in a spreadsheet, but the data is stored in text files. See https://www.gnu.org/software/units/manual/html_node/Units-Da...
Not sure what GP is thinking of, but type checking for Excel would be really nice:

ERR: A1 is USD/month, B1 is USD/user, so you cannot calculate A1+B1!

Not the unit definitions in a spreadsheet; I'd like a spreadsheet where each cell has not just a number, but a number with its unit. Formulas would be evaluated the way `units` does it. B1=3m; B2=4s; B3="=B1/B2" would give as value of B3 "0.75 m/s".
Ah, sure, that would be great. I'd say it would be useful in text based programming environments too though.
Agriculture would probably love a spreadsheet that understood units of measure and kept results as fractions.

Gotta love that a bushel of Yellow Corn is a different weight than a bushel of Oats.

All the grains are different. I had to hard code a table of those conversions for an application I worked on.
A simple example I used recently in order to guesstimate how much energy is needed to heat 300liters of water by 5K:

  You have: 5K * 300kg * water_specificheat
  You want: kWh
  * 1.7433333
really easy to use.

How much power could you obtain with a small water turbine if you had 10m of head and 1l/s ? Easy !

  You have: 10m * 1kg/s * gravity
  You want: W
  * 98.0665
(comment deleted)
I do these kind of calculations all of the time and never realize units had this level of functionality. Wow, thanks Faaak!
I kept reading this as “water specific cheat” and was really confused.
~ units

586 units, 56 prefixes

You have: 50m * 1kg/s * gravity

unknown unit 'gravity'

how did you get gravity?

  $ units
  Currency exchange rates from FloatRates (USD base) on 2020-12-21
  3677 units, 109 prefixes, 114 nonlinear units
  
  You have: 10m * 1kg/s * gravity
  You want: W
          * 98.0665
          / 0.010197162
  You have:
You lack some units and prefixes.
How do you get the other units/prefixes?

EDIT: Figured it out, just had to brew install gnu-units

Why isn't this released as a library (as opposed to a program)?
A program is easier to use. You do not need to link to it, just run it! Do you have trouble running programs?
In the README in the GNU units source code (2.21):

  203: Ideas the future (may or may not happen):
  204:
  205:  * Bundle up the units conversion stuff into a library. 
At the end of units.c, too:

  6280: /* NOTES:
  6281: 
  6282: mymalloc, growbuffer and tryallunits are the only places with print
  6283: statements that should (?) be removed to make a library.  How can
  6284: error reporting in these functions (memory allocation errors) be
  6285: handled cleanly for a library implementation?  
  6286: 
  6287: Way to report the reduced form of the two operands of a sum when
  6288: they are not conformable.
  6289: 
  6290: Way to report the required domain when getting an domain error.
  6291:
  6292: */
My prowling of the source code is probably uninformed because I'm not a C expert by any means but it seems like you could include units.h and use many of the useful functions in another program.
You might also be interested in boost::units.

It implements units using a ton of templates and "compile-time metaprogramming". [0]

The nice thing is that it has no runtime overhead as all templates are "compiled away". The disadvantage is that when you make a mistake against units, it results in a "template compiler error explosion".

[0]https://www.boost.org/doc/libs/1_65_0/doc/html/boost_units.h...

I love it, everything is there!

    You have: kilometers per hour
    You want: attoparsecs per microfortnight
            * 10.889018
For what's not, you can add additional definitions.

I found myself wanting to calculate Little Boy blast yields sufficiently often that I added a hiroshimabomb unit of energy.

Similarly, various areal units such as countries and states/provinces.

How many Australian football fields in 1.3 Canada's?
Aussie Rules is a variably-specified pitch:

Australian rules football grounds, even at the highest level of the game, have no fixed dimensions. For senior football, the playing field is an oval, typically between 135–185 metres (148–202 yd) long goal-to-goal and 110–155 metres (120–170 yd) wide wing-to-wing. Grounds can vary from long and narrow to almost circular, and are not necessarily symmetrical, depending upon how and where the field was constructed. At least 5 metres (5.5 yd) of space between the boundary line and any fence is required for safety.

https://en.wikipedia.org/wiki/Australian_rules_football_play...

Canadian Football: 150 yards length overall, 65 yards width overall. 2.0144628 acres area, 0.81522418 hectares.

https://sportsknowhow.com/football/field-dimensions/canadian...

The other question is if you're looking for length or areal metrics.

GNU Units is a beautiful piece of software. I keep using it for everything. You can convert between currencies, too.

If GNU Units cannot do something, I resort to using Maxima[1].

[1] https://maxima.sourceforge.io

I've been using WolframAlpha for this sort of thing. The idea of using something fully local and open-source does have some appeal, but it's hard to compete with the convenience of Ctrl+T "wo" <Tab> <Query> <Enter>.
You mean Ctrl+T "wo" <Tab> <Query> <Enter> <Wait 15 to 60 seconds for the page with your results to load> <Get something that's a mighty pain to copy/paste from>.

This experience is why I stopped using W|A few years ago. It's awesome, but it's also a pain.

Can relate; that's definitely been an issue for me as well. Though on the plus side it's nice to be able to open multiple tabs, make small tweaks to the formula, and switch between them to compare results.

Kinda wish there was an open source browser-based WolframAlpha alternative which did more of the computation locally so everything wasn't so slow.

I used to love Wolfram Alpha but it hasn't worked for me for several years and I've been unable to find out why.

Just tested now and it does work on Android FF - yay! But not on my desktop (neither Chrome nor Edge). Doesn't work for me on Firefox nor Brave either (neither Win10 nor Kubuntu). I have randomly assigned IP from major UK provider (which provider has also changed). Doesn't work through my work VPN either.

I've taken to using Geogebra (online) when I quickly need a function plotting.

If Units can convert between currencies, how does it source the exchange rates? They change on sub-second time scales and bid/ask spreads make the notion of a rate somewhat uncertain.

Edit: answering my own question. There are multiple providers, some requiring API-key signup, but the answer is far from definitive: https://www.gnu.org/software/units/manual/html_node/Currency...

(comment deleted)
Does the GNU projet make its repositories available online ?

I'd like to have a look at the code but all I found is an outdated mirror : https://github.com/ryantenney/gnu-units

Looks cool. If the poster is affiliated with the project, some suggestions:

1. I don't know what I am looking at with the examples. I think that this is an interactive session with the program, but this is not clear. 2. It is also unclear how to start the program as the executable name is not specified. This and the former point could be addressed by including one of those gifs of an interactive session that are popular in GitHub readmes these days. Sure, they are a bit gimmicky, but still useful. 3. It is not clearly stated if this is an executable program or a library. I believe it is the former, but this is nowhere in the introduction. 4. Probably this is included with a number of package managers. If that is the case, instructions for installing via a package manager would be helpful, especially since the name is not particularly unique.

No please don't use a gif. That's a terrible way to present information and leads to accessibility issues in documentation where it has no business. Instead, just show a shell prompt and the command needed to launch the program.
So long as those with disabilities are also accounted for, using gifs are a great way to demonstrate usage. I always appreciate running across them when I hit a readme.
Out of curiosity, what makes gifs better than static text for you?
(comment deleted)
I dislike them, but I see one benefit: they increase effective screen real estate by leveraging time as an additional dimension.

I guess they also allow you to passively view a sequence of stuff instead of having to actively scroll to move through the sequence. Maybe that feels easier to some people.

I see how they're useful, but I'm still not fond of them because:

(1) Often, the speed is wrong. Either they play too fast to follow along or they play too slowly and time is wasted waiting for it to show me the part I care about (or loop back around).

It's hard to get the speed just right. And AFAIK browsers don't offer controls to adjust the speed, and even if they did, it would be tedious.

(2) Personally I find animations (especially rapid or looping ones) quite distracting when trying to read the rest of the page. My eye just keeps moving to the thing that's trying to grab my attention.

I usually try to work around that by scrolling so that the animation is out of view. So, I'll live, but it's not a good user experience. And again, (some?) browsers seem to offer no help, like a setting to always enable animation controls (play/pause/etc.) or to loop only once.

There's a link to the documentation [0] right on the page. The documentation is very clear.

People should read their package manager's documentation for help using their package manager.

[0] https://www.gnu.org/software/units/manual/units.html

I agree with you, but package names aren't predictable. I find that an example of installing a package is helpful even for users familiar with use of package managers as it gives context and helps readability. Whenever I see `sudo apt-get somethingsomething` I immediately know that somethingsomething is the package name.
It's an old GNU tool. The package name will be "units" everywhere.
On Homebrew, at least, it's `gnu-units`.

If you try to install just `units`, brew is smart enough to suggest it, however.

Application developers typically do not decide package names (unless they provide their own package repository), distribution maintainers do. Package names may change over time, and might be different for different distributions (or even between different versions of the same distribution), even if they use the same package format.

Of course, application developers may decide anyway to keep track of package names for their N favourite distributions and display them on their website. But to me it comes across as a waste of useful space.

Maybe a shell script showing the inputs more explicitly would help?

    echo -e '500 miles\nkm' | gnu-units-executable-name
But I'm pretty sure from reading the post that units prompts you first for 'you have' (units and/or measurement to convert) and then prompts you for 'you want' (units to convert to).
This is a GNU implementation of an old UNIX tool. It’s older than Linux, let alone package managers. There’s also BSD and other implementations available (the BSD version comes pre-installed on macOS).

This isn’t some new tool that is uncommon in UNIX systems, it’s something that a lot of OSs ship with their base install and have done for several decades.

The reason why that matters is because documentation was very different back then. People relied on ‘man’ pages and physical binders with printed instructions. READMEs did exist but they were plain text (not even markdown) and that was generally just there to explain how to compile the source (the compilation would create man pages)

> the BSD version comes pre-installed on macOS

As discussed elsewhere in the thread (https://news.ycombinator.com/item?id=25659223), it looks like macOS uses an early version of GNU Units.

(comment deleted)
Ahh yes, before it adopted GPL. I should have spotted that because it’s very typical of Apple hehe
My first exposure to GNU Units was when I read the story about emails failing if they were to be sent more than 500 miles.

I’m sure it’s popped up here before but here’s a link for those who haven’t come across it: https://www.ibiblio.org/harris/500milemail.html

Same here back in the early aughts, but it took me a while to really dig in and discover that units also handles times too.

e.g. units "sec $(echo $SECONDS)" "day;hr;min;sec"

Useless use of echo there.
Not unless you’re also trying to demonstrate the magic of fork.
noted, thanks
Extraneously strenuous use of "useless" when using "redundant" would have done...unless useless has another usage.

EDIT: The commenter echoed, duckly.

It's very important to watch out for useless uses of echo/cat. What is cat, a few megabytes now? Fine for those of us on modern hardware, but you never know what PDP-11 user is going to copy paste your comment into their terminal.
I use a construct like this to ensure that the value ends with a newline.
$() chomps the newline though...
Fun story, but the numbers don't add up for me:

Wouldn't the signal have to do a full round trip within the timeout of 3 ms, meaning it could not go beyond 580 / 2 = 290 miles?

Additionally, the connection from A to B is usually not a straight line, and at that time fiber lines were much less widespread. All these factors combines make it unlikely that even 200 miles could be reached...

The FAQ [1] addresses both of these questions. See numbers 8 and 15, respectively.

[1]: https://www.ibiblio.org/harris/500milemail-faq.html

Thanks for that link.

> Well, to start with, it can't be three milliseconds, because that would only be for the outgoing packet to arrive at its destination. You have to get a response, too, before the timeout will be aborted. Shouldn't it be six milliseconds?

> Of course. This is one of the details I skipped in the story. It seemed irrelevant, and boring, so I left it out.

I don't understand, so it was in fact 6 ms?

This is addressed in the FAQ[1].

> That three millisecond time doesn't make sense as the timeout for a connect() call.

> Yes, I know. And it wasn't the timeout, actually. In the story, I make it sound like it took all of ten minutes from being made aware of the 500-mile email limit and determining a 3 ms light-speed issue. In fact, this took several hours, and quite a bit of detective work. The point is, eventually I came up with that figure, ran units, and gagged on my latte. (I'm fairly certain it was a different latte from the one I started with.) So what, in particular, is your question about the 3 ms figure?

[1] https://www.ibiblio.org/harris/500milemail-faq.html

I remember chatting with Trey about this as it happened in real time.

I love that every time this link gets posted to HN someone says it looks like a hoax.

I love that every time someone says something is a hoax a person who was there shows up to correct the record :)
I wanna see that happen for The Loch Ness monster!
I never claimed it was a hoax, I just didn't agree with the math, that's all. No reason to get upset.
Last time this showed up on here, a poster claimed to be the consultant who did the upgrade and caused the problem.
Same here... And I think it was quoted here a week or so ago again. Great story.
First time user. Failed on me.

    You have: 25 C
    You want: K
    conformability error
        25 A s
        1 K

    You have: 1 cup flour
    Unknown unit 'flour'
I think I'll stick with Google.
Well, flour isn't a unit...
It's relevant information if you're converting to mass, which was probably the intention. Google will return the correct result for "mass 1 cup flour", by comparison.
There is no correct way to turn "1 cup flour" into a mass. For one thing, you can fit more into the same volume by compressing. But more importantly, the material is ambiguous. There are many different kinds of flour with varying physical properties.
I started using this tool some years ago after I found out the CO₂ cost of a Google search. I don't need a web query when I search "40000lb in tonnes" when reading an American news article, for example.

  units 40000lb tonnes
   * 18.143695
It even includes currencies. "sudo units_cur" will update the exchange rate data file, if you need that.

  units 50GBP USD
   * 68.143948
> "sudo units_cur" will update the exchange rate data file, if you need that.

Oh good tip that. I noticed the package on Ubuntu 20.04 came with currency conversion from 2019. Not very useful without updating it.

> It even includes currencies. "sudo units_cur" will update the exchange rate data file, if you need that.

Doesn't work for XDR. Of course, who needs XDR? I sure don't. But you can get the XDR exchange rate with this command:

curl -d '__EVENTTARGET=lbnTSV' POST -v https://www.imf.org/external/np/fin/data/rms_sdrv.aspx

Maybe someone should integrate that into units_cur.

I absolutely adore this program. It works exactly like I expect, every time. Sometimes I'll just think about what a nice tool it is, even when I'm not using it.
My go-to test for such systems is converting miles per gallon to liters per 100 km. Can anybody give it a try?

Google did it well for the longest time, then stopped at some point. Now it works again.

Just tried it on iOS and it works now. Don’t quite remember when I last tried, but I believe it’s fairly recent.

(comment deleted)
(comment deleted)

        $ units "6 l/100 km" mpg

        reciprocal conversion

        * 39.202431

        / 0.025508622

    units "30 miles per gallon" "liters per 100km"
        reciprocal conversion
        * 7.8404861
        / 0.12754311
What I particularly like is the use of the locale:

    LANG=en_GB.UTF-8 units "30 miles per gallon" "liters per 100km"
        reciprocal conversion
        * 9.4160312
        / 0.10620186

    LANG=en_US.UTF-8 units "30 miles per gallon" "liters per 100km"
        reciprocal conversion
        * 7.8404861
        / 0.12754311
(Although it is incomplete, as it only uses Imperial gallons for "gallon" for en_GB, although there are probably more English locales that use(d) the Imperial gallon than the US gallon.)
I keep using different tools to do conversions so now I can unify that all into Units and get my speed in furlongs per fortnight as the ancient prophecies foretold.

Rejoice!

(but for real this is top I’m replacing multiple other tools with Units)

  You have: 1 kelvin
  You want: degcelsius
     * 1
     / 1
This does not seem right
It's right in the sense that the units are the same size. IE if something increases 10 deg K it also increases 10 deg C. But it's obvious to humans that that isn't what you where asking.

Compare:

   You have: 1 degF
   You want: degcelsius
           * 0.55555556
           / 1.8
Nitpick: There are no "deg K", because it is an absolute system it doesn't have degrees.

Notice you also don't need degrees metre, degrees watt or degrees newton, because those are absolute units they are metres, watts and newtons. It is Celsius (and Fahrenheit) which are weird because they aren't absolute but instead have this arbitrary zero point.

It is. The unit Kelvin is equal to the degree Celsius. If you want to convert temperatures, you need to use tempK and tempC:

    You have: tempK(1)
    You want: tempC
            -272.15
https://www.gnu.org/software/units/manual/units.html#Tempera...

> Conversions between temperatures are different from linear conversions between temperature increments—see the example below. The absolute temperature conversions are handled by units starting with ‘temp’, and you must use functional notation. The temperature-increment conversions are done using units starting with ‘deg’ and they do not require functional notation.

(comment deleted)
Makes sense because temperatures don't have a linear conversion.

From the man page: Nonlinear units are represented using functional notation. They make possible nonlinear unit conversions such as temperature.

  You have: tempK(1)
  You want: tempC
 -272.15
Edit: seems i am not alone :D
Do GNU Units handle units with implicit parameters?

For example, 1500 mAh (milliamp∗hours) could be converted to 5.55 Wh (watt∗hours), or 19.98 kJ in SI units, in the context of a 3.7-volt battery.

Naive approach fails:

  $ units "1500 milliampere*hours at 3.7 volts" "watt*hours"
  conformability error
        1.9593687e+09 kg^2 m / s^4
        3600 kg m^2 / s^2

  You have: 1500 mA * hr * 3.7 V
  You want: kJ
          * 19.98
          / 0.05005005

  You have: 1500 mA * hr * 3.7 V
  You want: watt * hours
          * 5.55
          / 0.18018018
You are multiplying mAh, volts, and atmospheres. (“at” is a unit not an operator.) You want to just multiply mAh and V like

  You have: 1500 mA hours * 3.7V
  You want: W hours
          * 5.55
          / 0.18018018

  You have: 1500 mA * hours * 3.7 V
  You want: watt * hours
          * 5.55
          / 0.18018018
> You have: 1|6 cup

Is that a pipe for division?

It has both / for division with the usual precedence and | for very tight-binding division, for use in fractions. So 1/6 cup parses as 1/(6 cup) but 1|6 cup parses as (1/6) cup.
> nonlinear conversions such as Fahrenheit to Celsius

The heck? "y = ax + b" is linear; just not homogeneous linear.

https://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conv...

https://en.wikipedia.org/wiki/Homogeneous_function

It's not a linear function for what it's worth. It breaks the f(x + y) = f(x) + f(y) part of linearity.

It is a linear equation, confusingly enough.

That's linear in the sense of "linear operator" or "linear map". I've never heard "nonlinear" as the opposite of that due to to a constant offset. "Linear map" and "linear operator" are used in contexts where homogeneity is a given.

A scaling plus displacement is linear in the sense of "linear equation" because, for instance, if you scale a signal and add DC offset, there is no distortion; no new harmonics are present in the output signal.

ax + by + c is called linear because the plot of points which satisfy the equation form a straight line; there is nothing confusing about it whatsoever.

Temperature delta conversions between C and F are certainly a linear map/operator. If it gets 5°C hotter, and then 3°C hotter, making 8°C hotter, that's exactly like 9°F hotter plus 5.4°F hotter making 14.4°F hotter.

Here is a test for linearity for a real-valued one-argument function which is not confused by an offset:

     x + y      f(x) + f(y)
  f( ------ ) = -----------
       2             2
Works just fine for °C/°F.

There is another criterion for a linear map that hasn't been mentioned, though: f(ax) = af(x).

Well one way or another it's not a linear function, so calling it a non-linear conversion is technically not incorrect.

That said I completely agree that it makes way more sense to just call it a linear conversion and to pick an example that's truly nonlinear. Not that there are that many non-linear units, but something like decibel or beaufort might work.

Insistence that "linear function" is defined one way is not supported by facts:

https://en.wikipedia.org/wiki/Linear_function

A "linear function" refers to a "linear map" only in certain contexts.

The word "affine" can be used for a mapping that fails to meet the definition of a linear map operator due to offsets. That is much better than "nonlinear".

GNU units supports normal unit conversion, such as meters to feet. These conversions all obey f(ax)=af(x), that is, if you have zero meters you have zero feet. If you want to convert something that breaks that rule then you have to give a definition in the database as a general functional form or as a lookup table. And you have to use functional syntax to perform the conversion. Some examples in the database include dB, wire gauge, shoe size, grit size for sandpaper, ring size, musical intervals, and sugar concentration. I call units defined using a lookup table or general functional form "nonlinear units".

Zero degrees Fahrenheit is of course not equal to 0 K, but conversion of absolute temperature is not presented as the best example of this class of unit conversion. It's highlighted because everybody asks about it. Temperature conversion was for years, before I figured out a way to support it elegantly, the most requested missing feature. Yes, it's a little strange to call affine transformations "nonlinear" but there is no special "affine unit" feature in the program.

Speaking of K, Celsius to K is also "nonlinear" math: you add or subtract a constant, around 272-something.
It's not about GNU.

V6 Unix (circa 1975) had it: http://man.cat-v.org/unix-6th/6/units

Your macOS have the BSD version:

    % units -v
    
      units version 1.0  Copyright (c) 1993 by Adrian Mariano
                        This program may be freely distributed
    usage: units [-f unitsfile] [-q] [-v] [from-unit to-unit]
I'm not sure of the total history, but "by Adrian Mariano" means macOS uses GNU units. Wiki says they forked a copy before the license change.
It’s about Unix, and GNU’s Not happens to be the best implementation of it. Which is why macOS copies pieces of it, despite needing to use obsolete versions in order to comply with Apple’s hatred for end-user freedom.
I’ve always wondered why units(1) isn’t a library. I’d love to have access to abstract “united quantities” and “united measurements” in my HLL of choice, rather than needing to switch to a special-purpose tool (units, Frink) or a statistical-analysis language like R just to get them.

Sometimes I need to bust out stoichiometry-like unit-balancing in the middle of my code, and I want type enforcement that I’m getting the right units in the output! Sometimes I want to do a series of floating-point calculations, and I need to put error-bars on the result, and it’d be great if those were tracked for me in the form of a measurement ADT that tracked sig-figs. Etc.

My only guess for why there’s no libunits, is that it’d be a obtrusive black-box of operational non-guarantees if it was implemented in C, given that what Units does — if naively implemented — would be heavy on arbitrary memory allocations, and basically necessitate garbage collection.

It’d make much more sense for a libunits to exist as a C++-ish “template library” that takes an allocator as an argument; but that doesn’t really help anyone who wants to use any language other than C++ (as the template parameters can’t be exposed in the C wrapper code that other languages FFI to.)

(Before someone asks, you could get a limited kind of united-quantities — ones that can only represent “realistic” units — with a static per-qty bitfield representing the compiled-in units. But this wouldn’t really let you represent arbitrary unexpected “unit polynomial” qtys, e.g. m^5s^-3. This might seem fine, but sometimes intermediate quantities in formulae look like that, even though they work out to something sensible when everything is multiplied out.)

Even if there couldn’t be a single units library implementation, it’d at least make sense for there to be 1. a canonical libunits-data release (sort of like tzdata), that individual library impls could pull in; and 2. a high-level API spec or reference-impl of what a libunits library for a given language-runtime would look like (sort of like how every HLL has copied JodaTime’s datetime design.)

I've seen a few before, and a quick search yielded a bunch for me, right off the bat. By no means exhaustive:

Julia: https://github.com/PainterQubits/Unitful.jl

Python: https://pint.readthedocs.io/en/0.6/

C++: https://github.com/nholthaus/units

etc.

And yes, it has always struck me as strange that date/time/calendar utilities are standard as canonical functionality shipped with language distributions, but units are not.

There are heaps of libraries for all languages because it is a fairly fun and straight forward project. I personally created this one because I was frustrated with how slow and clunky the existing javascript libraries were:

https://github.com/GhostWrench/pqm

Also, I open sourced just the unit database (in JSON format) for anyone interested in making their own version:

https://github.com/GhostWrench/unitdb

> straight forward project.

Assuming this is straightforward is as wrong as with calendars / time Systems.

What makes you say that? I suppose you could add as much complexity as you want with differentiating units from different standards and adding uncertainty in the unit definitions and on and on. But for 99% percent of users, just conversion factors and basic arithmetic operations are all that is needed. What application do you have in mind where this would fall short? In 15 years as an engineer I have not run into one.
What day is it?

Be conservative in what you send, be liberal in what you accept.

leap years, leap seconds, julian dates, pre-gregorian dates, years beyond 9999, yyyy-mm-dd vs d/m/y vs month day year vs ..., zero padding, 12 hour vs 24 hour, 32 bit time stamps, DOS time accurate to every two seconds, why does excel need the date time in a specific format, I dual boot between Windows10 and Linux and every-time I boot up Windows10 the first thing I need to do is adjust the date time, pico-second granularity, time zones, daylight savings time, ...

Today is Stability (4), Flint (Tijaax * Etznab)

It's a good day to: Spend time in reflection and introspection.

What are you talking about? The topic is unit conversion, not datetime libraries.
Don't disagree that datetime libraries that are used to control computer networking are immensely complex. Never said they weren't. However:

1) That is not what we are talking about

2) Sometimes you just need a stopwatch, timing applications _can_ be simple

Commenting on my own thread because the window to edit has passed. What is up with the haters today? I share a useful open-source library with everyone that I spent a ton of time working on only to get crapped on by the know-it-all crew? Did you even click the link and read the github readme? This library is tested against NIST data! It also has 100% test coverage and support for corner cases like non-linear unit conversions like temperature. How does anyone enjoy participating in this community?
HN has a huge number of users, most of whom don't comment on any given topic. So I think it's best to not care too much what the handful of individuals who interact with you happen to think.

I went to search through dang's comment history to see whether he's posted any statistics to make that "huge number of users" more precise, but instead I found this comment which explains it better than any statistics: https://news.ycombinator.com/item?id=18523361

The source isn't that big, I just downloaded 2.21 source and it is one directory with about ~45 files, and the majority of bytes are for /doc/dvi binaries, only 5 c files and a post Yacc'd tab file for parsing the units definitions file. Surprisingly simple now that I look at it.

Heck... I was going ask you to write it, but now I'm thinking this could be a fun weekend project.

I can see one function in that library:

    bool convert(const char *from, const char *to, double input, double *output);
No need to make it more complex for starters. If you want to make an .hxx Boost-like class around it, that's outta my league!

EDIT: markdown help

EDIT: added a status return instead of val return to check for conversion correctness (bool is newer C)

> (...how the heck do I make a literal asterisk in HN markdown?)

Spaces on both sides: const char * from

But also you are supposed to mark up code by indenting with four spaces, and then everything will be treated as verbatim text:

    const char *from
Keeping track of units was one of the first ever uses of C++ templates (one of the Bjarne books IIRC mentions it existing prior to the STL), but was apparently reinvented multiple times later.
FYI: use tab completion in the 'units' REPL to show a list of available definitions. You won't find them in the 'units' manpage, as they are defined in an external datafile (on my machine this file is /usr/share/units/definitions.units)