Skip to content

Sigfig exponential form#32

Open
dpvc wants to merge 4 commits intopstaabp:sig-fig-promotefrom
dpvc:sigfig-exponential-form
Open

Sigfig exponential form#32
dpvc wants to merge 4 commits intopstaabp:sig-fig-promotefrom
dpvc:sigfig-exponential-form

Conversation

@dpvc
Copy link
Copy Markdown

@dpvc dpvc commented Mar 18, 2026

Here is an update to add the ability to enter numbers in exponential form, e.g., 1.23x10^4. This took a bit of machinery to make it work, as one has to override most of the operators in order to be able to track when operands are simple numbers as opposed to when they are computations or had parentheses that were removed, etc. It also involves overriding several other Parser classes so that the markers can be properly maintained as the numbers move through the parse tree in various ways.

This implementation allows x to be used as both an operator for 1.23x10^4 as well as a variable (for when you eventually allow significant figures in formulas). The idea is that 1.23x10^4 is usually interpreted as 1.23*x*10^4 via implicit multiplication, so we override the implicit multiplication operator to look for 1.23*x (with implicit multiplication) on the left and 10^4 on the right, and then it removes the *x on the left to get 1.23*10^4 and marks it as exponential form so that it stringifies as 1.23x10^4 and is treated as a single number.

If x gets removed as a variable, then the x operator comes into play, and is treaded as *.

That is the basic idea, but there are a bunch of subtleties, especially in tracking what is a pure number and what is a computed number. The processing of exponential form is strict about the left-hand operand being a simple number (not a computation) and the right-hand being a constant 10 raised to an integer power (again not computed). Having parentheses counts as being a computation, so (1.23)x10^4 would not produce an exponential form, nor would 1.23x10^(4) or 1.23x(10)^4, or 1.23x10^--2. The only exception is that parentheses are allowed around a negative exponent: 1.23x10^(-4). The left-hand number can be any number, so 12.3x10^4 is also allowed.

The string and TeX output functions have been modified to allow printing of numbers in exponential form, so a number entered that was will always print in exponential form, and the string version will be 1.23x10^4 rather than 1.23E+04 (though you can use either form to enter the number). There is also a new content flag, alwaysExponentialForm that controls whether to print all reals in exponential form (off by default).

I modified your LimitedSignificantFigures context to use these new features to allow exponential form in the limited setting, and to handle the issue of negation that I mentioned earlier.

I didn't add any tests, but there should be a lot more to test the new notation and the limited context. There should also be tests using reduceConstants => 0, since that requires the parser to handle x and the other operators differently, so tests should be made for both settings of reduceConstants.

Finally, I didn't modify the POD documentation, so you will need to add the new exponential form there yourself.

As for the details of the changes, the modifications in SignificantFigures::Real->new() are to move the check for an existing Real to after the coercion to a Value object by Value::makeValue(), so that if that produces a Real (say from passing a string that reduces to a number rather than a number itself), we handle it as we would have if it were passed to new() directly. (This was a bug before, but I didn't catch it.) It also marks the result as pure when that is appropriate.

The string() and TeX() functions now add parentheses when the result contains exponential form and the number is being used in a larger expression (so that it is treated as a unit). That means that 2^(1.2x10^-3) will not become 2^1.2x10^3 which would be interpreted as (2^3)x10^3, producing a error (the left-hand operand of x must be a simple number).

The format() was actually producing some incorrect results, which are addressed here. First, the wrong condition was being used to convert an f format to E, which is fixed in (new) line 250. It also was adding a . too aggressively. It should only add it when the result is an integer having exactly the number of significant digits as total digits. That is fixed here.

The computation functions are modified to mark the results as being computed values (so not pure numbers), which is part of the tracking of computations so that exponential form can be processed properly. A power() method is added to handle exponentiation (mostly for 10^n, but also allows other exponentials).

The rest of the changes are basically just new code to handle the exponential form as described above. There are some comments indicating the most important parts, but mostly is not all that interesting.

In any case, I think this implements what you are trying to do in your #27 and #30 (and builds on your #25).

@pstaabp
Copy link
Copy Markdown
Owner

pstaabp commented Mar 20, 2026

Thanks Davide. Realized that this branch is still based on the 2.20 (main) branch of PG, so I will probably merge this soon and then rebase.

I have some other work done (@sfiedle1) and I have been working on adding some flags for "close" answers with correct and incorrect number of significant figures and giving messages. This is in another branch (https://github.com/pstaabp/pg/tree/sig-fig-updates), but I think merge these changes in first, and then start another PR off of this.

I was working on some tests that you recommended with the reducedConstants flag set either on or off.

It seems that if reducedConstants => 1, and a simple example like 2.0*3.2 becomes a SigFigNumber of 6.4, however with this same flag

2.0*(3.2*10^2) is correctly 6.4E+02, but 2.0*3.2*10^2 is reduced to 6E+02.

I'm trying to figure out how the parsing of this is working. Is 2.0*3.2 being done first and somehow losing a sigfig?

@dpvc
Copy link
Copy Markdown
Author

dpvc commented Mar 20, 2026

I'm trying to figure out how the parsing of this is working. Is 2.0*3.2 being done first and somehow losing a sigfig?

Because multiplication is done left to right, 2.0*3.2 is done first, and that become a computed value, not a pure one, so the resulting 6.4*10^2 is not considered exponential form, and so the 10 in 10^2 is not part of exponential form, and so has only one digit. That means 10^2 is 1E+02, not 1.0E+02, to 6.4*10^2 is 6.4*1E+02, or just 6 (only one digit). This is one of the downsides to not having integers have their full precision.

You need to do 2.0*(3.2*10^2) in order to get the result you are looking for. I had meant to mention that in my write-up but forgot. It is, unfortunately, the only situation where this doesn't work as well as one would hope.

The only thing I can think of to help this would be to have multiplication produce a warning when *10^n is used with a computed number so that at least a warning is given in this situation. But that might end up being produced when *10^2 was meant to really be multiplication by 10^2, not exponential form. But that is probably the less likely occurrence.

@dpvc
Copy link
Copy Markdown
Author

dpvc commented Mar 20, 2026

PS, If you use Formula('2.0*3.2*10^2') when reduceConstants=>0 is in effect, you will see how the formula was parsed. In this case, as 2.0*3.2*(1x10^1)^2 which should help you identify that the 10 is being used as having only one significant digit, rather than indicating exponential form.

@pstaabp
Copy link
Copy Markdown
Owner

pstaabp commented Mar 20, 2026

I was thinking that it was doing the multiply of 2.0 and 3.2 first. I didn't think that then it was no longer a sigfig when multiplying by 10^2. I'll trying to add some documentation to explain how to navigate the waters with multiple operators.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants