Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

OCaml  Module 2 – Expressions, values and functions

Integers, floats and arithmetic

Keep int and float apart with their own operators, predict 63-bit overflow, division and mod on negatives, nan and rounding, and parse numbers safely.

  • Beginner
  • 5 minutes
  • Examples run with ocaml 5.5.1, opam 2.6.0, dune 3.24.2 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 5 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Use int and float arithmetic with their separate operators, such as + and +.
  • Predict 63-bit integer overflow, truncating division and mod on negative numbers
  • Convert between strings, ints and floats safely with the _opt functions
  • Choose Int64, Int32 or an arbitrary-precision library when 63 bits are not enough

Before you start

What you’ll learn · Before you start

What you will learn

  • Use int and float arithmetic with their separate operators, such as + and +.
  • Predict 63-bit integer overflow, truncating division and mod on negative numbers
  • Convert between strings, ints and floats safely with the _opt functions
  • Choose Int64, Int32 or an arbitrary-precision library when 63 bits are not enough

Before you start

On this page

max_int + 1 in OCaml 5.5.1 gives -4611686018427387904 without any error, and -7 mod 2 gives -1, not 1. The same program prints a max_int of 2147483647 once it is compiled to JavaScript.

You will keep integers and floats apart with their own operators, predict overflow, division and mod on negative numbers, read infinity, nan and rounding correctly, parse numbers from text without exceptions, and choose a bigger integer type when 63 bits are not enough.

int and float are separate types with separate operators

OCaml never converts a number from one type to another by itself. Integers use +, -, *, / and mod; floats use their own operators with a dot, +., -., *. and /., and a float literal always has a dot or an exponent, 2.0 or 2.. Mixing them is a type error, so a conversion has to be written: float_of_int (also Float.of_int) from int to float, and truncate, int_of_float or Float.round the other way.

Text needs a conversion as well. int_of_string raises the exception Failure "int_of_string" on bad input, which stops a program unless it is caught, while int_of_string_opt returns None instead. Both read 0x, 0o and 0b prefixes and _ separators:

Mixing int and float, and parsing numbers from text Python · conversions_session.py
from session import toplevel

# int and float never mix without a conversion. Converting text to numbers: int_of_string raises on bad input, the _opt versions return None instead and accept
# 0x, 0o and 0b prefixes and _ separators. OCaml 5.5 adds bit counting such as Int.popcount.
toplevel("""
3 + 2.0;;
3 +. 2.0;;
float_of_int 3 +. 2.0;;
int_of_string "42";;
int_of_string "12a";;
int_of_string_opt "12a";;
int_of_string_opt "0x1F";;
int_of_string_opt "1_000_000";;
float_of_string_opt "2.5e3";;
Int.popcount 255;;
""")

Output

$ ocaml
OCaml version 5.5.1
Enter #help;; for help.

# 3 + 2.0;;
Line 1, characters 4-7:
1 | 3 + 2.0;;
        ^^^
Error: The constant 2.0 has type float but an expression was expected of type
         int
# 3 +. 2.0;;
Line 1, characters 0-1:
1 | 3 +. 2.0;;
    ^
Error: The constant 3 has type int but an expression was expected of type
         float
Hint: Did you mean 3.?
# float_of_int 3 +. 2.0;;
- : float = 5.
# int_of_string "42";;
- : int = 42
# int_of_string "12a";;
Exception: Failure "int_of_string".
# int_of_string_opt "12a";;
- : int option = None
# int_of_string_opt "0x1F";;
- : int option = Some 31
# int_of_string_opt "1_000_000";;
- : int option = Some 1000000
# float_of_string_opt "2.5e3";;
- : float option = Some 2500.
# Int.popcount 255;;
- : int = 8

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 conversions_session.py

