Calculate expressions involving significant figures.
This library provides a module Data.SigFig
that helps with the parsing and evaluation of expressions involving significant figures. Significant figures are a method, often used in chemistry, of assessing and controlling the precision/uncertainty from measured values in calculations.
Expressions with significant figures are not easily calculable with a normal calculator, since they require intermediate rounding based on certain rules. This library takes care of intermediate rounding during evaluation and provides functions to parse text into expressions. Parsing supports integer, float, and scientific notation (via E notation), constant terms (terms with effectively infinite significant figures), common operations, parentheses, and an additional small set of functions. Expressions may also be constructed via helper functions.
Significant Figures
This repository contains the Data.SigFig
module that contains a variety of functions to parse and evaluate expressions involving significant figures. It also contains an executable CLI using Haskeline.
Supported Operations
Operation | Example | Output |
---|---|---|
addition | 2.0 + 4.31 | 6.3 |
subtraction | 5.1 - 2 | 3 |
multiplication | 4.8 * 5.2 | 25 |
division | 4.00 / 3.1 | 1.3 |
constants | 2c * 4.2 | 8.4 |
logarithms | log(10) | 1.0 |
antilogarithms | exp(6.24) | 1.7 x 10^6 |
exponentiation | 4.0 ** 4 | 2.6 x 10^2 |
I created the distinction between antilogarithm and exponentiation to distinguish between which value is measured and which is constant. You'd use an antilogarithm, for instance, to calculate the molarity of H3O+ given pH, whereas exponentiation is nothing more than a shorthand for repeated multiplication.
For the rules regarding significant figures, → see here.
Of course, you can use parentheses and more complex expressions:
expr> log(10.45) * 100c + 3.6200 * (9.4523 + 876.45) / 2c
1705.4 (5 s.f.)
Support for general arbitrary-precision arithmetic on the rationals:
expr> 4.52c * 100c * (1c/60c)
113/15 (non-terminating const)
Super-smart display that shows scientific notation when necessary, and annotation for significant figures:
expr> 0.650 * 4000.
2.60 x 10^3 (3 s.f.)
Manipulation of Expressions and Terms
The Data.SigFig.Types
module contains some functions to help with manipulating terms and expressions in Haskell, allowing one to skip the process of parsing.
Reminders
Significant figures are correctly calculated by postponing rounding until the end of a sequence of similar operations; i.e., rounding to least decimal place only occurs after all addition/subtraction operations in a row are computed. Internally, this is done by parsing such runs as a list of operands and associated operations instead of treating operators as binary.
Functions that return irrational numbers do not work when given a constant value as input, since constants are represented internally as rationals, and therefore the essence of a "constant" in the context of significant figures (i.e., "infinite" significant figures") cannot be guaranteed. Since using a symbolic math engine is complete overkill for such a calculator, we limit the domain of these functions. NOTE: This doesn't mean you can't use constants within the argument expression, it just means the expression must not evaluate to a constant. For example, with the
log()
function,log(45c + 2)
(log(47)
) is perfectly fine, butlog(45c + 2c)
(log(47c)
) is not.Zero is a weird number for significant figures. First, → no measurement should ever be recorded as 0, or 0.0, or 0.00 or 0.0e2 or anything like that, since significant figures work on relative precision and that breaks down once you just have 0. But that's not the end of the story. Consider a case where you take a difference of two measurements and end up with 0, or really any situation in which you end up with 0 as an intermediate/final result. In this case, you do have a good idea of what precision you have, but it becomes incredibly difficult to work since all notion of significant decimal places is lost. Furthermore, you may think of a workaround, in which
0.000
and000.0
(alternatively written0.000 x 10^2
) as distinct values in the context of significant figures. The below doesn't feel right to me:exp(000.0) = 10 ^ (100c * 0.000) = 1
exp(0.000) = 10 ^ (1c * 0.000) = 1.00
A patch that resolves this will involve storing another piece of metadata for each value: its rightmost (or leftmost, given the total number of significant figures) significant decimal place.
Since I have concluded there is no trivial general solution to this, I have chosen to retain the number of significant figures for 0-values, such that they work as expected in larger expressions. Also, this is an incredibly niche case that should not ever appear in well-designed lab procedures.
TL;DR: How zeroes work is pretty controversial but it should work the way you'd expect them to intuitively (I asked other people how they'd expect zeroes to work and it was the way it currently is).