Newton 2.x Q&A Category: Utility Functions

Copyright © 1997 Newton, Inc. All Rights Reserved. Newton, Newton Technology, Newton Works, the Newton, Inc. logo, the Newton Technology logo, the Light Bulb logo and MessagePad are trademarks of Newton, Inc. and may be registered in the U.S.A. and other countries. Windows is a registered trademark of Microsoft Corp. All other trademarks and company names are the intellectual property of their respective owners.


For the most recent version of the Q&As on the World Wide Web, check the URL: http://www.newton-inc.com/dev/techinfo/qa/qa.htm
If you've copied this file locally, click here to go to the main Newton Q&A page.
This document was exported on 7/23/97.

Utility Functions


What Happened to FormattedNumberStr (2/12/96)

Q: The Newton 1.x documentation and OS included a sprintf-like function for formatting numbers called FormattedNumberStr. The Newton Programmer's Guide 2.0 First Edition (beta) says this function is no longer supported. How do I format my numbers?

A: You may continue to use FormattedNumberStr. Here is the FormattedNumberStrAPI that is supported. FormattedNumberStr should be considered to have undefined results if passed arguments other than those specified here.

FormattedNumberStr(number, formatString)

Returns a formatted string representation of a real number.

number A real number.
formatString A string specifying how the number should be formatted.

This function works similar to the C function sprintf. The formatString specifies how the real number should be formatted; that is, whether to use decimal or exponential notation and how many places to include after the decimal point. It accepts the following format specifiers:
%f Use decimal notation (such as "123,456.789000").
%e Use exponential notation (such as "1.234568e+05").
%E Use exponential notation (such as "1.234568E+05").

You can also specify a period followed by a number after the % symbol to indicate how many places to show following the decimal point. ("%.3f" yields "123,456.789" for example.)

Note: FormattedNumberStr uses the current values of GetLocale().numberFormat to get the separator and decimal characters and settings. The example strings above are for the US English locale.

Known Problems
Other specifiers
Do not use other formatStrings. Previous releases of the documentation listed %g and %G as supported specifiers. The behavior of these specifiers has changed with the Newton 2.0 OS. Given the similarities to the sprintf function, it may occur to you to try other sprintf formatting characters. Specifiers other than above have an undefined result and should be considered undocumented and unsupported.

Large numbers
FormattedNumberStr does not work properly for numbers larger than 1.0e24. If the number is very large the function can cause the Newton device to hang.

Small numbers or long numbers
If more than 15 characters of output would be generated, for example because you are using %f with large number or a large number of digits following the decimal, FormattedNumberStr has undefined results, and can cause the Newton device to hang.

Rounding
FormattedNumberStr does not guarantee which direction it will round. In the Newton 2.0 OS, it rounds half cases down rather than up or to an even digit. If you need a precisely rounded number you should use the math functions Ceiling, Floor, NearbyInt, or Round with suitable math.

Trailing decimals
In early releases of the Newton 1.0 OS, there was a bug in FormattedNumberStr that caused a trailing decimal character to be added when zero decimal positions was specified. That is, FormattedNumberStr(3.0, "%.0f") resulted in "3." not "3". To properly test for and remove this unwanted extra character you must be sure to use the character specified in the Locale settings and not assume the decimal character will be a period.


Backlight API (4/19/96)

Q: What is the API to check for and use the backlight?

A: There are three relevant pieces of information:

Checking for the backlight

To check if the backlight is there, use the Gestalt function as follows:

// define this somewhere in your project
// until the platform file defines it (not in 1.2d2)
constant kGestalt_BackLight := '[0x02000007, [struct, boolean], 1];

local isBacklight := Gestalt(kGestalt_BackLight);

if isBacklight AND isBacklight[0] then
   // has a backlight
else
   // has not got one

Status of the backlight

To find the current state of the backlight, use the following function:
    BackLightStatus()

return value = nil (backlight is off) or non-nil (backlight is on)

Changing backlight status

To turn the backlight on or off, use:

    BackLight(state)

return value - unspecified
state - nil (turn backlight off) or non-nil (turn backlight on)


Unusual Sort Order/Case Sensitivity in Swedish Locale (1/16/97)