3 + 2.0 failed because 2.0 is a float where + wants an int, and 3 +. 2.0 failed the other way, with the hint “Did you mean 3.?”. int_of_string_opt "0x1F" gave Some 31 and "1_000_000" gave Some 1000000. The last line uses a function new in OCaml 5.5: Int.popcount 255 counts the bits that are set, 8.

Parsing input with the raising version

Wrong: let age = int_of_string line on input a user typed, which stops the program on 12a. Right: match int_of_string_opt line with Some age -> … | None -> …, so bad input is a case the code handles.

A native int has 63 bits and wraps without warning

The manual’s chapter on values promises only the range −2³⁰ to 2³⁰ − 1, and adds that on 64-bit platforms the current implementation supports −2⁶² to 2⁶² − 1: one bit of each machine word goes to the runtime, which uses it to tell numbers from pointers. So max_int is 2⁶² − 1, and arithmetic past it integer overflow: A result beyond max_int or min_int. OCaml's int arithmetic wraps around silently, so max_int + 1 gives min_int and no error is raised.:

max_int, overflow and Int64 Python · int_limits_session.py
from session import toplevel

# int has 63 bits on a 64-bit machine, so max_int is 2^62 - 1, and one more wraps round to min_int without any
# error. Int64 has the full 64 bits.
toplevel("""
max_int;;
min_int;;
max_int + 1;;
max_int + 1 = min_int;;
Sys.int_size;;
Int64.max_int;;
Int64.add Int64.max_int 1L;;
""")

Output

$ ocaml
OCaml version 5.5.1
Enter #help;; for help.

# max_int;;
- : int = 4611686018427387903
# min_int;;
- : int = -4611686018427387904
# max_int + 1;;
- : int = -4611686018427387904
# max_int + 1 = min_int;;
- : bool = true
# Sys.int_size;;
- : int = 63
# Int64.max_int;;
- : int64 = 9223372036854775807L
# Int64.add Int64.max_int 1L;;
- : int64 = -9223372036854775808L

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 int_limits_session.py

max_int + 1 gave exactly min_int, and Sys.int_size reported 63. Int64 has all 64 bits, up to 9223372036854775807, but it wraps the same way, as Int64.add Int64.max_int 1L showed. The size of int also depends on where the program runs:

One program, three targets, three int sizes Python · targets_session.py
from session import files, run

# One program, three targets: native code, JavaScript through js_of_ocaml and WebAssembly through wasm_of_ocaml,
# each run and printing its own int size.
files("ints.ml")
run("""
ocamlfind ocamlopt -package unix ints.ml -o ints_native && ./ints_native
ocamlfind ocamlc -package js_of_ocaml -linkpkg ints.ml -o ints.byte
js_of_ocaml ints.byte -o ints.js && node ints.js
wasm_of_ocaml ints.byte -o ints_wasm.js && node ints_wasm.js
""")

Output

$ ocamlfind ocamlopt -package unix ints.ml -o ints_native && ./ints_native
int_size 63, max_int 4611686018427387903
$ ocamlfind ocamlc -package js_of_ocaml -linkpkg ints.ml -o ints.byte
$ js_of_ocaml ints.byte -o ints.js && node ints.js
int_size 32, max_int 2147483647
$ wasm_of_ocaml ints.byte -o ints_wasm.js && node ints_wasm.js
int_size 31, max_int 1073741823

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 targets_session.py

Native code printed 63 bits, the JavaScript build from js_of_ocaml 32 bits and the WebAssembly build from wasm_of_ocaml 31 bits, with a max_int of 1073741823. A program meant for the browser must not assume 63 bits. Answer three questions about your own numbers and see which type fits:

