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, but log(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
and 000.0
(alternatively
written 0.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).