Number
Number is an Object that wraps a single primitive:
any of xtc’s sized integers (i8/u8/i16/u16/i32/u32/i64/u64) or
a float/double. A typed collection stores primitives in it (an Array<i32>
keeps its elements as Numbers), and it lets a heterogeneous collection hold
mixed values.
#import "Foundation.xc" // or #import "Number.xc"Overview
Section titled “Overview”A Number has two storage kinds, integer and float, and one is
canonical at a time. The setters (setI8, setFloat …)
set the kind, the predicates isInt / isFloat
report it, and the getters read the value back in whatever type you ask for.
Two slots, widest of their kind. Integers are held bit-preserving in an
i64 slot, floats in an IEEE-754 double slot. Every narrow accessor
(asI8, asU16, asFloat …) is a cast from one of the two. The wide slots
are what make Array<i64> and Array<double> possible.
Cross-kind conversion is lazy and cached. Each Number tracks a two-bit
valid bitmap. Reading a float out of an int Number (or an int out of a float
one) converts on the first call, stores the result in the inactive slot, and
sets its valid bit. A Number compared against many values of the other kind
pays the conversion cost once. Any setter clears the other bit, so a stale
conversion never survives a new value.
Bit-preserving casts. setU32(0xFFFFFFFF).asI32() returns -1 and
setI32(-1).asU32() returns 0xFFFFFFFF, matching plain (i32)/(u32) casts.
As a result, withU32(0xFFFFFFFF).asFloat() converts from the signed view
(-1.0), not 4294967295.0. Set the kind with withFloat
if you need the unsigned magnitude.
Conforms to
Section titled “Conforms to”Comparable:compareorders twoNumbers (cross-kind via float promotion), so numbers can be sorted and used as keys.Hashable:hashmakes aNumberaMap/Setkey, and it agrees withequals:Int(42)andFloat(42.0)hash the same.
Every Number* is also an Object* and fits anywhere
one is expected, such as a collection slot or a dictionary value.
Topics
Section titled “Topics”Creating (inferred kind) · with
Creating (pinned kind) · withI8 / withU8 · withI16 / withU16 · withI32 / withU32 · withI64 / withU64 · withFloat / withDouble · init
Setting (inferred kind) · set
Setting (pinned kind) · setI8 / setU8 · setI16 / setU16 · setI32 / setU32 · setI64 / setU64 · setFloat / setDouble
Reading (by destination) · value
Reading (pinned type) · asI8 / asU8 · asI16 / asU16 · asI32 / asU32 · asI64 / asU64 · asFloat / asDouble
Predicates · isInt / isFloat
Protocol methods · description · equals · compare · hash
Creating (inferred kind)
Section titled “Creating (inferred kind)”static Number* with(i8 v) static Number* with(u8 v)static Number* with(i16 v) static Number* with(u16 v)static Number* with(i32 v) static Number* with(u32 v)static Number* with(i64 v) static Number* with(u64 v)static Number* with(float v) static Number* with(double v)Overloaded on the argument’s type: the compiler picks the storage kind from
v. Use it when the literal’s natural type is the one you want;
Number.with(50000) boxes a u16. To force a wider or differently-signed slot,
use the pinned withXxx factory instead.
Creating (pinned kind)
Section titled “Creating (pinned kind)”Each factory sets the storage kind regardless of the argument’s natural type. Use them when you need a wider or differently-signed slot than the natural fit.
withI8 / withU8
Section titled “withI8 / withU8”static Number* withI8(i8 v)static Number* withU8(u8 v)A Number holding an 8-bit signed / unsigned integer.
withI16 / withU16
Section titled “withI16 / withU16”static Number* withI16(i16 v)static Number* withU16(u16 v)A Number holding a 16-bit signed / unsigned integer.
withI32 / withU32
Section titled “withI32 / withU32”static Number* withI32(i32 v)static Number* withU32(u32 v)A Number holding a 32-bit signed / unsigned integer.
withI64 / withU64
Section titled “withI64 / withU64”static Number* withI64(i64 v)static Number* withU64(u64 v)A Number holding a 64-bit signed / unsigned integer. Needs -DENABLE_64BIT=1
on xt6502 (see Availability).
withFloat / withDouble
Section titled “withFloat / withDouble”static Number* withFloat(float v)static Number* withDouble(double v)A Number holding a floating-point value (both stored in the double slot).
void init(void)The default initializer: an integer Number holding 0. Prefer new Number()
plus a setter, or the with… factories; you rarely call init directly.
Setting (inferred kind)
Section titled “Setting (inferred kind)”void set(i8 v) void set(u8 v)void set(i16 v) void set(u16 v)void set(i32 v) void set(u32 v)void set(i64 v) void set(u64 v)void set(float v) void set(double v)The mutating counterpart of with: overloaded on the argument’s type,
it stores a new value in an existing Number, picking the kind from v.
Setting (pinned kind)
Section titled “Setting (pinned kind)”Each setter sets the canonical kind, writes its slot, and updates the valid bitmap, so the next cross-kind read converts afresh rather than returning a stale conversion.
setI8 / setU8
Section titled “setI8 / setU8”void setI8(i8 v)void setU8(u8 v)Store an 8-bit integer, pinning the int kind.
setI16 / setU16
Section titled “setI16 / setU16”void setI16(i16 v)void setU16(u16 v)Store a 16-bit integer.
setI32 / setU32
Section titled “setI32 / setU32”void setI32(i32 v)void setU32(u32 v)Store a 32-bit integer.
setI64 / setU64
Section titled “setI64 / setU64”void setI64(i64 v)void setU64(u64 v)Store a 64-bit integer.
setFloat / setDouble
Section titled “setFloat / setDouble”void setFloat(float v)void setDouble(double v)Store a floating-point value, pinning the float kind.
Reading (by destination)
Section titled “Reading (by destination)”i8 value(void) u8 value(void)i16 value(void) u16 value(void)i32 value(void) u32 value(void)i64 value(void) u64 value(void)float value(void) double value(void)Overloaded on return type: value() picks its type from the expected-type
context (assignment LHS, variable declaration, call argument), the same
mechanism Math.rand() uses to pick i16 or float. i32 v = n.value();
reads the int view; float f = n.value(); reads the float view. Each narrow
overload goes through the cache-aware conversion points, so kind conversion
happens at most once.
Reading (pinned type)
Section titled “Reading (pinned type)”Explicit return type. asI64 and asDouble are the cache-aware conversion
points. Every other getter delegates to them with a bit-preserving cast, so a
stale slot is converted at most once.
asI8 / asU8
Section titled “asI8 / asU8”i8 asI8(void)u8 asU8(void)The value truncated to 8 bits, signed / unsigned.
asI16 / asU16
Section titled “asI16 / asU16”i16 asI16(void)u16 asU16(void)The value truncated to 16 bits.
asI32 / asU32
Section titled “asI32 / asU32”i32 asI32(void)u32 asU32(void)The value truncated to 32 bits.
asI64 / asU64
Section titled “asI64 / asU64”i64 asI64(void)u64 asU64(void)The full 64-bit integer view. asI64 is a conversion point: on a float Number
it converts via the (i64)double cast (truncate toward zero, saturate to 0 on
overflow), caches the result, and returns the cached value on later calls.
asFloat / asDouble
Section titled “asFloat / asDouble”float asFloat(void)double asDouble(void)The floating-point view. asDouble is a conversion point: on an int Number it
converts via (double)i64 (exact to 2^53, losing precision beyond), caches the
result, and returns the cached value on later calls. asFloat narrows that to
binary32.
Predicates
Section titled “Predicates”isInt / isFloat
Section titled “isInt / isFloat”bool isInt(void)bool isFloat(void)Which kind is canonical: the one a setter last set. One of the two is always
true.
Protocol methods
Section titled “Protocol methods”The Object / Comparable
/ Hashable hooks that give Number value semantics.
description
Section titled “description”String* description(void)The %@ hook. An int renders exactly (String.withI64);
a float renders to six decimal places (String.withFloat’s default, matching
C’s printf %f).
equals
Section titled “equals”bool equals(Number* other)bool equals(Object* other)Value equality. Same-kind compares stay bit-exact; cross-kind promotes both
sides to float, so Number.withI16(42) equals Number.withFloat(42.0), and
a non-integer float never equals any integer Number. The Object* overload is
the Comparable slot heterogeneous containers use;
it returns false against a non-Number.
compare
Section titled “compare”i8 compare(Number* other)i8 compare(Object* other)Ordering: <0 / 0 / >0 (the C / NSComparisonResult convention). The kind
rules match equals, so Int(42) and Float(42.0) compare
equal here too. Comparing against a non-Number returns 0 (“these sort
equally”), because there is no meaningful order between a Number and another
kind of object. Pass a comparator if you need an order across kinds.
u32 hash(void) // u8 on the xt6502 buildScrambles the 32-bit integer view. Cross-kind equals promotes to float, and
Int(42) and Float(42.0) both asI32() to 42, so they hash the same, as
the Hashable contract requires. Float(42.5) also folds to
42 and collides with Int(42), but probe-chain equality keeps lookups
correct.
Worked example
Section titled “Worked example”#import "Stdio.xc"#import "Foundation.xc"
i32 main(void){ Number* n = Number.withI16((i16)-42); if (n.isInt()) Stdio.printf("%d\n", n.asI16()); // -42
Number* f = Number.withFloat(3.25); Stdio.printf("%s\n", f.description().cString()); // 3.250000
// Cross-kind equality: promotes to float. Number* a = Number.with((i16)42); Number* b = Number.withFloat(42.0); Stdio.printf("%d\n", (i16)a.equals(b)); // 1 (true) return 0;}