Interactive · answer and compare Which integer type fits your numbers? Answer three questions about the numbers your program handles; the types that cannot hold them are marked with the reason.
Can the values grow past about 4.6 × 10^18, the native int's limit?
Must the size match a file format, a network protocol or a C library exactly?
Will the program also be compiled to JavaScript or WebAssembly?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Four integer types against four things a program may need from its numbers
Type Holds very large values Same size on every target Fast and allocation-free Plain operators
int Size: 63 bits native, 32 in JavaScript Mixed up to 4611686018427387903 natively; wraps silently past it Poor 63 bits native, 32 in JavaScript, 31 in WebAssembly Good unboxed, the fastest Good +, -, *, / and mod
Int32.t Size: 32 bits everywhere Poor up to 2147483647 Good exactly 32 bits, as C and file formats expect Mixed boxed values cost an allocation Poor Int32.add, Int32.mul and literals such as 5l
Int64.t Size: 64 bits everywhere Good up to 9223372036854775807 Good exactly 64 bits on every target Mixed boxed values cost an allocation Poor Int64.add and literals such as 1L
Arbitrary precision Size: as many digits as memory allows Good no fixed limit, such as a library like Zarith Good the same value everywhere Poor slower, and a library to install Mixed the library's own functions and operators
How the answers decide
  • “Can the values grow past about 4.6 × 10^18, the native int's limit?” Yes, they can: rules out int and Int32.t. Past max_int a native int wraps round to min_int with no error, and Int32 stops at 2147483647.
  • “Can the values grow past about 4.6 × 10^18, the native int's limit?” Yes, they can: adds a caveat to Int64.t. Int64 stops at 9223372036854775807 and wraps the same way past it; with no known limit, use arbitrary precision.
  • “Must the size match a file format, a network protocol or a C library exactly?” Yes, a fixed size: rules out int and Arbitrary precision. Only Int32 and Int64 have a fixed size; int's size depends on the target, and arbitrary precision has none.
  • “Will the program also be compiled to JavaScript or WebAssembly?” Yes, for the browser: adds a caveat to int. The same program printed int_size 32 under js_of_ocaml and 31 under wasm_of_ocaml, so values that fit natively may wrap.

For counts, indexes and money in paise, int is right on a server. When values have no known limit, an arbitrary-precision library such as Zarith keeps every digit, at a cost in speed.

Checkpoint

Check: int, float and conversions · 63-bit ints and wider types

  1. Question 1 of 2 A program needs the average of three ints, total = 10 and n = 3, as a float. Which expression type-checks?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: float_of_int total /. float_of_int n

    Convert first, then divide with the float operator; converting after an integer division keeps its truncation.

  2. Question 2 of 2 In a native OCaml 5.5.1 program, what is max_int + 1?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: min_int, -4611686018427387904, with no error

    Use Int64 or an arbitrary-precision library when values can pass 4611686018427387903.

Division truncates towards zero, and mod takes the dividend’s sign

Integer division rounds towards zero, and mod takes the sign of the number being divided, so a = (a / b) * b + (a mod b) always holds. The Int module of OCaml 5.5 adds three more ways to divide, each with its own rounding:

-7 divided by 2, five ways Python · division_session.py
from session import toplevel

# Integer division truncates towards zero and mod takes the sign of the dividend; OCaml 5.5's Int.fdiv, cdiv, ediv
# and erem round down, round up or keep the remainder non-negative; erem keeps a step back from day 0 of a week
# inside 0 to 6. Dividing an int by zero raises an exception.
toplevel("""
-7 / 2;;
-7 mod 2;;
Int.fdiv (-7) 2;;
Int.cdiv (-7) 2;;
Int.ediv (-7) 2;;
Int.erem (-7) 2;;
(-1) mod 7;;
Int.erem (-1) 7;;
1 / 0;;
""")

Output

$ ocaml
OCaml version 5.5.1
Enter #help;; for help.

# -7 / 2;;
- : int = -3
# -7 mod 2;;
- : int = -1
# Int.fdiv (-7) 2;;
- : int = -4
# Int.cdiv (-7) 2;;
- : int = -3
# Int.ediv (-7) 2;;
- : int = -4
# Int.erem (-7) 2;;
- : int = 1
# (-1) mod 7;;
- : int = -1
# Int.erem (-1) 7;;
- : int = 6
# 1 / 0;;
Exception: Division_by_zero.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 division_session.py

