Integers and Floating-Point Numbers

    Julia provides a broad range of primitive numeric types, and a full complement of arithmetic and bitwise operators as well as standard mathematical functions are defined over them. These map directly onto numeric types and operations that are natively supported on modern computers, thus allowing Julia to take full advantage of computational resources. Additionally, Julia provides software support for , which can handle operations on numeric values that cannot be represented effectively in native hardware representations, but at the cost of relatively slower performance.

    The following are Julia’s primitive numeric types:

    • Integer types:
    • Floating-point types:

    Additionally, full support for Complex and Rational Numbers is built on top of these primitive numeric types. All numeric types interoperate naturally without explicit casting, thanks to a flexible, user-extensible .

    Literal integers are represented in the standard manner:

    The default type for an integer literal depends on whether the target system has a 32-bit architecture or a 64-bit architecture:

    1. # 32-bit system:
    2. julia> typeof(1)
    3. Int32
    4. # 64-bit system:
    5. julia> typeof(1)
    6. Int64

    The Julia internal variable Sys.WORD_SIZE indicates whether the target system is 32-bit or 64-bit:

    1. # 32-bit system:
    2. julia> Sys.WORD_SIZE
    3. 32
    4. # 64-bit system:
    5. julia> Sys.WORD_SIZE
    6. 64

    Julia also defines the types Int and UInt, which are aliases for the system’s signed and unsigned native integer types respectively:

    1. # 32-bit system:
    2. julia> Int
    3. Int32
    4. julia> UInt
    5. UInt32
    6. # 64-bit system:
    7. julia> Int
    8. Int64
    9. julia> UInt
    10. UInt64

    Larger integer literals that cannot be represented using only 32 bits but can be represented in 64 bits always create 64-bit integers, regardless of the system type:

    1. # 32-bit or 64-bit system:
    2. julia> typeof(3000000000)
    3. Int64

    Unsigned integers are input and output using the 0x prefix and hexadecimal (base 16) digits 0-9a-f (the capitalized digits A-F also work for input). The size of the unsigned value is determined by the number of hex digits used:

    1. julia> x = 0x1
    2. 0x01
    3. julia> typeof(x)
    4. UInt8
    5. julia> x = 0x123
    6. 0x0123
    7. julia> typeof(x)
    8. UInt16
    9. julia> x = 0x1234567
    10. 0x01234567
    11. julia> typeof(x)
    12. UInt32
    13. julia> x = 0x123456789abcdef
    14. 0x0123456789abcdef
    15. julia> typeof(x)
    16. UInt64
    17. julia> x = 0x11112222333344445555666677778888
    18. 0x11112222333344445555666677778888
    19. julia> typeof(x)
    20. UInt128

    This behavior is based on the observation that when one uses unsigned hex literals for integer values, one typically is using them to represent a fixed numeric byte sequence, rather than just an integer value.

    Binary and octal literals are also supported:

    1. julia> x = 0b10
    2. 0x02
    3. julia> typeof(x)
    4. UInt8
    5. julia> x = 0o010
    6. 0x08
    7. julia> typeof(x)
    8. UInt8
    9. julia> x = 0x00000000000000001111222233334444
    10. 0x00000000000000001111222233334444
    11. julia> typeof(x)
    12. UInt128

    As for hexadecimal literals, binary and octal literals produce unsigned integer types. The size of the binary data item is the minimal needed size, if the leading digit of the literal is not 0. In the case of leading zeros, the size is determined by the minimal needed size for a literal, which has the same length but leading digit 1. That allows the user to control the size. Values which cannot be stored in UInt128 cannot be written as such literals.

    Binary, octal, and hexadecimal literals may be signed by a - immediately preceding the unsigned literal. They produce an unsigned integer of the same size as the unsigned literal would do, with the two’s complement of the value:

    1. julia> -0x2
    2. 0xfe
    3. julia> -0x0002
    4. 0xfffe

    The minimum and maximum representable values of primitive numeric types such as integers are given by the and typemax functions:

    1. julia> (typemin(Int32), typemax(Int32))
    2. (-2147483648, 2147483647)
    3. julia> for T in [Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128]
    4. println("$(lpad(T,7)): [$(typemin(T)),$(typemax(T))]")
    5. end
    6. Int8: [-128,127]
    7. Int16: [-32768,32767]
    8. Int32: [-2147483648,2147483647]
    9. Int64: [-9223372036854775808,9223372036854775807]
    10. Int128: [-170141183460469231731687303715884105728,170141183460469231731687303715884105727]
    11. UInt8: [0,255]
    12. UInt16: [0,65535]
    13. UInt32: [0,4294967295]
    14. UInt64: [0,18446744073709551615]
    15. UInt128: [0,340282366920938463463374607431768211455]

    The values returned by and typemax are always of the given argument type. (The above expression uses several features that have yet to be introduced, including , Strings, and , but should be easy enough to understand for users with some existing programming experience.)

    In Julia, exceeding the maximum representable value of a given type results in a wraparound behavior:

    1. julia> x = typemax(Int64)
    2. 9223372036854775807
    3. julia> x + 1
    4. -9223372036854775808
    5. julia> x + 1 == typemin(Int64)
    6. true

    Thus, arithmetic with Julia integers is actually a form of modular arithmetic. This reflects the characteristics of the underlying arithmetic of integers as implemented on modern computers. In applications where overflow is possible, explicit checking for wraparound produced by overflow is essential; otherwise, the type in Arbitrary Precision Arithmetic is recommended instead.

    An example of overflow behavior and how to potentially resolve it is as follows:

    Division errors

    Integer division (the div function) has two exceptional cases: dividing by zero, and dividing the lowest negative number () by -1. Both of these cases throw a DivideError. The remainder and modulus functions (rem and mod) throw a when their second argument is zero.

    1. julia> 1.0
    2. 1.0
    3. julia> 1.
    4. 1.0
    5. julia> 0.5
    6. 0.5
    7. julia> .5
    8. 0.5
    9. julia> -1.23
    10. -1.23
    11. julia> 1e10
    12. 1.0e10
    13. julia> 2.5e-4

    The above results are all Float64 values. Literal values can be entered by writing an f in place of e:

    1. julia> x = 0.5f0
    2. 0.5f0
    3. julia> typeof(x)
    4. Float32
    5. julia> 2.5f-4
    6. 0.00025f0

    Values can be converted to Float32 easily:

    1. julia> x = Float32(-1.5)
    2. -1.5f0
    3. julia> typeof(x)
    4. Float32

    Hexadecimal floating-point literals are also valid, but only as values, with p preceding the base-2 exponent:

    1. julia> 0x1p0
    2. 1.0
    3. julia> 0x1.8p3
    4. 12.0
    5. julia> x = 0x.4p-1
    6. 0.125
    7. julia> typeof(x)
    8. Float64

    Half-precision floating-point numbers are also supported (Float16), but they are implemented in software and use for calculations.

    1. julia> sizeof(Float16(4.))
    2. 2
    3. julia> 2*Float16(4.)
    4. Float16(8.0)

    The underscore _ can be used as digit separator:

    1. julia> 10_000, 0.000_000_005, 0xdead_beef, 0b1011_0010
    2. (10000, 5.0e-9, 0xdeadbeef, 0xb2)

    Floating-point numbers have two zeros, positive zero and negative zero. They are equal to each other but have different binary representations, as can be seen using the function:

    1. julia> 0.0 == -0.0
    2. true
    3. julia> bitstring(0.0)
    4. "0000000000000000000000000000000000000000000000000000000000000000"
    5. julia> bitstring(-0.0)
    6. "1000000000000000000000000000000000000000000000000000000000000000"

    There are three specified standard floating-point values that do not correspond to any point on the real number line:

    For further discussion of how these non-finite floating-point values are ordered with respect to each other and other floats, see Numeric Comparisons. By the , these floating-point values are the results of certain arithmetic operations:

    1. julia> 1/Inf
    2. 0.0
    3. julia> 1/0
    4. Inf
    5. julia> -5/0
    6. -Inf
    7. julia> 0.000001/0
    8. Inf
    9. julia> 0/0
    10. NaN
    11. julia> 500 + Inf
    12. Inf
    13. julia> 500 - Inf
    14. -Inf
    15. julia> Inf + Inf
    16. Inf
    17. julia> Inf - Inf
    18. NaN
    19. julia> Inf * Inf
    20. Inf
    21. julia> Inf / Inf
    22. NaN
    23. julia> 0 * Inf
    24. NaN

    The typemin and functions also apply to floating-point types:

    1. julia> (typemin(Float16),typemax(Float16))
    2. (-Inf16, Inf16)
    3. julia> (typemin(Float32),typemax(Float32))
    4. (-Inf32, Inf32)
    5. julia> (typemin(Float64),typemax(Float64))
    6. (-Inf, Inf)

    Most real numbers cannot be represented exactly with floating-point numbers, and so for many purposes it is important to know the distance between two adjacent representable floating-point numbers, which is often known as machine epsilon.

    Julia provides , which gives the distance between 1.0 and the next larger representable floating-point value:

    These values are 2.0^-23 and 2.0^-52 as Float32 and values, respectively. The eps function can also take a floating-point value as an argument, and gives the absolute difference between that value and the next representable floating point value. That is, eps(x) yields a value of the same type as x such that x + eps(x) is the next representable floating-point value larger than x:

    1. julia> eps(1.0)
    2. 2.220446049250313e-16
    3. julia> eps(1000.)
    4. 1.1368683772161603e-13
    5. julia> eps(1e-27)
    6. 1.793662034335766e-43
    7. julia> eps(0.0)
    8. 5.0e-324

    The distance between two adjacent representable floating-point numbers is not constant, but is smaller for smaller values and larger for larger values. In other words, the representable floating-point numbers are densest in the real number line near zero, and grow sparser exponentially as one moves farther away from zero. By definition, eps(1.0) is the same as eps(Float64) since 1.0 is a 64-bit floating-point value.

    Julia also provides the and prevfloat functions which return the next largest or smallest representable floating-point number to the argument respectively:

    1. 1.25f0
    2. julia> nextfloat(x)
    3. 1.2500001f0
    4. julia> prevfloat(x)
    5. 1.2499999f0
    6. julia> bitstring(prevfloat(x))
    7. "00111111100111111111111111111111"
    8. julia> bitstring(x)
    9. "00111111101000000000000000000000"
    10. julia> bitstring(nextfloat(x))
    11. "00111111101000000000000000000001"

    This example highlights the general principle that the adjacent representable floating-point numbers also have adjacent binary integer representations.

    Rounding modes

    If a number doesn’t have an exact floating-point representation, it must be rounded to an appropriate representable value. However, the manner in which this rounding is done can be changed if required according to the rounding modes presented in the .

    The default mode used is always RoundNearest, which rounds to the nearest representable value, with ties rounded towards the nearest value with an even least significant bit.

    Floating-point arithmetic entails many subtleties which can be surprising to users who are unfamiliar with the low-level implementation details. However, these subtleties are described in detail in most books on scientific computation, and also in the following references:

    • The definitive guide to floating point arithmetic is the ; however, it is not available for free online.
    • For a brief but lucid presentation of how floating-point numbers are represented, see John D. Cook’s article on the subject as well as his to some of the issues arising from how this representation differs in behavior from the idealized abstraction of real numbers.
    • Also recommended is Bruce Dawson’s series of blog posts on floating-point numbers.
    • For an excellent, in-depth discussion of floating-point numbers and issues of numerical accuracy encountered when computing with them, see David Goldberg’s paper .
    • For even more extensive documentation of the history of, rationale for, and issues with floating-point numbers, as well as discussion of many other topics in numerical computing, see the collected writings of , commonly known as the “Father of Floating-Point”. Of particular interest may be An Interview with the Old Man of Floating-Point.

    To allow computations with arbitrary-precision integers and floating point numbers, Julia wraps the and the GNU MPFR Library, respectively. The and BigFloat types are available in Julia for arbitrary precision integer and floating point numbers respectively.

    Once created, they participate in arithmetic with all other numeric types thanks to Julia’s :

    1. julia> BigInt(typemax(Int64)) + 1
    2. 9223372036854775808
    3. julia> big"123456789012345678901234567890" + 1
    4. 123456789012345678901234567891
    5. julia> parse(BigInt, "123456789012345678901234567890") + 1
    6. 123456789012345678901234567891
    7. julia> string(big"2"^200, base=16)
    8. "100000000000000000000000000000000000000000000000000"
    9. julia> 0x100000000000000000000000000000000-1 == typemax(UInt128)
    10. true
    11. julia> 0x000000000000000000000000000000000
    12. 0
    13. julia> typeof(ans)
    14. BigInt
    15. julia> big"1.23456789012345678901"
    16. 1.234567890123456789010000000000000000000000000000000000000000000000000000000004
    17. julia> parse(BigFloat, "1.23456789012345678901")
    18. 1.234567890123456789010000000000000000000000000000000000000000000000000000000004
    19. julia> BigFloat(2.0^66) / 3
    20. 2.459565876494606882133333333333333333333333333333333333333333333333333333333344e+19
    21. julia> factorial(BigInt(40))
    22. 815915283247897734345611269596115894272000000000

    However, type promotion between the primitive types above and BigInt/ is not automatic and must be explicitly stated.

    1. julia> x = typemin(Int64)
    2. -9223372036854775808
    3. julia> x = x - 1
    4. 9223372036854775807
    5. julia> typeof(x)
    6. Int64
    7. julia> y = BigInt(typemin(Int64))
    8. -9223372036854775808
    9. julia> y = y - 1
    10. -9223372036854775809
    11. julia> typeof(y)
    12. BigInt

    The default precision (in number of bits of the significand) and rounding mode of BigFloat operations can be changed globally by calling and setrounding, and all further calculations will take these changes in account. Alternatively, the precision or the rounding can be changed only within the execution of a particular block of code by using the same functions with a do block:

    1. julia> setrounding(BigFloat, RoundUp) do
    2. BigFloat(1) + parse(BigFloat, "0.1")
    3. end
    4. 1.100000000000000000000000000000000000000000000000000000000000000000000000000003
    5. julia> setrounding(BigFloat, RoundDown) do
    6. BigFloat(1) + parse(BigFloat, "0.1")
    7. end
    8. 1.099999999999999999999999999999999999999999999999999999999999999999999999999986
    9. julia> setprecision(40) do
    10. BigFloat(1) + parse(BigFloat, "0.1")
    11. end
    12. 1.1000000000004

    To make common numeric formulae and expressions clearer, Julia allows variables to be immediately preceded by a numeric literal, implying multiplication. This makes writing polynomial expressions much cleaner:

    1. julia> x = 3
    2. 3
    3. julia> 2x^2 - 3x + 1
    4. 10
    5. julia> 1.5x^2 - .5x + 1
    6. 13.0

    It also makes writing exponential functions more elegant:

    1. julia> 2^2x
    2. 64

    The precedence of numeric literal coefficients is slightly lower than that of unary operators such as negation. So -2x is parsed as (-2) * x and √2x is parsed as (√2) * x. However, numeric literal coefficients parse similarly to unary operators when combined with exponentiation. For example 2^3x is parsed as 2^(3x), and 2x^3 is parsed as 2*(x^3).

    Numeric literals also work as coefficients to parenthesized expressions:

    1. julia> 2(x-1)^2 - 3(x-1) + 1
    2. 3

    Note

    The precedence of numeric literal coefficients used for implicit multiplication is higher than other binary operators such as multiplication (*), and division (/, \, and //). This means, for example, that 1 / 2im equals -0.5im and 6 // 2(2 + 1) equals 1 // 1.

    Additionally, parenthesized expressions can be used as coefficients to variables, implying multiplication of the expression by the variable:

    1. julia> (x-1)x
    2. 6

    Neither juxtaposition of two parenthesized expressions, nor placing a variable before a parenthesized expression, however, can be used to imply multiplication:

    Both expressions are interpreted as function application: any expression that is not a numeric literal, when immediately followed by a parenthetical, is interpreted as a function applied to the values in parentheses (see for more about functions). Thus, in both of these cases, an error occurs since the left-hand value is not a function.

    The above syntactic enhancements significantly reduce the visual noise incurred when writing common mathematical formulae. Note that no whitespace may come between a numeric literal coefficient and the identifier or parenthesized expression which it multiplies.

    Juxtaposed literal coefficient syntax may conflict with some numeric literal syntaxes: hexadecimal, octal and binary integer literals and engineering notation for floating-point literals. Here are some situations where syntactic conflicts arise:

    • The hexadecimal integer literal expression 0xff could be interpreted as the numeric literal 0 multiplied by the variable xff. Similar ambiguities arise with octal and binary literals like 0o777 or 0b01001010.
    • The floating-point literal expression 1e10 could be interpreted as the numeric literal 1 multiplied by the variable e10, and similarly with the equivalent E form.
    • The 32-bit floating-point literal expression 1.5f22 could be interpreted as the numeric literal 1.5 multiplied by the variable f22.

    In all cases the ambiguity is resolved in favor of interpretation as numeric literals:

    • Expressions starting with 0x/0o/0b are always hexadecimal/octal/binary literals.
    • Expressions starting with a numeric literal followed by e or E are always floating-point literals.
    • Expressions starting with a numeric literal followed by f are always 32-bit floating-point literals.

    Unlike E, which is equivalent to e in numeric literals for historical reasons, F is just another letter and does not behave like f in numeric literals. Hence, expressions starting with a numeric literal followed by F are interpreted as the numerical literal multiplied by a variable, which means that, for example, 1.5F22 is equal to 1.5 * F22.

    Julia provides functions which return literal 0 and 1 corresponding to a specified type or the type of a given variable.

    These functions are useful in Numeric Comparisons to avoid overhead from unnecessary .

    Examples:

    1. julia> zero(Float32)
    2. 0.0f0
    3. julia> zero(1.0)
    4. 0.0
    5. julia> one(Int32)
    6. 1
    7. julia> one(BigFloat)
    8. 1.0