Q: When I set the unit to the Swedish locale and use the StrPos global function to search for a ':' character, it finds other characters such as '.' or ';'. Isn't that a bug? How can I reliably search for these characters in any locale?

A: The global function StrPos and many of the other string functions are case insensitive - they treat upper and lowercase letters as being identical. In other languages, characters such as accented letters may be considered as different cases of the base letter, so they are treated as identical as well. In the Newton OS model, the concepts 'same case' and 'same position in the sorting order' are not distinguished, so all cases of a letter will sort to the same position. Going backwards, all characters that sort identically are considered to be different cases of the same letter. Well, in the Swedish sort order, many punctuation characters are defined to sort to the same place, and so the case insensitive functions in the Newton device treat the characters as identical. Many special and punctuation characters are grouped this way, but perhaps the most surprising set is ? Á : , . ; À and !, which all sort to the same position and so are treated as identical in Swedish by StrPos and other case insensitive functions.

To search a string for a particular character using a case sensitive search, use the CharPos function instead of StrPos.


Time Zones, GMT, Daylight Savings, and Newton Time (3/4/97)

Q: There don't seem to be any functions in the Newton OS for converting between standard time values, such as finding the time in a different time zone, or GMT time. I know it's possible because the built in Time Zones application does it. How can I do this in my own application?

A: The Newton OS doesn't actually have the concept of time zones. Instead, for each city if keeps track of the offset (in seconds) from GMT for that city. You can find this in the 'gmt slot of a city entry, which can be gotten with the GetCityEntry global function. See the "Built In Apps and System Data" chapter of the Newton Programmers Guide for details. Note that the docs incorrectly say the gmt slot contains the offset in minutes, when it is actually specified in seconds. The current location is available in the 'location slot of the user configuration frame. Use GetUserConfig('location) to access it. The global function LocalTime can be used to convert a time to the local time in a distant city.

A simple way to get the local time from a GMT time would be to create a city entry representing GMT (gmt offset 0, no daylight savings) and then use LocalTime to compute the delta between the current city and the GMT city, then add the delta to the given GMT time. LocalTime can be used directly to go the other way--getting the GMT time from the local time.

LocalTime(time, where)
time - a time in minutes in the local (Newton device) zone, for example as returned from the Time function
where - a city entry, as returned from GetCityEntry
result - a time in minutes in the where city, adjusted as necessary for time zone and daylight savings.

LocalTime tells you the local time for the distant city, given a time in the current city. For example, to find out the time in Tokyo:

    Date(LocalTime(time(), GetCityEntry("Tokyo")[0]))
    #C427171  {year: 1997, month: 2, Date: 22, dayOfWeek: 6, 
          hour: 8, minute: 1, second: 0, daysInMonth: 28}



Because the Newton OS doesn't have time zones, it can't keep track of daylight savings time by changing zones (for example, from Pacific Standard Time to Pacific Daylight Time). Instead, it uses a bunch of rules that tell it when to set the time ahead or back, and by how much. The global function DSTOffset can be used to find out how much these daylight savings time rules have adjusted a given time for a given city.

DSTOffset(time, where)
time - a time in minutes in the where city
where - a city entry, as returned from GetCityEntry
result - an integer, number of minutes that daylight savings adjusted that time in that city.

DSTOffset tells you what the daylight savings component is of a given time in a given location. This component would need to be subtracted from the result of the global function Time to get a non-daylight-adjusted time for the current location.
    // it's currently 2:52 PM on 3/4/97, no DST adjustment
    DSTOffset(Time(), GetCityEntry("Cupertino")[0]);
    #0        0

    // but during the summer, DST causes the clocks to "spring forward" an hour.
    DSTOffset(StringToDate("6/6/97 12:34"), GetCityEntry("Cupertino")[0]);
    #F0       60



Square Root of Negative Number Bug (3/4/97)

Q: When I call Sqrt with a negative number on the Newton, or use Compile in the NTK Inspector, I get a strange result. However, if I just type sqrt(-2) into the listener I get a different strange result. What's going on?

    call compile("sqrt(-2)") with ()
    #4412F2D  -1.79769e+308

    sqrt(-2)
    #440DE05  1.00000e+999