Expression Result Rounds
-7 / 2 -3 towards zero
-7 mod 2 -1 remainder with the sign of -7
Int.fdiv (-7) 2 -4 down, towards minus infinity
Int.cdiv (-7) 2 -3 up, towards plus infinity
Int.ediv (-7) 2, Int.erem (-7) 2 -4, 1 so the remainder is never negative

The table matters when a negative number meets mod. Testing “is n odd?” with n mod 2 = 1 fails for -7, whose mod 2 is -1; n mod 2 <> 0 or Int.erem n 2 = 1 works for both signs. Indexes that wrap around need the same care. Counting the days of a week from 0, one step back from day 0 is (-1) mod 7, which gave -1, not a day at all, while Int.erem (-1) 7 gave 6, the last day of the week. And 1 / 0 raised Division_by_zero, an exception, while the next section shows that floats behave quite differently.

Key idea

Integer division and mod follow the sign of the dividend, so test oddness with n mod 2 <> 0, and use Int.erem when a remainder must never be negative.

Floats follow IEEE 754: infinity, nan and binary rounding

Floats are 64-bit binary numbers, as the Float module’s documentation describes, so division by zero does not raise: it gives infinity, or nan, “not a number”, for 0. /. 0.. Comparing nan is where programs go wrong:

infinity, nan, rounding and printing Python · float_session.py
from session import toplevel

# Floats follow IEEE 754: dividing by zero gives infinity or nan, and nan is not equal to itself under =, though
# compare and Float.equal treat it as equal. Converting to int truncates; printing shows the binary value.
toplevel("""
1. /. 0.;;
0. /. 0.;;
nan = nan;;
compare nan nan;;
Float.equal nan nan;;
truncate 2.9;;
Float.round 2.5;;
string_of_float (0.1 +. 0.2);;
0.1 +. 0.2 = 0.3;;
Float.abs (0.1 +. 0.2 -. 0.3) < 1e-9;;
Printf.printf "%.17g %h %F\\n" (0.1 +. 0.2) 0.1 1.0;;
""")

Output

$ ocaml
OCaml version 5.5.1
Enter #help;; for help.

# 1. /. 0.;;
- : float = infinity
# 0. /. 0.;;
- : float = nan
# nan = nan;;
- : bool = false
# compare nan nan;;
- : int = 0
# Float.equal nan nan;;
- : bool = true
# truncate 2.9;;
- : int = 2
# Float.round 2.5;;
- : float = 3.
# string_of_float (0.1 +. 0.2);;
- : string = "0.3"
# 0.1 +. 0.2 = 0.3;;
- : bool = false
# Float.abs (0.1 +. 0.2 -. 0.3) < 1e-9;;
- : bool = true
# Printf.printf "%.17g %h %F\n" (0.1 +. 0.2) 0.1 1.0;;
0.30000000000000004 0x1.999999999999ap-4 1.
- : unit = ()

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 float_session.py

nan = nan was false, because IEEE 754 makes nan unequal to everything, itself included, while compare nan nan gave 0 and Float.equal nan nan gave true, so sorting and tables treat it consistently. truncate 2.9 dropped the fraction to 2, and Float.round 2.5 rounded half away from zero, to 3. Finally, printing: string_of_float showed 0.3, while %.17g printed the stored value 0.30000000000000004, and %h gave the exact binary value of 0.1 in hexadecimal.

That stored value is why 0.1 +. 0.2 = 0.3 answered false. Neither 0.1 nor 0.2 has an exact binary form, and their rounded sum lands one step away from the rounded 0.3. Results of float arithmetic are compared within a tolerance chosen for the problem: Float.abs (0.1 +. 0.2 -. 0.3) < 1e-9 gave true. Amounts that must add up exactly, such as money, belong in integers of the smallest unit instead.

