This chapter describes GemStone’s numeric classes. These include Integers, floating point (limited-precision rational numbers), fractions (arbitrary precision rational numbers), and decimal numbers.
Most numbers can be specified as literals within your code, and most numbers can be used in expressions with, or converted to, other types of numbers.
Integers
Describes classes that represent whole numbers: SmallInteger and LargeInteger.
Binary Floating Point
Describes classes for binary floating point numbers: SmallDouble and Float.
Other Rational Numbers
Describes classes for other rational numbers with different ranges and precisions, including Fraction, FixedPoint, ScaledDecimal, and DecimalFloat.
Internationalizing Decimal Points using Locale
How to control the display of decimal points.
Random Number Generator
Information on the set of random number generator classes, providing random numbers of various purposes.
Integers in GemStone are composed of SmallIntegers and LargeIntegers. Most Integers you are likely to use will be SmallIntegers, in the range of -260 to 260 -1. Integers outside this range are represented by LargeIntegers. Operations that result in a value outside the SmallInteger range transparently result in LargeIntegers, and vice-versa
The literal syntax for Integer will create either a SmallInteger or LargeInteger.
Integers can be specified using radix notation, using the r or # characters.
For example, to specify the hex SmallInteger value FF, the following are all valid:
FFr16
FF#16
Number fromString: 'FFr16'
'ff#16' asNumber
SmallIntegers are special (immediate) objects, that is, the number itself is encoded in the OOP, making instances of this class both small (since no further storage is required) and fast. They are also unique, so SmallIntegers of the same value are always identical (==) as well as equal (=).
SmallIntegers have a range from -260 to 260 -1. Values outside this range must be represented as LargeIntegers.
LargeIntegers are not special objects; they require an OOP.
Each instance of LargeInteger is stored as an array of bytes, where every 4 bytes represents a base 4294967296 digit. The first 4 bytes are the sign digit (0 or 1), the next 4 bytes in that array constitute the least significant base 4294967296 digit, and the last 4 bytes are the most significant base 4294967296 digit.
Instances of LargeInteger have a maximum size of 4067 digits plus the sign. The maximum absolute value for a LargeInteger is (2130144 - 1). Attempting to create a LargeInteger that exceeds this maximum will fail with an Integer overflow error.
Floating point values in GemStone are composed of SmallDoubles and Floats. The most commonly used floating points will be SmallDoubles. While both SmallDouble and Float represents 8-byte binary floating point numbers, as defined in IEEE standard 754, SmallDoubles have a reduced exponent range. Some floating point values therefore can only be represented by instances of Float, rather than SmallDouble. Similarly to SmallInteger and LargeInteger, GemStone operations return one or the other as needed.
The numerical behavior of instances of Float is implemented by the mathematics package of the vendor of the machine on which the Gem process is running. There are slight variations in results with different platform’s implementation of the
IEEE-754 standard.
You can get the components of a floating point value using the methods signBit,
exponent, and mantissa.
SmallDoubles are special objects; as with SmallIntegers, the number itself is encoded in the OOP, making instances small and fast. They are also unique, so SmallDoubles of the same value are identical (==) as well as equal (=).
Each SmallDouble contains a 61 bit value, in IEEE format but with reduced exponent range. There is 1 sign bit, 8 bits of exponent and 52 bits of fraction. SmallDoubles are always in big-endian format (both on disk and in memory).
SmallDoubles can represent C doubles that have value zero or that have exponent bits in range 0x381 to 0x3ff, which corresponds to about 5.0e-39 to 6.0e38; approximately the range of C 4-byte floats.
Floats are not special objects; they require an OOP.
Each Float contains a 64 bit value in IEEE format, with 1 sign bit, 11 bits of exponent and 52 bits of mantissa. Floats are in cpu-native byte order when in memory, and the byte order of the extent when on disk.
In addition to the finite numbers, the IEEE standard defines floating point formats to include Infinity (positive and negative) and NaNs (not a Number), which can be quiet or signaling. NaNs results from an operations whose result is not a real number, such as:
-23 sqrt
%
PlusQuietNaN
Infinity results from operations that return a value outside the range of representation, such as:
32.0 / 0
%
PlusInfinity
ExceptionalFloats are named, unique instances of Float, not of SmallDouble. Exceptional Floats include:
PlusInfinity
MinusInfinity
PlusQuietNaN
MinusQuietNaN
PlusSignalingNaN
MinusSignalingNaN
Since the sign of NaNs is not defined, GemStone operations return only positive NaNs; they do not return MinusQuietNan or MinusSignalingNan.
An unusual quality of NaNs is that they are not equal to themselves. This means that NaNs can cause problems if used as keys of hashed equality-based collections.
PlusQuietNaN = PlusQuietNaN
%
false
Literal numbers in evaluated code that include a decimal point by default create a SmallDouble or Float. If the value is in the SmallDouble range, a SmallDouble will be created, otherwise a Float will be created.
Literal floats may be specified using exponential notation. For example, 5.1e3 and 5.1e-3 are valid SmallDouble literals.
ANSI specifies that float values may have exponents e, d, or q. These exponents, as well as E and D, are legal in GemStone, but have the same result: a SmallDouble or Float. Likewise, the ANSI class names FloatE, FloatD, and FloatQ can be used in code, but all resolve to Float class.
Note that using a plus sign before the exponent is not allowed in literal floats, although it can be used to create floating points from strings (using Float fromString:). This avoids ambiguity with Smalltalk dialects that would interpret this as the addition operator. For example, 5.1E+3, which historically GemStone would interpret as the same as 5.1E3, is disallowed; code must either omit the +, or include white space to clarify the addition operator.
SmallDoubles and Floats are printed by default using asString or printString, in the notation equivalent to the C printf expression %.16g. This provides a maximum of 16 significant digits, rounding the fractional portion and changing to exponent notation if the whole number portion has more than 16 digits.
You can use asStringUsingFormat: to control the details of how floating point numbers are formatted when printing. asStringUsingFormat: accepts an Array of three elements:
12.3456 asString
%
12.3456
12.3456 asStringUsingFormat: #(-8 2 false)
%
12.35
12.3456 asStringUsingFormat: #(4 10 true)
%
1.2345600000e01
For some application, binary floating points are problematic, since there are common decimal values that cannot be expressed exactly in binary floating point; for example, 5.1 does not have a precise binary floating point representation. This can make computation results incorrect. For example:
5.1 * 100000
%
509999.9999999999
There are several options to avoid this: Fraction, FixedPoint, ScaledDecimal, and DecimalFloat. These classes are independent of each other, and each provides different qualities of precision and range.
Fractions precisely represent rational numbers. Fractions are composed of an integer numerator and an integer denominator. As the ratio of two Integers, fractions can represent any rational number to an unbounded level of precision.
The display of fractions is as the numerator and denominator separated by the $/ character, which is also the division binary method. Fractions have no literal representation. An expression such as 1/3, which performs a division of two Integers, will return a fraction if the result is not an Integer.
(1/3) printString
%
1/3
Any expression, not just division expressions, that could result in fractions will be reduced automatically, to the lowest fraction or to an Integer.
(5/6) + (1/6)
%
1
SmallFractions are special objects, in which the OOP itself encodes the value. As with SmallDouble and Float, creating a fraction will result in either an instance of SmallFraction or Fraction, depending on the specific value.
SmallFractions can hold objects with numerators between -536870912 and 536870911, and denominators from 1 to 134217727.
FixedPoints, like Fractions, represents rational numbers, but also include information on how they should be displayed. A FixedPoint is composed of an integer numerator, integer denominator, and an integer scale. Like Fraction, this allows rational numbers to be represented with unbounded precision, and since fractional arithmetic is used in calculations, numerical results do not lose precision.
The scale provides automatic rounding when representing the FixedPoint as a String.
FixedPoint uses a literal notation using p, such as 1.23p2. This is not an exponential notation; the 2 here specifies scale. The values 1.23p2, 1.23p3, and 1.23p4 are all equal.
ScaledDecimals represent a decimal number to the precision of a fixed number of fractional digits. ScaledDecimals are composed of an integer mantissa and a power-of-10 scale. While ScaledDecimals represent decimal fractions to the precision specified, not all values can be represented exactly by ScaledDecimals. The maximum scale is 30000.
Literal ScaledDecimals can be created using the s notation; for example, 1.23s2. This is not an exponential notation; the 2 here is the scale, and mantissa is resized appropriately. The values 1.23s2, 1.23s3, and 1.23s4 are all equal.
The number of fractional digits must not be greater than the scale.
For returned values from mathematical operations, ANSI does not precisely specify the scale of a returned ScaledDecimal. The following rules are used:
For some mathematical operations, the returned value type is a ScaledDecimal, but the returned value cannot always be exactly represented as a ScaledDecimal with the correct scale. In these cases, the results are rounded using the following rules:
DecimalFloats represent base 10 floating point numbers, as defined in IEEE standard 854-1987.
Literal DecimalFloats can be specified in exponential notation using the f or F character; for example, 5.432F2 creates a DecimalFloat equivalent to 543.2.
Objects of class DecimalFloat have 20 digits of precision, with an exponent in the range -15000 to +15000. The first byte has encoded in it the sign and kind of the floating-point number. Bit 0 is the sign bit (0=positive, 1=negative). The values in bits 1 through 3 indicate the kind of DecimalFloat:
001x = normal
010x = subnormal
011x = infinity
100x = zero
101x = quiet NaN
110x = signaling NaN
Bytes 2 and 3 encode the exponent as a biased 16-bit number (byte 2 is more significant). The actual exponent is calculated by subtracting 15000. Bytes 4 through 13 form the mantissa of the number. Each byte holds two BCD digits, with bits 4 through 7 of byte 4 containing the most significant digit.
Similarly to Float, operations that would not result in a real number, or that produce a result outside the representable range, result in Exceptional numbers:
DecimalPlusInfinity
DecimalMinusInfinity
DecimalPlusQuietNaN
DecimalMinusQuietNaN
DecimalPlusSignalingNaN
DecimalMinusSignalingNaN
The following table lists the notations that may appear in a literal number.
You can instruct the compiler to understand a new numerical literal format by sending a message to your customized subclass of Number to register that format.
The following method provides this registration:
Number >> parseLiterals: aCharacter exponentRequired: aBoolean
Once this is sent to an instance of a subclass of Number, when the compiler encounters a numeric value using aCharacter, it will send fromString: to that class.
For example, say you have defined a class ComplexNumber. For the literal format, you wish to use NiM, where N represent the real part and M represents the imaginary part. So for example, 4.5+5i would be specified using the literal form 4.5i5.
First, you would define the ComplexNumber>>fromString: method, which will parse a string of the form NiM and return the new instance of ComplexNumber.
Then, to allow the literals to be included in code, send the following message.
ComplexNumber parseLiterals: $i exponentRequired: true
Now, assuming you have implemented the behavior appropriately, the compiler can evaluate expressions of the form:
(3.5i5 + 7.1i3) asString
%
10.6i8.0
Once invoked, the new literal format will be recognized until the session logs out.
Note that for subsequent logins, compiled references to that literal will continue to be valid, but unless the method is invoked again, methods with that literal cannot be recompiled. Including the invocation of parseLiterals:exponentRequired: in session initialization code (such as using loginHook:) is recommended.
To uninstall a custom literal without logging out, use the same method, passing in for aBoolean. For example,
The class Locale allows you to obtain operating system locale information and use or override it in GemStone. GemStone currently only uses the decimalPoint setting, to provide localized reading and writing of numbers involving decimal points. Updates to Locale are stored in session state, and only persist for the lifetime of the session. They are not affected by commit or abort.
Note that Smalltalk syntax requires the use of “.” as the decimal point separator, so expressions involving literal floating point numbers within Smalltalk code will still require use of the period, regardless of Locale.
To override the operating system locale information, use the following message:
Locale class >> setCategory: categorySymbol locale: LocaleString
Note that the LocaleString passed to setCategory:locale: must be defined on the host machine. If the given locale is not found, this method will return nil. You can use the UNIX command locale -a to get a list of all available LocaleStrings. To check the decimal point, the following method returns the decimalPoint setting for the current Locale:
Locale decimalPoint
%
,
While there are a number of Locale category symbols, the only ones that are of use in this release are #LC_NUMERIC and #LC_ALL, either of which will set the category that affects the decimal point.
For example, To use decimal localization appropriate for Germany:
Locale setCategory: #LC_NUMERIC locale: 'de_DE'.
To reset to UNIX default value, using period:
Locale setCategory: #LC_ALL locale: 'C'.
In order to be able to export and input numerical values regardless of the Locale of a particular session, methods whose printed form includes the decimal point provide the following set of methods:
(instance method) asStringLocaleC
(class method) fromStringLocaleC:
These methods use a period as a decimal separator, regardless of Locale.
The class Random and its subclasses provide random number generation.
There are two types of random number generation, which correspond to separate subclass hierarchies. The SeededRandom subclasses provide random numbers generated within GemStone code, using a starting seed value. The HostRandom subclass provides access to the host operating system’s /dev/urandom random number generator. This is much slower, but unlike the SeededRandom numbers, on some platforms this may be designed to be cryptographically secure.
The class hierarchy of the Random classes are:
Object
Random (abstract)
HostRandom
SeededRandom (abstract)
Lag1MwcRandom
Lag25000CmwcRandom
The Random class is an abstract superclass for the random number generators. It also can be used to create an instance of the default random number generator class, Lag25000CmwcRandom. Sending instance creation messages to Random will return instances of Lag25000CmwcRandom. For example:
Random seed: 12345
%
aLag25000CmwcRandom
Random new
will create an instance of Lag25000CmwcRandom that is seeded with numbers generated from the host OS /dev/urandom.
Once you have an instance of a concrete subclass of Random, you can generate random numbers or collections of random numbers with the following range and type specifications:
float - a random Float in the range [0,1)
floats: n - a collection of n random floats in the range [0,1)
integer - a random non-negative 32-bit integer, in the range [0,232-1]
integers: n - a collection of n random non-negative integers in the range [0,232-1]
integerBetween: l and: h - a random integer in the range [l,h]. l and h should be less than approximately 231.
integers: n between: l and: h - a collection of n random integers in the range [l,h]. l and h should be less than approximately 231.
smallInteger - Answer a random integer in the SmallInteger range,
[-260,260-1]
Subsequent calls to the same instance will generate new random numbers.
You should create an instance of a Random subclass and retain that to generate many random numbers, rather than creating new instances of a Random subclass.
HostRandom allows access to the host operating system's /dev/urandom random number generator.
HostRandom is much slower than the other subclasses of Random. However, /dev/urandom on some platforms may be intended to be a cryptographically secure random number generator, which none of the other subclasses are. It also has the advantage of not needing an initial seed, and so is good for generating random seeds for the faster Random subclasses.
HostRandom uses a shared singleton instance, which is accessed by sending #new to the class HostRandom. Sending #new has the side effect of opening the underlying file /dev/urandom. This file normally remains open for the life of the session, but if you wish to close it you can send #close to the instance, and later send #open to reopen it. If you store a persistent reference to the singleton instance the underlying file will not be open in a new session and you must send #open to the instance before asking for a random number.
Since HostRandom is a service from the operating system, it cannot be seeded, and should not be used when a repeatable random sequence of numbers is needed.
SeededRandom is an abstract superclass for classes that generate sequences of random numbers that can be generated repeatedly by giving the same initial seed to the generator.
In addition to creating new instances using the class methods new and seed:, the following instance methods allow repeatable sequences to be generated:
seed: aSmallInteger
Sets the seed of the receiver from the given seed, which can be any SmallInteger. The subsequent random number sequence generated will be the same as if this generator had been created with this seed.
fullState, fullState: stateArray
The internal state of a generator is more than can be represented by a single SmallInteger. These messages allow you to retrieve the full state of a generator at any time, and to restore that state later. The random number sequence generated after the restoration of the state will be the same as that generated after the retrieval of the state. You might, for instance, allow a generator to get its initial state from /dev/urandom, then save this state so the random sequence can be repeated later.
Lag1MwcRandom is intended for internal use only, in creating a random seed for instances of Lag25000CmwcRandom. Lag1MwcRandom is slower, is not perfectly fair, and has a shorter period, so the only advantage is its ability to be seeded by a single 61-bit SmallInteger, rather than a seed of more than 800000 bits as required by Lag25000CmwcRandom.
Lag25000CmwcRandom is a seedable random generator with a period of over 10240833. It is a lag-25000 generator using the complementary multiply-with-carry algorithm to generate random numbers. Its period is so long that every possible sequence of 24994 successive 32-bit integers appears somewhere in its output, making it suitable for generating random n-tuples where n<24994. Its output is fair in that the number of 0 bits and 1 bits in the full sequence are equal.
While this generator is recommended for most uses, it is not cryptographically secure, so for applications such as key generation you should consider using HostRandom, once you satisfy yourself that HostRandom is secure enough on your operating system.
You can also allow the seed bits to be initialized from the HostRandom, then retrieve that state by sending #fullState. That state can later be restored by sending the retrieved state as an argument to #fullState:.