Julia Data Types
There is also a term called "literal", which is used to express a fixed value in source code notation. Integers, floating-point numbers, and strings are all examples of literals.
For instance:
Julia provides a rich set of primitive numeric types and a comprehensive set of arithmetic operations defined on them. It also offers bitwise operators and some standard mathematical functions.
Integer Types
The table below lists the integer types supported by Julia:
Type | Signed? | Bits | Minimum Value | Maximum Value |
---|---|---|---|---|
Int8 | ✓ | 8 | -2^7 | 2^7 – 1 |
UInt8 | 8 | 0 | 2^8 – 1 | |
Int16 | ✓ | 16 | -2^15 | 2^15 – 1 |
UInt16 | 16 | 0 | 2^16 – 1 | |
Int32 | ✓ | 32 | -2^31 | 2^31 – 1 |
UInt32 | 32 | 0 | 2^32 – 1 | |
Int64 | ✓ | 64 | -2^63 | 2^63 – 1 |
UInt64 | 64 | 0 | 2^64 – 1 | |
Int128 | ✓ | 128 | -2^127 | 2^127 – 1 |
UInt128 | 128 | 0 | 2^128 – 1 | |
Bool | N/A | 8 | false (0) | true (1) |
Integer literal forms:
Example
julia> 1
1
julia> 1234
1234
The default type of an integer literal depends on whether the target system is a 32-bit or 64-bit architecture (most systems are now 64-bit):
Example
# 32-bit system:
julia> typeof(1)
Int32
# 64-bit system:
julia> typeof(1)
Int64
Julia's built-in variable Sys.WORD_SIZE
indicates whether the target system is a 32-bit or 64-bit architecture:
Example
# 32-bit system:
julia> Sys.WORD_SIZE
32
# 64-bit system:
julia> Sys.WORD_SIZE
64
Julia also defines the types Int
and UInt
, which are aliases for the system's native signed and unsigned integer types, respectively.
Example
# 32-bit system:
julia> Int
Int32
julia> UInt
UInt32
# 64-bit system:
julia> Int
Int64
julia> UInt
UInt64
Overflow Behavior
In Julia, exceeding the maximum value that a type can represent results in wraparound behavior:
Example
julia> x = typemax(Int64)
9223372036854775807
julia> x + 1
-9223372036854775808
julia> x + 1 == typemin(Int64)
true
Thus, Julia's integer arithmetic is essentially a form of modular arithmetic, reflecting the characteristics of modern computer implementations of low-level arithmetic. In programs where overflow may occur, it is necessary to explicitly check for loops at the boundary of the maximum values. Otherwise, it is recommended to use the BigInt type from arbitrary precision arithmetic as an alternative.
Here is an example of overflow behavior and how to resolve it:
Example
julia> 10^19
-8446744073709551616
julia> big(10)^19
10000000000000000000
Division Errors
- Division by zero
- Division by the smallest negative number
The rem
remainder function and mod
modulo function throw a DivideError when dividing by zero, as shown in the following example:
Example
julia> mod(1, 0)
ERROR: DivideError: integer division error
Stacktrace:
[1] div at .\int.jl:260 [inlined]
[2] div at .\div.jl:217 [inlined]
[3] div at .\div.jl:262 [inlined]
[4] fld at .\div.jl:228 [inlined]
[5] mod(::Int64, ::Int64) at .\int.jl:252
[6] top-level scope at REPL[52]:1
julia> rem(1, 0)
ERROR: DivideError: integer division error
Stacktrace:
[1] rem(::Int64, ::Int64) at .\int.jl:261
[2] top-level scope at REPL[54]:1
Floating-Point Types
The table below lists the floating-point types supported by Julia:
Type | Precision | Bits |
---|---|---|
Float16 | Half | 16 |
Float32 | Single | 32 |
Float64 | Double | 64 |
Additionally, full support for complex and rational numbers is built on top of these primitive data types.
Floating-point literals are formatted as follows, with the option to use E
when necessary.
Example
julia> 1.0
1.0
julia> 1.
1.0
julia> 0.5
0.5
julia> .5
0.5
julia> -1.23
-1.23
julia> 1e10
1.0e10
julia> 2.5e-4
0.00025
Note: The format with E
represents exponentiation. For example, 1.03 times 10 to the power of 8 can be abbreviated as "1.03E+08", where "E" stands for exponent.
The results above are all of type Float64. Using f
instead of e
yields literals of type Float32:
Example
julia> x = 0.5f0
0.5f0
julia> typeof(x)
Float32
julia> 2.5f-4
0.00025f0
Values can be easily converted to Float32:
julia> x = Float32(-1.5)
-1.5f0
julia> typeof(x)
Float32
Hexadecimal floating-point literals also exist but are only applicable to Float64 values. They are generally represented with a p
prefix and an exponent base of 2:
Example
julia> 0x1p0
1.0
julia> 0x1.8p3
12.0
julia> x = 0x.4p-1
0.125
julia> typeof(x)
Float64
Julia also supports half-precision floating-point numbers (Float16), but they are implemented in software using Float32.
julia> sizeof(Float16(4.))
2
julia> 2*Float16(4.)
Float16(8.0)
The underscore _
can be used as a numeric separator:
Example
julia> 10_000, 0.000_000_005, 0xdead_beef, 0b1011_0010
(10000, 5.0e-9, 0xdeadbeef, 0xb2)
Zeros in Floating-Point Numbers
There are two types of zeros in floating-point numbers, positive zero and negative zero. They are equal to each other but have different binary representations, which can be viewed using the bitstring function:
Example
julia> 0.0 == -0.0
true
julia> bitstring(0.0)
### Special Floating-Point Values
There are three specific standard floating-point values that do not correspond to any point on the real number line:
| Float16 | Float32 | Float64 | Name | Description |
| --- | --- | --- | --- | --- |
| Inf16 | Inf32 | Inf | Positive Infinity | A number greater than all finite floating-point numbers |
| -Inf16 | -Inf32 | -Inf | Negative Infinity | A number less than all finite floating-point numbers |
| NaN16 | NaN32 | NaN | Not a Number | A value that is not equal (==) to any floating-point value, including itself |
Here are some examples of floating-point arithmetic:
## Example
```julia
julia> 1/Inf
0.0
julia> 1/0
Inf
julia> -5/0
-Inf
julia> 0.000001/0
Inf
julia> 0/0
NaN
julia> 500 + Inf
Inf
julia> 500 - Inf
-Inf
julia> Inf + Inf
Inf
julia> Inf - Inf
NaN
julia> Inf * Inf
Inf
julia> Inf / Inf
NaN
julia> 0 * Inf
NaN
julia> NaN == NaN
false
julia> NaN != NaN
true
julia> NaN < NaN
false
julia> NaN > NaN
false
We can also use the typemin
and typemax
functions:
Example
julia> (typemin(Float16), typemax(Float16))
(-Inf16, Inf16)
julia> (typemin(Float32), typemax(Float32))
(-Inf32, Inf32)
julia> (typemin(Float64), typemax(Float64))
(-Inf, Inf)
Machine Precision
Most real numbers cannot be represented exactly by floating-point numbers, so it is necessary to know the distance between two adjacent representable floating-point numbers, which is commonly called machine epsilon.
Julia provides the eps
function, which gives the difference between 1.0 and the next floating-point number that Julia can represent:
Example
julia> eps(Float32)
1.1920929f-7
julia> eps(Float64)
2.220446049250313e-16
julia> eps() # same as eps(Float64)
2.220446049250313e-16
These values are 2.0^-23 for Float32 and 2.0^-52 for Float64. The eps
function can also take a floating-point value as an argument and return the absolute difference between that value and the next representable floating-point number. That is, eps(x)
produces a value of the same type as x
, and x + eps(x)
is the next representable floating-point number larger than x
:
Example
julia> eps(1.0)
2.220446049250313e-16
julia> eps(1000.0)
1.1368683772161603e-13
julia> eps(1e-27)
1.793662034335766e-43
julia> eps(0.0)
5.0e-324
The distance between two adjacent representable floating-point numbers is not constant; the smaller the value, the smaller the gap, and the larger the value, the larger the gap. In other words, representable floating-point numbers are densest around zero on the real number line and become increasingly sparse as they move away from zero, growing exponentially. By definition, eps(1.0)
is equal to eps(Float64)
because 1.0 is a 64-bit floating-point value.
Julia also provides the nextfloat
and prevfloat
functions, which return the next larger or smaller representable floating-point number based on the argument:
Example
julia> x = 1.25f0
1.25f0
julia> nextfloat(x)
1.2500001f0
julia> prevfloat(x)
1.2499999f0
julia> bitstring(prevfloat(x))
"00111111100111111111111111111111"
julia> bitstring(x)
"00111111101000000000000000000000"
julia> bitstring(nextfloat(x))
"00111111101000000000000000000001"
This example demonstrates a general principle, which is that adjacent representable floating-point numbers also have adjacent binary integer representations.
---
## Rounding Modes
If a number does not have an exact floating-point representation, it must be rounded to an appropriate representable value.
The default mode used by Julia is always `RoundNearest`, which means rounding to the nearest representable value, with preference for the value that uses the least significant digits.
## Example
julia> BigFloat("1.510564889",2,RoundNearest) 1.5
julia> BigFloat("1.550564889",2,RoundNearest) 1.5
julia> BigFloat("1.560564889",2,RoundNearest) 1.5
---
## Literals for 0 and 1
Julia provides literal functions for 0 and 1 that can return a specific type or the type of a given variable.
| Function | Description |
| --- | --- |
| zero(x) | Zero literal of the type of x or variable x |
| one(x) | One literal of the type of x or variable x |
These functions can be used in numerical comparisons to avoid the overhead of unnecessary type conversions.
## Example
julia> zero(Float32) 0.0f0
julia> zero(1.0) 0.0
julia> one(Int32) 1
julia> one(BigFloat) 1.0
---
## Type Conversion
Type conversion is the process of converting a variable from one type to another. For example, if you want to store a float value into a simple integer type, you need to cast the float type to int type. You can use the type cast operator to explicitly convert a value from one type to another, as shown below:
**First Method:**
T(x) or convert(T,x)
Both will convert x to type T.
- If T is a floating-point type, the result of the conversion will be the nearest representable value, which could be positive or negative infinity.
- If T is an integer type, an InexactError will be thrown if x cannot be represented by type T.
**Second Method:**
`x % T` can also convert integer x to integer type T, with the result being consistent with x modulo 2^n, where n is the number of bits in T.
**Third Method:**
Rounding functions accept an optional parameter of type T. For example, `round(Int,x)` is a shorthand for `Int(round(x))`.
## Example
julia> Int8(127) 127
julia> Int8(128) ERROR: InexactError: trunc(Int8, 128) Stacktrace: [...]
julia> Int8(127.0) 127
julia> Int8(3.14) ERROR: InexactError: Int8(3.14) Stacktrace: [...]
julia> Int8(128.0) ERROR: InexactError: Int8(128.0) Stacktrace: [...]
julia> 127 % Int8 127
julia> 128 % Int8 -128
julia> round(Int8,127.4) 127
julia> round(Int8,127.6) ERROR: InexactError: trunc(Int8, 128.0) Stacktrace: [...] ```