Check for nan with Float.is_nan

x = nan is always false, even when x is nan. Write Float.is_nan x, or compare with Float.equal when nan must count as equal to itself.

Checkpoint

Check: Division and mod on negatives · infinity, nan and rounding

  1. Question 1 of 2 is_odd n = (n mod 2 = 1) returns false for n = -7. Which fix works for every int?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: n mod 2 <> 0

    mod takes the sign of the dividend; Int.erem n 2 = 1 also works, because erem is never negative.

  2. Question 2 of 2 let x = 0. /. 0. in x = x. What is the value?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: false

    Check with Float.is_nan x rather than x = nan.

Practise: numbers that surprise

The questions give expressions with negative numbers, overflow, mixed types and nan, and ask what OCaml 5.5.1 answers. Each answer is in one of the sessions above:

Practice that adapts to you

Practise this lesson

12 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 12 Which operator adds two floats?

    Choose one answer.

    Show the answer: Question 1 of 12

    Answer: +.

    +., -., . and /. for floats; +, -, , / and mod for ints.

  2. Question 2 of 12 What does int_of_string_opt "12a" return?

    Choose one answer.

    Show the answer: Question 2 of 12

    Answer: None

    The session printed - : int option = None.

  3. Question 3 of 12 What number is inside the Some that int_of_string_opt "0x1F" returns?

    Type a number.

    A hint

    0x means hexadecimal; 1F is 16 + 15.

    Show the answer: Question 3 of 12

    Answer: 31

    The session printed Some 31. The 0o and 0b prefixes work in the same way.

  4. Question 4 of 12 What does Sys.int_size give in a native OCaml program on a 64-bit Mac?

    Type a number.

    A hint

    One bit of the word is not part of the number.

    Show the answer: Question 4 of 12

    Answer: 63 bits

    The session printed 63; the JavaScript and WebAssembly builds printed 32 and 31.

  5. Question 5 of 12 A counter of bytes uploaded works on the server but wraps to a negative number in the browser build. What is the best fix?

    Choose one answer.

    Show the answer: Question 5 of 12

    Answer: Use Int64 for the counter, which is 64 bits on every target

    The targets session printed three different int sizes for one program.

  6. Question 6 of 12 What is -7 / 2 in OCaml?

    Choose one answer.

    Show the answer: Question 6 of 12

    Answer: -3

    The session printed - : int = -3.

  7. Question 7 of 12 What does Int.erem (-7) 2 give?

    Type a number.

    A hint

    erem keeps the remainder non-negative.

    Show the answer: Question 7 of 12

    Answer: 1

    -7 = -4 × 2 + 1, so ediv gives -4 and erem gives 1, as the session printed.

  8. Question 8 of 12 Which of these raises an exception in OCaml?

    Choose one answer.

    Show the answer: Question 8 of 12

    Answer: 1 / 0

    Only integer division by zero raises; floats and overflow produce values.

  9. Question 9 of 12 What does Float.round 2.5 give?

    Choose one answer.

    Show the answer: Question 9 of 12

    Answer: 3.

    The session printed - : float = 3.

  10. Question 10 of 12 string_of_float (0.1 +. 0.2) gives "0.3". What does Printf.printf "%.17g" (0.1 +. 0.2) print?

    Choose one answer.

    Show the answer: Question 10 of 12

    Answer: 0.30000000000000004

    Short printed floats can hide the binary value; %.17g or %h shows it.

  11. Question 11 of 12 Days of a week are numbered 0 to 6, and today is day 2. Which day is 5 days earlier, computed with Int.erem (2 - 5) 7?

    Type a number.

    A hint

    2 - 5 is -3. Which remainder between 0 and 6 does Int.erem give for -3 and 7?

    Show the answer: Question 11 of 12

    Answer: 4 day

    Int.erem keeps the remainder between 0 and 6: -3 = -1 × 7 + 4, so the answer is day 4. (2 - 5) mod 7 would give -3, not a day at all, just as (-1) mod 7 gave -1 in the session while Int.erem (-1) 7 gave 6.

  12. Question 12 of 12 A test checks a computed total with total = 0.3, where total is 0.1 +. 0.2. What should it do instead?

    Choose one answer.

    Show the answer: Question 12 of 12

    Answer: Compare within a tolerance, such as Float.abs (total -. 0.3) < 1e-9

    Floats are binary, so decimal fractions are rounded. Compare float results within a tolerance chosen for the problem, and keep exact amounts in integers.

