Crash BASIC Data Types
This guide covers all data types in Crash BASIC, their operations, and how they convert between each other.
Type Overview
| Type | Declaration | Description | Example |
|---|---|---|---|
| INTEGER | DIM x AS INTEGER |
64-bit signed whole number | 42, -100, 0 |
| FLOAT | DIM x AS FLOAT |
64-bit floating point | 3.14, -0.5, 1.0 |
| STRING | DIM x AS STRING |
Text (UTF-8) | "hello", "" |
| COORDINATE | DIM p AS COORDINATE |
N-dimensional point | (10, 20), (1, 2, 3) |
| RANGE | DIM r AS RANGE |
Rectangle from two coordinates | (0,0)-(100,100) |
| ARRAY | DIM arr(10) AS INTEGER |
Indexed collection | arr(0), arr(5) |
| OBJECT | DIM obj AS OBJECT |
Key-value dictionary | obj("key") |
| MUTATION | DIM m AS MUTATION |
List of 2D points | MUTATION((0,0), (100,100)) |
INTEGER
What it is: A 64-bit signed whole number. Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.
What it's used for: Counters, indices, sprite IDs, screen coordinates, game scores, health points, or any value that doesn't need decimal precision.
Operations
DIM a AS INTEGER
DIM b AS INTEGER
a = 10
b = 3
' Arithmetic
PRINT a + b ' Addition: 13
PRINT a - b ' Subtraction: 7
PRINT a * b ' Multiplication: 30
PRINT a \ b ' Integer division: 3 (truncates toward zero)
PRINT a MOD b ' Modulo (remainder): 1
PRINT -a ' Negation: -10Integer Division vs Float Division
PRINT 7 \ 2 ' Integer division: 3
PRINT 7 / 2 ' Float division: 3.5Use \ when you need a whole number result. Use / when you need decimal precision.
Number Literal Formats
Whole numbers can be written in decimal, hexadecimal, or octal. Hex and
octal use the classic BASIC &H / &O prefixes (case-insensitive):
x = 255 ' decimal
x = &HFF ' hexadecimal (also &hff) -> 255
x = &O377 ' octal (also &o377) -> 255These are most useful for packed color values in direct-color mode
(SET PALETTE OFF), where a color is a literal 0xAARRGGBB:
SET PALETTE OFF
COLOR &HFFFF8800 ' opaque orange: A=FF, R=FF, G=88, B=00Note: The
&H/&Oprefixes take precedence over the&name
dereference operator (see Strings as Handles below).&HFFis always
the hex value 255 — to dereference a variable whose name starts withH/O, the rest must contain a non-digit (e.g.&Handleris still a
deref becauseandlerisn't all hex digits).
Truthiness
0is falsy- Any non-zero value (
1,-1,42, etc.) is truthy
DIM health AS INTEGER
health = 0
IF health THEN
PRINT "Alive"
ELSE
PRINT "Dead" ' This prints
END IFFLOAT
What it is: A 64-bit double-precision floating point number. Provides approximately 15-17 significant decimal digits.
What it's used for: Physics calculations, smooth movement, rotation angles, scaling factors, or any value requiring decimal precision.
Operations
DIM x AS FLOAT
DIM y AS FLOAT
x = 10.5
y = 3.0
PRINT x + y ' 13.5
PRINT x - y ' 7.5
PRINT x * y ' 31.5
PRINT x / y ' 3.5
PRINT -x ' -10.5Truthiness
0.0is falsy- Any non-zero value (
0.001,-3.14, etc.) is truthy
Precision Note
Floating point math can have small rounding errors:
' Don't compare floats with exact equality
IF x > 3.13 AND x < 3.15 THEN ' Good
IF x = 3.14 THEN ' RiskySTRING
What it is: A sequence of UTF-8 characters. Can be empty ("") or contain any text.
What it's used for: Player names, messages, file paths, dialog text, state names, tags.
Operations
DIM s1 AS STRING
DIM s2 AS STRING
s1 = "Hello"
s2 = "World"
' Concatenation
PRINT s1 + " " + s2 ' "Hello World"
' Repetition (works in either order, like Python)
PRINT "ab" * 3 ' "ababab"
PRINT 3 * "xy" ' "xyxyxy"
PRINT "=" * 20 ' a 20-character divider rule
PRINT "x" * 0 ' "" (zero or negative count yields an empty string)
PRINT "x" * 2.6 ' "xx" (a float count is truncated toward zero)
' Length
PRINT LEN(s1) ' 5
PRINT LEN("") ' 0Common String Functions
' Substrings
PRINT LEFT$("Hello", 2) ' "He"
PRINT RIGHT$("Hello", 2) ' "lo"
PRINT MID$("Hello", 2, 3) ' "ell" (start at 2, take 3)
' Case conversion
PRINT UCASE$("hello") ' "HELLO"
PRINT LCASE$("HELLO") ' "hello"
' Trimming
PRINT TRIM$(" hi ") ' "hi"
' Search
PRINT INSTR("Hello", "ll") ' 3 (1-based position)
' Replace
PRINT REPLACE$("hello", "l", "L") ' "heLLo"Escape Sequences
Use "" to include a quote inside a string:
PRINT "He said ""Hello""" ' He said "Hello"Truthiness
Strings don't have direct truthiness. Use LEN() to check if empty:
DIM name AS STRING
name = ""
IF LEN(name) > 0 THEN
PRINT "Has name"
ELSE
PRINT "No name" ' This prints
END IFStrings as Handles (Dereferencing with `&`)
A string can hold the name of a variable, function, or subroutine, and the & prefix operator looks up the thing with that name and operates on it. The same prefix works in reads, writes, and calls — think of it as "follow the pointer stored in this string."
Calling functions and subs by name
FUNCTION Add(a, b)
Add = a + b
END FUNCTION
FUNCTION Multiply(a, b)
Multiply = a * b
END FUNCTION
op$ = "ADD"
PRINT &op$(3, 4) ' 7 — calls Add
op$ = "MULTIPLY"
PRINT &op$(3, 4) ' 12 — now calls MultiplySubs work the same way — the & prefix is valid anywhere a call target appears:
SUB OnIdle()
PRINT "idle"
END SUB
SUB OnActive()
PRINT "active"
END SUB
handler$ = "ONIDLE"
CALL &handler$()
handler$ = "ONACTIVE"
CALL &handler$()Names are case-insensitive (internally uppercased), and namespaced paths are allowed:
handler$ = "Player.Attack"
CALL &handler$(25) ' calls Player.Attack(25)Reading and writing variables by name
score = 42
ref$ = "SCORE"
PRINT &ref$ ' 42 — read
&ref$ = 100 ' write
PRINT score ' 100You can build variable names at runtime — useful for indexed-by-name patterns:
FOR i = 1 TO 3
ref$ = "VAL" + STR$(i)
&ref$ = i * 10
NEXT i
' Now VAL1=10, VAL2=20, VAL3=30Naming a field or an array element
A handle isn't limited to a plain variable. It can name a field down a chain of
objects, an element of an array, or both at once — and it reads and writes the
same place either way:
TYPE Unit
hp AS INTEGER
END TYPE
TYPE Squad
members(0 TO 2) AS Unit
leader AS Unit
END TYPE
DIM squad AS Squad
DIM grid(4, 4) AS INTEGER
ref$ = "squad.leader.hp" ' a field down a path
&ref$ = 100
ref$ = "squad.members(1).hp" ' an element partway down a path
&ref$ = 50
ref$ = "grid(2, 3)" ' any number of dimensions
&ref$ = 7The subscript may be a literal or the name of a variable holding one, so a
handle can be assembled while the program runs:
DIM enemies(8) AS Unit
DIM i AS INTEGER
FOR i = 0 TO 7
ref$ = "enemies(" + STR$(i) + ").hp"
&ref$ = 100
NEXT i
' Or let the path name the variable and resolve it when used:
i = 3
ref$ = "enemies(i).hp"
PRINT &ref$ ' 100A subscript is resolved when the deref happens, not when the string is
built, so "enemies(i).hp" follows i — read it while i is 3 and you get
element 3.
Common use cases
State dispatcher — swap the active handler by name:
state$ = "IDLE"
SUB OnIdle() ... END SUB
SUB OnWalking() ... END SUB
SUB OnAttack() ... END SUB
' In the game loop:
CALL &("ON" + state$)()Event handlers keyed by string — route by message name:
SUB HandleJump() ... END SUB
SUB HandleFire() ... END SUB
input$ = GetInput()
CALL &("HANDLE" + input$)()Runtime-swappable behavior — no IF ladder needed:
currentAttack$ = "SLASH"
SUB Slash() ... END SUB
SUB Stab() ... END SUB
SUB Bash() ... END SUB
' Later:
currentAttack$ = "BASH"
CALL ¤tAttack$()Where string expressions work without `&`
Several statements accept a string expression as a name by design — you don't need & for these because the statement itself already knows to look the name up:
| Statement | String expression | What it looks up |
|---|---|---|
SET STATE expr |
state name | SUB of that name |
EVENT expr |
event name | fires event listeners by name |
DELAY UNTIL expr |
event name | waits for that event |
STOP TIMER expr / STOP THREAD expr / STOP RENDER expr |
callback id | the running callback with that id |
CALL ... AS expr |
id | names the registered callback |
Use & when you're invoking a function or sub through its name, or reading/writing a variable by name. Use the bare string expression where a statement documents it accepting one.
COORDINATE
What it is: An N-dimensional point with components stored as floats. Supports 2D, 3D, and 4D coordinates.
What it's used for: Positions, velocities, directions, offsets. Essential for sprite movement and physics.
Operations
DIM pos AS COORDINATE
DIM vel AS COORDINATE
pos = (100, 200)
vel = (5, -3)
' Addition (move by velocity)
pos = pos + vel ' (105, 197)
' Subtraction (get difference)
DIM diff AS COORDINATE
diff = (10, 20) - (3, 8) ' (7, 12)
' Scaling (multiply by number)
vel = vel * 2 ' (10, -6)
pos = 0.5 * pos ' (52.5, 98.5)
' Scaling down (divide by number) — the mirror of multiplying
vel = vel / 2 ' (5, -3)
DIM mid AS COORDINATE
mid = ((0, 0) + (4, 6)) / 2 ' (2, 3) — midpoint
' Negation
vel = -vel ' (-10, 6)Multiplying and dividing by a number both work component by component and give
you back a COORDINATE, so v * 2 / 2 is v again. Division divides each
component rather than scaling by the reciprocal, so exact cases stay exact:(3, 6, 6) / 3 is (1, 2, 2), not (0.999…, 2, 2).
Dividing by zero is a runtime error, the same as it is for numbers.
Dividing by a coordinate is a different question, and gives a number. Where
a coordinate meets arithmetic that has no component-wise reading, it stands for
its length (MAG). So dividing one coordinate by another compares their
lengths, and dividing a number by a coordinate divides by one:
p = (3, 4) ' length 5
q = (6, 8) ' length 10
PRINT q / p ' 2 — q is twice as long as p
PRINT 2 / p ' 0.4 — 2 / 5
PRINT q / 2 ' (3, 4) — a COORDINATE, per aboveThis is the same rule that lets IF MAG(vel) > 10 be written as a plain
comparison. If you want lengths explicitly, say so with MAG().
Component Access
Access individual components with .X, .Y, .Z, .W:
pos = (100, 200)
PRINT pos.X ' 100
PRINT pos.Y ' 200
' Works with 3D and 4D coordinates
pos3d = (10, 20, 30)
PRINT pos3d.Z ' 30
pos4d = (1, 2, 3, 4)
PRINT pos4d.W ' 4
' Missing dimensions return 0
pos2d = (10, 20)
PRINT pos2d.Z ' 0 (not defined, defaults to 0)Distance Between Points
Use MAG() with coordinate subtraction to compute distance:
a = (10, 20)
b = (40, 60)
' MAG(a - b) = Euclidean distance
dist = MAG(a - b)
' Works with any dimensionality
p1 = (1, 2, 3)
p2 = (4, 6, 3)
dist3d = MAG(p1 - p2) ' sqrt(9 + 16 + 0) = 5Interpolation
Use LERP() for smooth coordinate interpolation:
start = (0, 0)
dest = (100, 200)
current = LERP(start, dest, 0.5) ' (50, 100)Comparison
Coordinates compare component-wise:
IF pos = (100, 200) THEN
PRINT "At target"
END IFUse Cases
' Sprite movement
DIM playerPos AS COORDINATE
DIM playerVel AS COORDINATE
playerPos = (160, 120)
playerVel = (0, 0)
' In game loop:
playerPos = playerPos + playerVel
' Direction vectors
DIM direction AS COORDINATE
direction = (1, 0) ' Right
direction = (0, -1) ' Up
direction = (-1, 1) ' Down-left diagonal
' Distance check
IF MAG(playerPos - enemyPos) < 32 THEN
PRINT "Too close!"
END IF
' Smooth camera follow
camPos = LERP(camPos, playerPos, 0.1)RANGE
What it is: A rectangle defined by two corner coordinates.
What it's used for: Defining screen regions, clipping areas, texture source rectangles.
Declaration
DIM area AS RANGE
' Two coordinates separated by dash
area = (0, 0)-(100, 100)
' Top-left to bottom-right
area = (10, 20)-(50, 80)Use Cases
Ranges are primarily used in graphics operations that require rectangular regions:
' Line drawing uses range syntax
LINE (0, 0)-(100, 100), 15
' Box drawing
LINE (10, 10)-(50, 50), 12, BFARRAY
What it is: An indexed collection of values of the same type. Always zero-indexed. A reference type (see below).
What it's used for: Lists of enemies, inventory items, high scores, tile maps, any collection of similar data.
Declaration
' DIM arr(n) creates n elements with indices 0 through n-1
DIM scores(5) AS INTEGER ' 5 elements: indices 0, 1, 2, 3, 4
' Empty array (useful with PUSH/QUEUE)
DIM stack(0) AS INTEGER ' 0 elements (empty array)
' 2D array
DIM grid(5, 3) AS INTEGER ' 5x3 = 15 elements
' String array
DIM names(10) AS STRING ' 10 elements: indices 0 through 9
' Range syntax with a ZERO lower bound: DIM arr(0 TO end) creates (end + 1) elements
DIM items(0 TO 4) AS INTEGER ' 5 elements: indices 0, 1, 2, 3, 4Arrays are zero-based — the lower bound must be 0. A non-zero lower
bound such asDIM items(1 TO 5)is a loud runtime error, not a
silent re-base. (Indexing never applied a lower-bound offset, so a
non-zero base would have left the top index out of bounds and index0
a phantom — so it's rejected outright.) UseDIM arr(N)forNelements
indexed0..N-1, orDIM arr(0 TO N)if you want to spell out the
upper bound.
Access
DIM arr(5) AS INTEGER
' Set values
arr(0) = 100
arr(1) = 200
arr(4) = 400 ' Last valid index
' Read values
PRINT arr(0) ' 100
' In loops
FOR i = 0 TO 4 ' 0 to n-1
arr(i) = i * 10
NEXT iBounds
DIM arr(10) AS INTEGER
PRINT LBOUND(arr) ' 0 (always — arrays are zero-based)
PRINT UBOUND(arr) ' 9 (highest valid index = n-1)
' With an explicit zero-lower-bound range
DIM items(0 TO 4) AS INTEGER
PRINT LBOUND(items) ' 0
PRINT UBOUND(items) ' 4LBOUND is always 0; only UBOUND carries information.
Dynamic Operations
DIM items(3) AS STRING
items(0) = "sword"
items(1) = "shield"
items(2) = "potion"
' Remove and return last element
lastItem = POP(items) ' Returns "potion", array now has 2 elements
' Remove and return first element
firstItem = DEQUEUE(items) ' Returns "sword", array now has 1 elementResizing
DIM data(10) AS INTEGER
' ... use it ...
' Resize (preserves existing data)
REDIM data(20)Arrays Are Reference Types
An array is a reference, not a value. Assigning one array to another with= makes a second name for the same array — they share storage. Change one,
you change both:
DIM a(3) AS INTEGER
a(0) = 10
b = a ' b is an ALIAS of a, NOT a copy
b(0) = 99
PRINT a(0) ' 99 — same underlying arrayTo get an independent copy, use CLONE. It's a deep copy — nested arrays
and objects are duplicated all the way down:
b = CLONE(a) ' a real, separate array
b(0) = 99
PRINT a(0) ' 10 — untouchedThis reference behavior also governs how arrays move through parameters:
| Passing | Element writes through the parameter | Reassigning the parameter |
|---|---|---|
| BYREF (default) | Reach the caller's array (shared) | Reaches the caller (BYREF) |
| BYVAL | Do not reach the caller — the callee got a deep copy | Local only |
So SUB Fill(arr) (BYREF) mutates the caller's array, whileSUB Fill(BYVAL arr) works on its own private copy.
Note the SUB: this is a subroutine's story. A FUNCTION parameter is always
BYVAL, so an array handed to one is always a private copy — BYREF is not
allowed there at all. (See the variable-scoping guide for the full
parameter-passing story.)
Sorting an Array
BEGIN SORT sorts a one-dimensional array in place with a comparator block:
BEGIN SORT scores AS a, b
RETURN a - b ' ascending; RETURN b - a for descending
END SORTYou name the two elements being compared and RETURN a three-way sign:
negative if a sorts before b, 0 if equal, positive if after. It's stable,
and it sorts the array itself (no copy returned). Because a 1-D slice is a
view that shares the parent's storage (see below), you can sort one row of a
grid in place: BEGIN SORT grid(0) AS a, b ... END SORT.
Partial Indexing — Slicing Higher-D Arrays
Want a 2D plane out of a 3D array? Pin the outer dimension and use it like a normal 2D array — no copy, no reshape.
When you index a higher-dimensional array with fewer indices than its rank, you get back a shared-storage view of the remaining dimensions:
DIM Rooms(2, 9, 9) AS INTEGER ' shape [3, 10, 10] — 3 rooms, each 10x10
' Pin the outer dim: get back a 2D view that aliases Rooms[1, :, :]
room1 = Rooms(1)
PRINT room1(4, 3) ' reads Rooms(1, 4, 3)
' Writes flow through to the parent — these two are equivalent:
Rooms(0)(3, 2) = 4 ' partial-index write
Rooms(0, 3, 2) = 4 ' full-index write (same cell, same storage)Key rules:
| Indices given | Indices on array | Result |
|---|---|---|
| Fewer than rank | 3D array, 1 index | 2D view (rows × cols) |
| Fewer than rank | 3D array, 2 indices | 1D view (cols) |
| Equal to rank | Any array | The element value |
| Out of bounds | Any array | Halts loudly with the shape |
Views share storage. A view is a window onto the parent. Mutating through the view mutates the parent. Mutating the parent shows through the view. Pointers, not copies.
Views can be assigned to variables. level = Rooms(currentLevel) snapshots the view at one outer index. Later writes to level(...) still land on the parent — the variable holds the view, not a fresh array.
Views can't be resized. PUSH/POP/DEQUEUE on a view is a no-op — the parent's shape is fixed in the outer dimensions you didn't pin. Want a slice you can grow or mutate without touching the parent? CLONE it first (row = CLONE(Rooms(1))) to get an independent array, or operate on the parent directly.
A 1-D slice can be sorted in place. BEGIN SORT Rooms(1) AS a, b ... END SORT sorts that row inside the parent, since the slice shares storage. (The slice must be 1-D — slicing a 3-D array down to a 2-D view and handing that to BEGIN SORT is an error.)
Where this shines:
TILEMAP "lvl" TILES 0 FROM Rooms(level)— feed one 2D slice of a 3D room atlas to the renderer- Iterating outer dim:
FOR i = 0 TO 2 ... process(Rooms(i)) ... NEXT i - Passing one slice of a multi-player state grid to a per-player handler
OBJECT
What it is: A key-value dictionary where keys are strings and values can be any type.
What it's used for: Structured data, configuration, JSON-like data, polygon vertices.
Declaration and Use
Objects support two access syntaxes:
DIM player AS OBJECT
' Dot notation (keys become uppercase internally)
player.name = "Hero"
player.health = 100
PRINT player.NAME ' "Hero"
PRINT player.HEALTH ' 100
' Parentheses notation (keys are uppercase)
PRINT player("NAME") ' "Hero"JSON Serialization
' Convert to JSON string
DIM jsonStr AS STRING
jsonStr = SERIALIZE$(42) ' "42"
jsonStr = SERIALIZE$("hello") ' "\"hello\""
' Parse JSON back to value
DIM data AS OBJECT
data = DESERIALIZE$("{""name"":""test""}")
PRINT data.NAME ' "test"
' Parse JSON array
jsonArr = DESERIALIZE$("[1,2,3]")
PRINT jsonArr(0) ' 1MUTATION
What it is: A list of 2D coordinate points that can be animated with behaviors.
What it's used for: Defining paths for sprite behaviors, animation waypoints, multi-point effects.
Declaration and Use
DIM m AS MUTATION
' Create with literal syntax - list of coordinate pairs
m = MUTATION((100, 200), (300, 400), (500, 600))
' Query number of points
PRINT MUTATIONCOUNT(m) ' 3
' Query point coordinates (0-indexed)
PRINT MUTATIONPOINTX(m, 0) ' 100
PRINT MUTATIONPOINTY(m, 0) ' 200
PRINT MUTATIONPOINTX(m, 1) ' 300
' Modify a point
SET MUTATIONPOINT m, 1, 999, 888
PRINT MUTATIONPOINTX(m, 1) ' 999With Expressions
Points can use expressions:
DIM x AS INTEGER, y AS INTEGER
x = 50
y = 75
DIM path AS MUTATION
path = MUTATION((x, y), (x * 2, y * 2), (x + 100, y + 100))Use with Behaviors
Mutations can be registered with the behavior engine to receive coordinate updates during animations. See the behavior system documentation for details on how mutations integrate with sprite animations.
Type Coercion
Crash BASIC automatically converts types in many situations.
Addition (+)
| Left | Right | Result | Meaning |
|---|---|---|---|
| Integer | Integer | Integer | Numeric addition |
| Integer | Float | Float | Numeric addition (promotes to float) |
| Float | Float | Float | Numeric addition |
| String | Any | String | Concatenation (right converted to string) |
| Any | String | String | Concatenation (left converted to string) |
| Coordinate | Coordinate | Coordinate | Component-wise addition |
| Mutation | Mutation | Mutation | Combines point lists |
Subtraction (-)
| Left | Right | Result | Meaning |
|---|---|---|---|
| Integer | Integer | Integer | Numeric subtraction |
| Integer | Float | Float | Numeric subtraction (promotes to float) |
| Float | Float | Float | Numeric subtraction |
| Coordinate | Coordinate | Coordinate | Component-wise subtraction |
Multiplication (*)
| Left | Right | Result | Meaning |
|---|---|---|---|
| Integer | Integer | Integer | Numeric multiplication |
| Integer | Float | Float | Numeric multiplication (promotes to float) |
| Float | Float | Float | Numeric multiplication |
| String | Integer | String | Repeat string N times |
| Integer | String | String | Repeat string N times |
| String | Float | String | Repeat string N times (truncates float) |
| Coordinate | Number | Coordinate | Scale all components |
| Number | Coordinate | Coordinate | Scale all components |
String repetition clamps the count at zero, so a zero or negative count yields an empty string, and a float count is truncated toward zero ("x" * 2.6 is "xx").
Division (/)
| Left | Right | Result | Meaning |
|---|---|---|---|
| Any | Any | Float | Always returns float (converts both to float) |
Division by zero returns NaN.
Integer Division (\\)
| Left | Right | Result | Meaning |
|---|---|---|---|
| Any | Any | Integer | Both converted to integer, then divided |
Integer division by zero returns 0.
Modulo (MOD)
| Left | Right | Result | Meaning |
|---|---|---|---|
| Integer | Integer | Integer | Integer remainder |
| Any | Any | Float | Float remainder (if either is non-integer) |
Negation (-)
| Operand | Result | Meaning |
|---|---|---|
| Integer | Integer | Negate value |
| Float | Float | Negate value |
| Coordinate | Coordinate | Negate all components |
Comparison (=, <>, <, >, <=, >=)
| Left | Right | Behavior |
|---|---|---|
| Integer | Integer | Direct comparison |
| Float | Float | Direct comparison |
| Integer | Float | Integer promoted to float |
| String | String | Lexicographic (case-sensitive) |
| String | Number | String parsed as number, then compared |
| Coordinate | Coordinate | Component-wise equality only |
All comparisons return INTEGER (1 for true, 0 for false).
Logical (AND, OR, NOT)
| Operation | Behavior |
|---|---|
| A AND B | Bitwise AND of integer values |
| A OR B | Bitwise OR of integer values |
| NOT A | Bitwise NOT of integer value |
Non-integers are converted to integers first.
Quick Reference
| Operation | Result Type | Example |
|---|---|---|
| Integer + Integer | Integer | 5 + 3 → 8 |
| Integer + Float | Float | 5 + 2.5 → 7.5 |
| Float + Float | Float | 1.5 + 2.5 → 4.0 |
| Integer / Integer | Float | 7 / 2 → 3.5 |
| Integer \ Integer | Integer | 7 \ 2 → 3 |
| String * Integer | String | "ab" * 3 → "ababab" |
| String + Integer | String | "x" + 5 → "x5" |
| Coordinate + Coordinate | Coordinate | (1,2) + (3,4) → (4,6) |
| Coordinate * Number | Coordinate | (10,20) * 2 → (20,40) |
Comparison Coercion
Integers and floats compare naturally:
IF 5 = 5.0 THEN PRINT "Equal" ' True
IF 5 < 5.5 THEN PRINT "Less" ' True
IF 6 > 5.5 THEN PRINT "Greater" ' TrueStrings compare lexicographically (case-sensitive):
IF "abc" < "abd" THEN PRINT "Yes" ' True
IF "abc" = "ABC" THEN PRINT "Yes" ' False (case matters)Explicit Conversion
| Function | From | To | Example |
|---|---|---|---|
VAL() |
String | Number | VAL("42") → 42 |
STR$() |
Number | String | STR$(42) → "42" |
INT() |
Float | Integer | INT(3.7) → 3 (floor) |
FIX() |
Float | Integer | FIX(-3.7) → -3 (toward zero) |
CHR$() |
Integer | String | CHR$(65) → "A" |
ASC() |
String | Integer | ASC("A") → 65 |
INT vs FIX
Both convert floats to integers, but differ for negative numbers:
PRINT INT(3.7) ' 3 (floor)
PRINT INT(-3.7) ' -4 (floor - toward negative infinity)
PRINT FIX(3.7) ' 3 (truncate)
PRINT FIX(-3.7) ' -3 (truncate - toward zero)Boolean Logic
Crash BASIC uses integers for boolean values:
TRUE= 1FALSE= 0
Logical Operators
DIM a AS INTEGER
DIM b AS INTEGER
a = 1
b = 0
PRINT a AND b ' 0 (false)
PRINT a OR b ' 1 (true)
PRINT NOT a ' 0 (false)
PRINT NOT b ' -1 (true, bitwise NOT)Comparison Results
Comparisons return 0 (false) or 1 (true):
DIM result AS INTEGER
result = (5 > 3) ' 1
result = (5 < 3) ' 0
' Can use directly in expressions
health = health - (10 * (isHit = TRUE))Summary Table
| Type | Default Value | Truthy When | Converts To |
|---|---|---|---|
| INTEGER | 0 | Non-zero | Float (auto), String (STR$) |
| FLOAT | 0.0 | Non-zero | Integer (INT/FIX), String (STR$) |
| STRING | "" | n/a (use LEN) | Number (VAL) |
| COORDINATE | (0, 0) | n/a | Components via .X, .Y, .Z, .W |
| RANGE | n/a | n/a | n/a |
| ARRAY | Empty | n/a | JSON (SERIALIZE$) |
| OBJECT | Empty | n/a | JSON (SERIALIZE$) |
| MUTATION | Empty | n/a | n/a |