Na­tive sup­port for dec­i­mal num­bers in the Python pro­gram­ming lan­guage

As part of the project of ex­plor­ing how dec­i­mal num­bers could be added to JavaScript, I'd like to take a step back and look at how oth­er lan­guages sup­port dec­i­mals (or not). Many lan­guages do sup­port dec­i­mal num­bers. It may be use­ful to un­der­stand the range of op­tions out there for sup­port­ing them. For in­stance, what kind of data mod­el do they use? What are the lim­its (if there are any)? Does the lan­gauge in­clude any spe­cial syn­tax for dec­i­mal?

Here, I'd like to briefly sum­ma­rize what Python has done.

Does Python sup­port dec­i­mals?

Python sup­ports dec­i­mal arith­metic. The func­tion­al­i­ty is part of the stan­dard li­brary. Dec­i­mals aren't avail­able out-of-the-box, in the sense that all Python pro­grams, re­gard­less of what they im­port, can start work­ing with dec­i­mals. There is no dec­i­mal lit­er­al syn­tax in the lan­guage. That said, all one needs to do is im­port * from dec­i­mal and you're ready to rock.

Dec­i­mals have been part of the Python stan­dard li­brary for a long time: they were added in ver­sion 2.4, in No­vem­ber 2001. Python does have a process for propos­ing ex­ten­sions to the lan­guage, called PEP (Python Ex­ten­sion Pro­pos­al). Ex­ten­sive dis­cus­sions on the of­fi­cial mail­ing lists took place. Python dec­i­mals were for­mal­ized in PEP 327.

The dec­i­mal li­brary pro­vides ac­cess to some of the in­ter­nals of dec­i­mal arith­metic, called the con­text. In the con­text, one can spec­i­fy, for in­stance, the num­ber of dec­i­mal dig­its that should be avail­able when op­er­a­tions are car­ried out. One can also for­bid mix­ing of dec­i­mal val­ues with prim­i­tive built-in types, such as in­te­gers and (bi­na­ry) float­ing-point num­bers.

In gen­er­al, the Python im­ple­men­ta­tion aims to be an im­ple­men­ta­tion of the Gen­er­al Dec­i­mal Arith­metic Spec­i­fi­ca­tion. In par­tic­u­lar, us­ing this data mod­el, it is pos­si­ble to dis­tin­guish the dig­it strings 1.2 and 1.20, con­sid­ered as dec­i­mal val­ues, as math­e­mat­i­cal­ly equal but nonethe­less dis­tinct val­ues.

Aside: How does this com­pare with Dec­i­mal128, one of the con­tender data mod­els for dec­i­mals in JavaScript? Since Python's dec­i­mal fea­ture is an im­ple­men­ta­tion of the Gen­er­al Dec­i­mal Arith­metic Spec­i­fi­ca­tion, it works with a sort of gen­er­al­ized IEEE 754 Dec­i­mal. No bit width is spec­i­fied, so Python dec­i­mals are not lit­er­al­ly the same as Dec­i­mal128. How­ev­er, one can suit­ably pa­ra­me­ter­ize Python's dec­i­mal to get some­thing es­sen­tial­ly equiv­a­lent to Dec­i­mal128:

  1. spec­i­fy the min­i­mum and max­i­mum ex­po­nent to -6144 and 6143, re­spec­tive­ly (the de­faults are -999999 and 999999, re­spec­tive­ly)
  2. spec­i­fy the pre­ci­sion to 34 (de­fault is 28)

API for Python dec­i­mals

Here are the sup­port­ed math­e­mat­i­cal func­tions:

As men­tioned above, the data mod­el for Python dec­i­mals al­lows for sub­nor­mal dec­i­mals, but one can al­ways nor­mal­ize a val­ue (re­move the trail­ing ze­ros). (This isn't ex­act­ly a math­e­mat­i­cal func­tion, since dis­tinct mem­bers of a co­hort are math­e­mat­i­cal­ly equal.)

In Python, when im­port­ing dec­i­mals, some of the ba­sic arith­metic op­er­a­tors get over­loaded. Thus, +, *, and **, etc., pro­duce cor­rect dec­i­mal re­sults when giv­en dec­i­mal ar­gu­ments. (There is some pos­si­bil­i­ty for some­thing rough­ly sim­i­lar in JavaScript, but that dis­cus­sion has been paused.)

Trigono­met­ric func­tions are not pro­vid­ed. (These func­tions be­long to the op­tion­al part of the IEEE 754 spec­i­fi­ca­tion.)