A: There is a floating point library bug in Sqrt on the Newton OS. When passed a negative number, the large positive value is returned instead of a not-a-number value. You can work around it using Pow(x, 0.5) instead of Sqrt(x) if there is no way to guarantee that the value passed to Sqrt is non-negative, or simply check and see if the argument is less than 0 and return a not-a-number constant.

The reason sqrt(-2) works differently when you type it into the NTK Inspector is because of a compiler process known as constant folding. Sqrt can be evaluated at compile time if you pass it a constant argument. So what's really happening is that NTK is evaluating the Sqrt function during the compile phase and passing the resulting floating point number (or rather, not-a-number) to the Newton device where it's promptly returned. An NTK real number formatting limitation displays non-a-number values and infinities as 1.00000e+999 rather than as some other string. You can use IsNAN to determine if a real number is a not-a-number value.

You can avoid constant folding and force evaluation on the Newton device by using a variable. For instance:
    x := -2;
    y := sqrt(x);
    #C4335B1  -1.79769e+308


Also, note that FormattedNumberStr does not properly handle not-a-number values. (it returns "Number too small.")


Making Use of the Serial Number Chip (4/3/97)

Q: I would like to get the serial number from the units that support it, as either an integer or real number. How can I do this?

A: You probably don't really want to do this. The serial number is an 8-byte binary object, so you could use ExtractByte or ExtractWord or possibly ExtractLong to get the bytes out in integer form, then do something with them. However, keep in mind that NewtonScript integers are only 30 bits wide, whereas the serial number is 64 bits wide, so you'll never be able to put all the information contained in the serial number into a single integer. (3 integers would be required.)

That is, let us suppose you added up the value of all the bytes in a serial number. You would get a single NewtonScript integer, but it would also be possible for a different serial number to produce the same integer. (Just swap the positions of two of the bytes.) Same goes for XOR or any checksumming scheme. There's just no way to reduce 64 bits of information to 30 bits without allowing loss of uniqueness. (If you come up with a way, let us know, it'd make a great compression algorithm!)

Real numbers aren't suitable either, for much the same reason. It's true that in NewtonScript reals are 8 bytes wide, but they use the IEEE 64-bit real number specification, and so not all combinations of 8 bytes are considered unique. That is, you might think about taking the serial number result and using SetClass to change it's class to 'real, which would effectively "cast" the 8-byte object to a real number. This is a bad idea, because real numbers are interpreted using bitfields with special meanings, and it's possible for two real numbers to have different binary representations and still evaluate as equal using the '=' operator. (Any two not-a-number values will do this.)

Serial numbers are best treated as strings or as 8-byte binary objects, so that no data is lost. StrHexDump is the best way to format the serial number object for humans to read. If you want to break it up to make it more easily readable, you could do something like this:
    local s := StrHexDump(call ROM_GetSerialNumber with (), 2);
    StrReplace(s, " ", "-", 3);

Which produces this string (on my unit):
    "0000-0000-0154-8423 "

Please note that the serial number provided by the chip does NOT match the serial number that Apple Computer and other Newton device manufacturers may put on the outside of the case. When supporting a device, Apple and its licensees will most likely request the user-visible serial number, typically found on a sticker on the case. Please be sure that you present data from the internal chip-based serial number in such a way as to ensure the user will not be confused. (This is the reason the chip-based serial number is not displayed by any software built into the device.)


Programmatically Cancelling a Confirm Slip (4/3/97)

Q: During an operation, I bring up a slip to ask the user if they really want to abort the operation. Before they answer, the operation may complete or be aborted anyway. I would like to remove the slip if this happens, much like the "Remount" slip is removed when a gripped card is ejected, reinserted, and then re-ejected.

A: There's no way to dismiss a ModalConfirm slip, because your code is paused waiting for the result. You can, however, remove an AsyncConfirm slip. The return value from AsyncConfirm (which is documented in the Newton Programmer's Guide as "unspecified") is actually a reference to the confirm view. Sending that view a Close message dismisses the slip. The callback function will not be called if this slip is removed in this way, so make sure your program handles that case.