cwal

s2: Numbers
Login

s2: Numbers

(⬑Table of Contents)

Numeric Types (Integer and Double)

See also: type introduction for supported numeric literal syntax.

Jump to...

Numbers (Integer and Double)

s2 (or, rather, cwal) supports signed integer values and double-precision floating point values, both with build-time-specified limits. By default the C-standard double type is used for doubles and either 32- or 64-bit integers are used for integers, depending on the platform. In C they are typedef'd as cwal_double_t resp. cwal_int_t. It is possible (and usual, but not required) to build with 64-bit integer support on 32-bit platforms.

ACHTUNG: for floating-point values, cwal does not guaranty numeric precision beyond what the system's local standard C library does. Do not expect to be able to use it with 20-digit precision. It also does no locale-specific conversions, so floating point numbers always (when using any built-in conversions) use a dot (not a comma) as the fractional separator.

While numbers are not objects and may not have custom properties, they have methods inherited from their prototype(s), listed in the following subsections. Note that it is legal to call a method on a numeric literal

1.1.ceil(); // ⇒ 2
1.3.floor(); // ⇒ 1
42.toDouble(); // ⇒ 42.0
0.prototype.twice = proc(){ return this * 2 };
2.twice(); // ⇒ 4

Shared Numeric-type Methods

The following methods are shared by both the integer and double types...

integer compare(Value[, Value])

Compares this value to the first argument using "memcmp semantics." If passed two values it compares those instead.

integer nthPrime(integer between 1 and 1000 (inclusive))

Returns the Nth prime number out of the first 1000 primes. Throws if its argument is out of range. This is primarily intended to assist in getting numbers to pass to the Hash constructor.

mixed parseDouble(value)

If value is a numeric value or (special case) boolean, it is converted to a double and returned. If it is a string which strictly looks like a complete number (in decimal, octal, binary, or hex format), it is parsed as such and its double value is returned. Returns undefined if it does not think the value represents a double or an integer (which it promotes to a double). Note that this will parse hex or octal integer values, but will coerce the result to a double.

mixed parseInt(value)

If value is a numeric value or boolean, it is converted to an integer and returned. If it is a string which strictly looks like a complete number (in decimal, octal, or hex format), it is parsed as such and its integer value is returned. If passed a double, it returns that value truncated to an integer. Returns undefined if it does not think the value represents an integer.

mixed parseNumber(value)

Works like a combination of parseInt() and parseDouble(). It returns either an integer or double value on success and undefined for non-numeric values. Boolean true/false are treated as the integers 1 resp. 0.

double toDouble()

Returns the double value of this number. This is a no-op for doubles and coerces integers into their double counterpart. Note that 64-bit integers have a larger integral range than normal doubles (but not long doubles), so this may lead to incorrect results with large integer values (more than 48 bits).

integer toInt()

Returns the integer form of this number. This is a no-op for integers. Doubles get truncated, not rounded (because round(3) is C99 and s2 is otherwise C89 compliant).

string toJSONString()

This is equivalent to the toString() method, but is provided for symmetry with other JSON-capable classes.

string toString([string format=undefined])

Returns the number in string form (potentially truncated, for doubles). e.g. "3" === 3.toString(). If passed a string, the string is expected to be the "trailing" part of a formatting flag compatible with Buffer.appendf(), namely the part after `$1%. e.g. 12.toString("04x")==="000c" and 1.2.toString("2.2f")==="1.20"1. Throws if passed an argument which is either not a string or which causes cwal_buffer_format() to complain (return non-0, in which case the exception message normally explains exactly which part of the formatting string is broken).

Reminder: Buffer.appendf()'s %s format only supports types which have a "native" string representation (namely strings and buffers), so numbers do not work with the %s formatting flag. One can, however, use the %d (integer) formatting flag to coerce a double or boolean to integer form, e.g. 2.3.toString('d')==="2", which is functionally equivalent to, but more efficient than, 2.3.toInt().toString().

Customizing Numeric Prototypes

While numbers are not containers and may not have properties, they do have a prototype which can be customized (in fact, the integer and double prototype inherits a common one for operations common to both types):

var dbl = 0.0;
dbl.prototype.twice = proc(){ return this * 2 };
assert 6.2 === 3.1.twice();
assert 8.0 === (2.0*2).twice();

Achtung: high-precision truncation/rounding errors are always a possibility with such comparisons because the input script is a string, so "8.0" might end up with a different literal double value than 4.0*2.0. For more information than one can possibly want to know regarding the reasons for this, feed your favourite search engine the phrase "why does 1.0 not equal 1.0".

Achtung again: Double and Integer each have their own prototypes which derive from a common prototype, so to add functionality to both integers and doubles, clients need to add it to 0.prototype.prototype. In other words, 0.prototype.prototype === 0.0.prototype.prototype!

Note that all numeric values are immutable and cannot be changed by, e.g. re-assigning to this in a custom method. (this is just a scope-level variable which disappears when the function returns.)

Installing operator overloads for the core operators will (by design!) not work for numeric types - the engine always uses the built-in operators for them, ignoring any client-installed ones, simply for sanity's sake. s2 does, however, allow "overloadable-only" operators to be used on these prototypes, as those have no default semantics which it could otherwise apply.

Double Methods

integer ceil()

Returns the smallest integral value not less than itself. i.e. it works like C's ceil(3), within some "reasonable" level of precision.

double floor()

Returns the largest integral value not greater than itself. i.e. it works like C's floor(3).

Potential TODO: add some numeric constants as properties, like PI (but we could use the UTF-8 Pi symbol, if i could ever find it in this drop-down menu).

Integer Methods and Properties

int rand()

Returns the same as the C-standard rand(3): the next pseudo-random number from the system's own RNG, possibly trimmed to fit within cwal's compile-time numeric range (if that's smaller than the platform's int type). Note that the RNG is not automatically seeded by the script engine: call srand() to seed it. If it is not seeded, it may (depending on its runtime environment) return the same sequence of numbers across all executions of a script.

THAT SAID: the exact RNG used by this function is unspecified. It may, in the future, be modified to use a different random number source.

void srand(int randomSeed)

Sets the random seed for future calls to the rand() method. This affects all C-level APIs which use rand(3). FWIW, the conventional approach to seed the RNG, if security is not an issue, is to pass it the current time, e.g. 0.srand(s2.time.time()), but keep in mind that a script which is run repeatedly within the same clock second would seed the RNG with the same value each time. Throws if not passed exactly 1 argument.

mixed toChar()

Returns the UTF-8 character for this integer as a length-1 string, undefined if it is not a UTF-8 character, or an empty string if it is 0 (the NUL byte).

Design notes: toChar() should arguably return a length-1 string ("\0") for the NUL-byte case, but that would likely just lead to confusion. While the vast majority of cwal- and s2-level APIs use the "proper" length of a String value, many C APIs do not (they depend on a NUL byte to mark the end of a string), and would consider a string of "\0" to have a length of 0. Thus we return an empty string (which has an implicit NUL byte, as cwal always NUL-pads strings which it creates (X- and Z-strings may come from client code, and might not be NUL-padded (but, in practice, always are unless someone specifically aims to misbehave)).

Prototype Properties

Footnotes

like %d does?


  1. ^ Reminder to self: why does %f not support a 0 prefix padding