Interview questions

Warm-up (fresher to 2–5 years): why does 3 + 2.0 not compile in OCaml?

OCaml has no implicit conversions between numeric types, and its operators are typed: + takes two ints and +. takes two floats. In 3 + 2.0, 2.0 is a float where + expects an int, so the type checker rejects the expression. You write the conversion yourself, float_of_int 3 +. 2.0, which gives 5., or 3 + int_of_float 2.0 if you want an int. The design costs a few characters but removes a class of bugs where a value is silently rounded or widened, and it is also why the compiler can keep ints unboxed and fast.

Two harder questions follow, about a sum that turns negative and about averaging prices. Settle your answer before you open each first step:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    A tool adds up file sizes in bytes with plain ints. The native build is right, but the same code built with wasm_of_ocaml for a browser page shows a negative total once the files pass about one gigabyte. Explain what happens and what you would change.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    You receive prices as strings, such as "120", "99" and "abc", and must return their average. Describe an OCaml function for it: how it parses, what it does with bad or empty input, and how it divides.

    Model answer, Pro. First step and follow-ups

Flashcards · 7 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. Why does 3 + 2.0 not compile?

    int and float are separate types with separate operators: + takes ints and +. takes floats. Write the conversion yourself, as in float_of_int 3 +. 2.0.

  2. What does max_int + 1 give in native OCaml 5.5.1?

    min_int, -4611686018427387904, with no error: a native int has 63 bits and wraps silently.

  3. What do you use when 63 bits are not enough, or a fixed width is required?

    Int64 or Int32 for fixed widths, or an arbitrary-precision library such as Zarith. Under JavaScript and WebAssembly a plain int has only 32 or 31 bits.

  4. Why is -7 mod 2 equal to -1, and how should oddness be tested?

    Integer division truncates towards zero and mod takes the sign of the dividend. Test oddness with n mod 2 <> 0, and use Int.erem (OCaml 5.5) for a remainder that is never negative.

  5. Which OCaml 5.5 functions round an integer division down or up?

    Int.fdiv rounds down and Int.cdiv rounds up; Int.ediv and Int.erem keep the remainder non-negative.

  6. Why is x = nan always false, and what should you write instead?

    Floats follow IEEE 754, where nan equals nothing, not even itself. Write Float.is_nan x, or compare with Float.equal when nan must equal itself.

  7. How do you read a number a user typed without stopping the program on bad input?

    match int_of_string_opt line with Some age -> … | None -> …; plain int_of_string raises on input such as 12a.

Key takeaways

  • int and float are separate types with separate operators, such as + and +., and every conversion between them, or from text, is written out.
  • A native int has 63 bits and wraps silently past max_int, and the same program has 32-bit or 31-bit ints in JavaScript or WebAssembly, so larger or fixed sizes need Int64, Int32 or an arbitrary-precision library.
  • Integer division truncates towards zero and mod takes the dividend’s sign, while OCaml 5.5’s Int.fdiv, Int.cdiv, Int.ediv and Int.erem round down, up or keep the remainder non-negative.
  • Floats follow IEEE 754, so division by zero gives infinity or nan, nan = nan is false, and %.17g shows the binary value behind a short printed float.

Terms in this lesson

integer overflow
A result beyond max_int or min_int. OCaml's int arithmetic wraps around silently, so max_int + 1 gives min_int and no error is raised.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.