reptile7's JavaScript blog
Saturday, February 25, 2023
 
Memory Coda and Most of the scie( ) Function Story
Blog Entry #418

I've got a bit more to say about the memory business before moving on...

Toward a more normal memory

Like the Super Calculator and its and keys, my handheld Casio fx-85v calculator has a key for basic memory input, although the two calculators have different post-memory behaviors, namely,
upon inputting a first number and copying it to memory and then inputting a second number,
the Casio overwrites the first number in the display field with the second number -
pressing and then and then gives a 7 display - whereas
the SC appends the second number to the first number in the total field -
pressing and then and then gives a 97 total value.

I prefer the Casio behavior to that of the SC and would sync the latter with the former via a meminput global boolean flag as per our approach to an M+ capability in the previous post. Accordingly, the more( ) function that controls the total display becomes:

var meminput = 0, memplus = 0;
function more(obj, where) {
    /* If the meminput or memplus flag was switched to 1 by the getinput( ) function, then a new input replaces a preceding input. */
    if (where.value == "" || where.value == "0" || meminput || memplus) where.value = obj;
    /* If both flags are 0, then a new input is appended to a preceding input. */
    else where.value += obj;
    /* meminput or memplus must be turned off for a new input having more than one character. */
    if (meminput) meminput = 0; if (memplus) memplus = 0; }


• You might prefer the original SC behavior if you wanted to do some arithmetic on the first number after memorizing it.
• Not mentioned previously: I named the M+ flag memplus and not mem+ because a JavaScript identifier cannot contain a + symbol.

My Casio also has an key and an M- function that is accessed by SHIFT-ing the key, and a sequential use of the key, the key, and the M- function clearly indicates that they all interact with a common hidden field.
In the previous post we converted the SC's key to an key
but if we alternatively add a new key and leave the key in place
then both controls will act on the memory2 field à la the Casio and
we can similarly add a new key that does so as well.

function getinput(func) { ...
    if (func == "mem-") { memminus = 1; return mem(mode, a, c); } ... }

var meminput = 0, memplus = 0, memminus = 0;
...updated mem( ) and more( ) code...

<button type="button" onclick="getinput('mem-');"> M- </button>


As a power of 10

Just above the SC's Memory section - at the top of its tables[2] table - is a Scientific Notation section.

Scientific Notation:


The key calls on a scie( ) function to express a standard form number in scientific notation, e.g., it'll convert 2023 to 2.023*10^3. The "standard form" and "scientific notation" number descriptors evidently have different definitions in the US and in the UK - I'll use their American definitions in the discussion that follows. Complementarily, the key calls on an unscie( ) function that solicits the user for the "decimal" (coefficient) and the "power that is given to 10" (exponent) of a scientific notation number and then outputs the corresponding standard form number.

Standard to scientific

The scie(mode, obj) function is called
and equipped with mode = 1|0 and obj = document.mainform.total arguments
by the getinput(func) function in the usual manner.
(See the complete, unabridged SC code for the full details.)

A switched-on mode effects the display of an alert( ) help pop-up whose message reads:
Scie is [a] cool function that allows you to convert any given number into scientific notation. The format of the answer is as follows: Ex: input:120, output:1.2*10^2, would be 1.2 multiplied by 10 to the second power.

Next, the user is prompt( )-queried for a number to express in scientific notation - you know the drill:

if (obj.value != "" && obj.value != "0") {
    aa = window.prompt("Do you want to convert the current number in the total text box?", "y or n");
    if (aa == "y") a = obj.value;
    else a = window.prompt("Enter a number to convert:", ""); }
else a = window.prompt("Enter a number to convert:", "");


The standard form number input is given an a identifier
and is converted into scientific notation by the following code:

var b = 0;
while (a > 10) { // Divide by ten until variable is no longer greater than 10
    a /= 10;
    b += 1; }


A while loop iteratively divides a by 10 until a is less than or equal to 10. Meanwhile, a b counter counts up the number of divisions and will subsequently serve as the exponent in the final output.

Lastly, a doit(a + "*10^" + b, obj) command
concatenates a, *10^, and b
and the resulting string and obj are shipped off to the doit( ) function,
which loads the former into the latter.
Per the No offshoring needed subsection of the previous post, the doit( ) action is actually unnecessary:
an obj.value = a + "*10^" + b assignment works just as well.

Starting a scope notes

If a is itself a positive integer power of 10, say 100000, then the while divisions will stop when a hits 10 and the output will be 10*10^4 versus the 1*10^5 output we would want: adjusting the while condition to a >= 10 solves this problem.

For as less than 10,
the while condition returns false from the get-go,
b stays at 0,
and the output has the form a*10^0,
which is what we would want
for positive as ranging from 1 up to but not including 10
and for negative as ranging from -1 down to but not including -10
but is not what we would want
for positive and negative as between 1 and -1
and for negative as less than or equal to -10.

We can accommodate all number as and also weed out nonnumber as with the following code:

var b = 0;
while (! isNaN(a)) {
    if (a >= 10 || a <= -10) { a /= 10; b += 1; }
    else if (a < 1 && a > 0 || a < 0 && a > -1) { a *= 10; b -= 1; }
    else break; }
    if (a == 0) { obj.value = 0; return; }
if (isNaN(a)) { window.alert("Please input a number."); obj.value = 0; return; }
obj.value = a + "*10^" + b;


For positive and negative as between 1 and -1, a is iteratively multiplied by 10 and b is decremented until a rises above 1 or falls below -1, e.g., 0.000017 (the acid dissociation constant for acetic acid) is converted to 1.7*10^-5.

