Kahan summation: Difference between revisions
Content added Content deleted
(GP -- partial) |
(Tcl implementations added) |
||
Line 923: | Line 923: | ||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ |
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ |
||
</pre> |
</pre> |
||
=={{header|Tcl}}== |
|||
===Tcl: Floats=== |
|||
First, using native floating point we see the same epsilon value as other languages using float64: |
|||
<lang Tcl># make {+ - * /} etc available as commands, for easier expressions |
|||
namespace path ::tcl::mathop |
|||
# find epsilon with native floating point: |
|||
proc epsilon {} { |
|||
set e 1.0 |
|||
while {1 + $e != 1} { |
|||
set e [/ $e 2] |
|||
} |
|||
return $e |
|||
} |
|||
# kahan sum with native floats: |
|||
proc kahansum {args} { |
|||
set sum 0.0 |
|||
set c 0.0 |
|||
foreach i $args { |
|||
set y [- $i $c] |
|||
set t [+ $sum $y] |
|||
set c [- [- $t $sum] $y] |
|||
set sum $t |
|||
} |
|||
return $sum |
|||
} |
|||
puts "Native floating point:" |
|||
puts "\tEpsilon is: [set e [epsilon]]" |
|||
puts "\tAssociative sum: [expr {1.0 + $e - $e}]" |
|||
puts "\tKahan sum: [kahansum 1.0 $e -$e]"</lang> |
|||
<pre>Epsilon is: 1.1102230246251565e-16 |
|||
Associative sum: 0.9999999999999999 |
|||
Kahan sum: 1.0</pre> |
|||
===Tcl: Decimals=== |
|||
{{tcllib|math::decimal}} |
|||
For the decimal part of the exercise we can use a the Tcllib library <tt>math::decimal</tt>. Note how similar the implementation of Kahan sum is: the only changes are <tt>fromstr</tt> and <tt>tostr</tt>. |
|||
The last stanza exercises the decimal package's different rounding modes, to see what happens there: |
|||
<lang Tcl>package require math::decimal |
|||
namespace path ::math::decimal |
|||
proc kahansum {args} { |
|||
set sum [fromstr 0.0] |
|||
set c [fromstr 0.0] |
|||
foreach i $args { |
|||
set i [fromstr $i] |
|||
set y [- $i $c] |
|||
set t [+ $sum $y] |
|||
set c [- [- $t $sum] $y] |
|||
set sum $t |
|||
} |
|||
return [tostr $sum] |
|||
} |
|||
proc asum {args} { |
|||
set sum [fromstr 0.0] |
|||
foreach a $args { |
|||
set sum [+ $sum [fromstr $a]] |
|||
} |
|||
return [tostr $sum] |
|||
} |
|||
setVariable precision 6 |
|||
set a 10000.0 |
|||
set b 3.14159 |
|||
set c 2.71828 |
|||
foreach rounding {half_even half_up half_down down up floor ceiling} { |
|||
setVariable rounding $rounding |
|||
puts "Rounding mode: $rounding" |
|||
puts "\tAssociative sum $a + $b + $c: [asum $a $b $c]" |
|||
puts "\tKahan sum $a + $b + $c: [kahansum $a $b $c]" |
|||
}</lang> |
|||
The results are a little surprising: |
|||
{{out}} |
|||
<pre>Rounding mode: half_even |
|||
Associative sum 10000.0 + 3.14159 + 2.71828: 10005.8 |
|||
Kahan sum 10000.0 + 3.14159 + 2.71828: 10005.9 |
|||
Rounding mode: half_up |
|||
Associative sum 10000.0 + 3.14159 + 2.71828: 10005.8 |
|||
Kahan sum 10000.0 + 3.14159 + 2.71828: 10005.9 |
|||
Rounding mode: half_down |
|||
Associative sum 10000.0 + 3.14159 + 2.71828: 10005.8 |
|||
Kahan sum 10000.0 + 3.14159 + 2.71828: 10005.9 |
|||
Rounding mode: down |
|||
Associative sum 10000.0 + 3.14159 + 2.71828: 10005.8 |
|||
Kahan sum 10000.0 + 3.14159 + 2.71828: 10005.8 |
|||
Rounding mode: up |
|||
Associative sum 10000.0 + 3.14159 + 2.71828: 10006.0 |
|||
Kahan sum 10000.0 + 3.14159 + 2.71828: 10005.9 |
|||
Rounding mode: floor |
|||
Associative sum 10000.0 + 3.14159 + 2.71828: 10005.8 |
|||
Kahan sum 10000.0 + 3.14159 + 2.71828: 10005.8 |
|||
Rounding mode: ceiling |
|||
Associative sum 10000.0 + 3.14159 + 2.71828: 10006.0 |
|||
Kahan sum 10000.0 + 3.14159 + 2.71828: 10005.9</pre> |
|||
In no rounding mode are both answers correct. |
|||
With "down" and "floor" rounding, the Kahan sum is too low (10005.8), but any other rounding makes it correct (10005.9). |
|||
The Associative largest-to-smallest sum is never correct: "up" and "ceiling" rounding make it too high, while the rest make it low. |
|||
=={{header|zkl}}== |
=={{header|zkl}}== |