[Arjen Markus] (10 august 2004) "Real" numbers on computers can cause a lot of agony because they are implemented (most of the time) using binary rather than decimal arithmetic. Thus "obvious" computations like: 0.1 + 0.1 == 0.2 fail on most computer systems. There are many ways to solve these problems, though they almost always mean that you need another package or library than the default one - for instance arbitrary precision arithmetic packages. An important class of applications where these problems are really annoying is that of financial computations. I am no expert in this field, but here is my thought: ''Why not use'' '''fixed-point arithmetic''' ''?'' The range for the numbers one deals with is fairly limited, the precision is too, but you do need "decimally" predictable results. I have an implementation of the basic arithmetical operations in mind that should be easy to use. It is Tcl only, will not imply too much overhead as most will be done using integer computations and ought to be flexible enough for most such applications. I have not coded it yet, just thought it over and I want to use this Wiki page to see if there is any interest in it ... ---- [AM] (19 august 2004) I am slowly making progress with this little package. I do have a new addition procedure, but I want to make things more modular to save code and avoid mistakes. That is on its way ... ---- In answer to the question ''Why not use'' '''fixed-point arithmetic''' ''?'', why not use rational numbers, where the numerator and denominator are separately maintained? I believe some [Scheme] implementations can do that -- ''[escargo] 19 Aug 2004'' (I was always amazed how close the rational approximation for pi, 355/113, came to the correct value.) [Lars H]: Rational arithmetic suffers from the "combinatorial explosion"; the number of digits in the representation is typically doubled with every operation. Unless you want to compute an exact value, you probably don't want that. (As for [pi] having good rational approximations, there is another side also of that coin. The common proofs that e and pi are ''transcendental'' numbers are based on a lemma that non-rational algebraic numbers cannot be that easily approximated using rational numbers.) ---- Here is a very first version of such a package ... (It surprised me how difficult it is to adequately insert the decimal point ;) # fixedpoint.tcl -- # Implement fixed-point arithmetic # # fixedpoint -- # Namespace for the procedures and variables # namespace eval ::math::fixedpoint { variable precision 3 } # fixed -- # Convert a numerical value (interpreted as string!) to a fixed-point # number using the current precision # # Arguments: # strval String representation of the value # # Result: # A fixed-point number # proc ::math::fixedpoint::fixed { strval } { variable precision if { ![string is double -strict $strval] } { return -code error "Argument must be a valid number" } # # Find the decimal point (if any) and the exponent (if any) # set p [string first . $strval] set e [string first e $strval] set E [string first e $strval] set newval $strval set expon 0 if { $e != -1 } { set newval [string range $strval 0 [expr {$e-1}]] set expon [string range $strval [expr {$e+1}] end] } if { $E != -1 } { set newval [string range $strval 0 [expr {$E-1}]] set expon [string range $strval [expr {$E+1}] end] } if { $p != -1 } { set whole [string range $newval 0 [expr {$p-1}]] set fract [string range $newval [expr {$p+1}] end] } set expon [expr {$precision+$expon-[string length $fract]}] while { $expon > 0 } { set fract "${fract}0" incr expon -1 } if { $expon < 0 } { set fract [string range $fract 0 end$expon] # TODO: insert proper rounding ... } return [list [expr wide($whole$fract)] $precision] } # tostring -- # Convert a fixed-point number to a string # # Arguments: # fixedval Fixed-point number # # Result: # A string with the decimal representation # proc ::math::fixedpoint::tostring { fixedval } { set m [lindex $fixedval 0] if { $m == 0 } { return "0." } set p [lindex $fixedval 1] set sign "" if { $m < 0 } { set sign "-" set m [expr {-$m}] } if { [string length $m] < $p } { set m "[string repeat 0 [expr {$p-[string length $m]}]]$m" set sign "${sign}0" } return $sign[string range $m 0 end-$p].[string range $m end-[expr {$p-1}] end] } # + -- # Add two fixed-point numbers # # Arguments: # op1 First operand # op2 Second operand # # Result: # The sum of the two # proc ::math::fixedpoint::+ { op1 op2 } { # TODO: handle different precisions # TODO: handle overflow # TODO: handle unary plus set m1 [lindex $op1 0] set m2 [lindex $op2 0] set p [lindex $op1 1] return [list [expr {$m1+$m2}] $p] } # - -- # Subtract two fixed-point numbers # # Arguments: # op1 First operand # op2 Second operand # # Result: # The difference of the two # proc ::math::fixedpoint::- { op1 op2 } { # TODO: handle different precisions # TODO: handle overflow # TODO: handle unary minus set m1 [lindex $op1 0] set m2 [lindex $op2 0] set p [lindex $op1 1] return [list [expr {$m1-$m2}] $p] } set tcl_precision 17 puts [::math::fixedpoint::fixed "1.01"] puts [::math::fixedpoint::fixed "1.011e3"] puts [::math::fixedpoint::fixed "1.0112222222e3"] puts [::math::fixedpoint::tostring [::math::fixedpoint::fixed "1.0112222222e3"]] set op1 [::math::fixedpoint::fixed "1.01"] set op2 [::math::fixedpoint::fixed "1.021"] puts [::math::fixedpoint::tostring [::math::fixedpoint::+ $op1 $op2]] puts [expr {1.01+1.021}] puts [::math::fixedpoint::tostring [::math::fixedpoint::- $op1 $op2]] puts [expr {1.01-1.021}] ---- [[ [Category Mathematics] ]]