À un même opérateur arithmétique (notion syntaxique) peuvent correspondre plusieurs opérations (notion sémantique). Par exemple, l'opération de division dépend du type des opérandes, selon qu'ils sont entiers ou flottants : 5/2 a pour valeur tandis que 5.0/2.0 a pour valeur . On dit que l'opérateur / est surchargé.
Quand les opérandes d'une opération ne sont pas de même type (par exemple un int et un float), il y a conversion implicite de l'un d'eux dans le type de l'autre ; par exemple, int + float est converti en float + float. Cette conversion implicite se fait toujours dans la direction du << plus grand >> type : de int vers float, de float vers double.
Les conversions ascendantes entre types entiers d'une part, et entre types flottants d'autre part, se font toujours sans perte d'information :
Par contre, la conversion de int en float se fait avec une perte de précision, même si ces deux types ont généralement la même taille. Ces conversions implicites sont également appliquées lors du passage d'argument et lors du retour d'un appel de fonction. Par exemple, si une fonction f est déclarée int f(int), un appel f(2.1), avec un argument double conduira d'abord à une conversion de double en int : c'est donc f(2) qui sera invoquée.
Il existe aussi des conversions explicites, qui doivent figurer dans le programme. Par exemple, pour effectuer une opération flottante quand les opérandes sont entiers, il faut d'abord appliquer une conversion explicite, ou coercition (en anglais, cast), double() à l'un des opérandes : double(5)/2 et 5/double(2) ont ainsi pour valeur et double(5) a pour valeur le flottant . Il existe des coercitions entre tous les types numériques. Les conversions descendantes, par exemple de int vers char, peuvent conduire à une perte d'information, par troncature. Les conversions entre float et int, même si ces deux types ont généralement la même taille se font toujours avec perte d'information. Seule la conversion d'int en double se fait généralement sans perte d'information.