1. A method, comprising: identifying, by a processor, input code including ordered programming statements within memory; generating, by the processor, an initial graph representation of arithmetic operations specified by the ordered programming statements, wherein the initial graph representation includes nodes corresponding to the arithmetic operations and edges corresponding to data flow between the nodes; annotating, by the processor, at least a first portion of the edges of the initial graph representation with predefined attributes describing requirements related to the corresponding data flow to produce, by the processor, a partially annotated graph representation; applying, by the processor, a set of propagation rules to each node within the partially annotated graph representation to update attributes of at least a second portion of the edges of the partially annotated graph representation describing requirements related to the corresponding data flow and to produce, by the processor, a fully annotated graph representation; for each edge within the fully annotated graph representation, selecting, by the processor, numerical types for the edge based on the attributes of the edge describing the requirements related to the corresponding data flow to produce a transformed graph representation; and generating, by the processor, output code based on the transformed graph representation.