When a reaches its final value, the ! isNaN(a) condition is still true, so the loop is terminated with a break statement, otherwise it would run indefinitely.

The a value can be 0 or the empty string
if the user respectively enters 0 into or leaves blank
and s the
The 'Enter a number to convert:' prompt( ) box
prompt( ) box that pops up if either
(a) the total field holds 0 or is blank initially
or (b) the user does not answer y to the
The 'Do you want to convert the current number in the total textbox?' prompt( ) box
prompt( ) query.
(The preceding images are Preview-enlarged screen shots that were originally taken when using Firefox.)
In these cases - given that "" == 0 returns true1 - the else if (a == 0) clause resets the total value to 0 and ends the scie( ) function via a return statement.
FYI: 0 is a special case [that] does not have a unique representation in scientific notation, i.e., 0 = 0 × 100 = 0 × 101 = ...

By nonnumber as I specifically mean any and all a strings for which Number(a) returns NaN. A nonnumber a value is converted accordingly to NaN for the original a > 10 while condition1, which in that case returns false as when NaN is one of the operands of any relational comparison (>, <, >=, <=), the result is always false, quoting Mozilla, and the output again has the form a*10^0. We have seen previously that nonnumber strings include not just alphabetic text like Hello World but also legitimate mathematical expressions like 123*(456+789), which should be eval( )ed with the key before being fed to the scie( ) function. As detailed above, my code flags nonnumber as and then pops up a Please input a number alert, resets the total value to 0, and returns.

1When comparing a string with a number, JavaScript will convert the string to a number when doing the comparison. An empty string converts to 0. A nonnumeric string converts to NaN[, which always causes a false return] - see the Comparing Different Types section of W3Schools' JavaScript Comparison and Logical Operators page.

The null thing

Up to this point I have negligently not said anything about what happens if the user cancels the various SC function prompts, and now is a good time to do so. Clicking the button on a prompt( ) box returns the primitive value null (not "null" as a string). Number(null) returns 0 and isNaN(null) relatedly returns false, and the null value that is returned by canceling the SC functions' 'Enter a number to do X' prompts2 does behave like 0 for the mathematical operations that follow those prompts, e.g., Math.cos(null) returns 1 and Math.log(null) returns -Infinity.
2A user usually first sees a
'Do you want to use the current number in the total text box to do X?' prompt,
whose cancelation leads to an 'Enter a number to do X' prompt -
see, e.g., this code given earlier.

For the scie( ) function, then, canceling the "Enter a number to convert:" prompt (vide supra) gives an a = null
whose a > 10 while test returns false and that is outputted as null*10^0. It would be better to intercept that null as we did the empty string and other nonnumber as, yes?

The a = null gets through the modified ! isNaN(a) while gate but there is some strangeness in null vs. 0 comparisons in that
null >= 0 and null <= 0 both return true but
null == 0 itself returns false.
Stack Overflow has a Why 'null >= 0 && null <= 0' but not 'null == 0'? page at which commenters Michael Liquori and/or Christian C. Salvadó link to relevant sections in the ECMAScript-262 Edition 5.1 specification -
specifically, 11.8.5 The Abstract Relational Comparison Algorithm (ARCA) and 11.9.3 The Abstract Equality Comparison Algorithm (AECA) -
that together with other sections in the specification provide detailed procedures for carrying out such comparisons. In brief, here's what happens:

(R) For the null >= 0 and null <= 0 relational comparisons, null is converted to +0 (yes, positive zero) via a ToNumber(null) operation (this also occurs for the null > 10 relational comparison). The mathematical value of +0 is simply 0 (see the eighth paragraph of this specification section), and the ARCA tests if the left-hand +00 is less than the right-hand 0 and returns false, which is subsequently adjusted to true for the >= operator and for the <= operator.

(E) For the null == 0 equality comparison, the AECA leaves null alone and returns false - that's it.

Anyway, getting back to the interception of an a = window.prompt("Enter a number to convert:", "");-canceled null in the scie( ) function, it follows that an a == 0 test isn't going to do the trick. A simple solution is at hand, however; we can trap null (or the empty string or 0 while we're at it) by Number( )izing a before comparing it to 0:

if (Number(a) == 0) { obj.value = 0; return; }

An archival aside:
We first discussed the prompt( ) method of the window object, and the null return, in Blog Entry #11, which dates to my early blogging days and which I admit I find somewhat painful to read today. My choice of links for that entry provides a snapshot of the thrashing around I did documentation-wise prior to discovering Netscape's and the W3C's reference materials. Most but not all of those links had been dead for a while when I began writing the current post; I've recently gone to the trouble of repairing the dead ones, so you can go check out The Prompt( ) Method for yourself if you would like a pristine taste of my 'humble beginnings'.

More ECMAScript specification notes, in the name of completeness

Without getting into its grammar productions, the 9.3.1 ToNumber Applied to the String Type section covers the "" == 0 and nonnumeric string → NaN situations that were addressed earlier:
If the grammar cannot interpret the String as an expansion of StringNumericLiteral, then the result of ToNumber is NaN.
The MV [mathematical value] of StringNumericLiteral ::: [empty] is 0.

In actuality, a relational comparison with a NaN operand initially returns undefined, which is subsequently adjusted to false.

The ECMAScript-262 Edition 5.1 specification went live in June 2011; the current ECMAScript specification is located at
https://tc39.es/ecma262/.
In our next episode we'll deal with two more scie( ) function issues:

(1) Do you like the 2.023*10^3 output format? No? I don't either. Let's see if we can change it to 2.023×103, shall we?

(2) I ran 12345 through the scie( ) function and got 1.2345000000000002*10^4. Huh? What's with all those trailing digits? We'll get this worked out.


Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)