If   f,   a,   and   b   are values with uncertainties   σf,   σa,   and   σb,   and   c   is a constant;

then if   f   is derived from   a,   b,   and   c   in the following ways,

then   σf   can be calculated as follows:

Addition/Subtraction
 If   f = a ± c,   or   f = c ± a   then   σf = σa
 If   f = a ± b   then   σf2 = σa2 + σb2
Multiplication/Division
 If   f = ca   or   f = ac       then   σf = |cσa|
 If   f = ab   or   f = a / b   then   σf2 = f2( (σa / a)2 + (σb / b)2)
Exponentiation
 If   f = ac   then   σf = |fc(σa / a)|


Caution:

This implementation of error propagation does not address issues of dependent and independent values.   It is assumed that   a   and   b   are independent and so the formula for multiplication should not be applied to   a*a   for example.   See   the talk page   for some of the implications of this issue.


Task details

 Add an uncertain number type to your language that can support addition, subtraction, multiplication, division, and exponentiation between numbers with an associated error term together with 'normal' floating point numbers without an associated error term. 
Implement enough functionality to perform the following calculations.
 Given coordinates and their errors:
x1 = 100 ± 1.1
y1 = 50 ± 1.2
x2 = 200 ± 2.2
y2 = 100 ± 2.3
 if point p1 is located at (x1, y1) and p2 is at (x2, y2); calculate the distance between the two points using the classic Pythagorean formula: 
  d = √   (x1 - x2)²   +   (y1 - y2)²    
 Print and display both    d    and its error.


References

 A Guide to Error Propagation B. Keeney, 2005.
 Propagation of uncertainty Wikipedia.


Related task

   Quaternion type


package require Tcl 8.6
oo::class create RAII-support {
    constructor {} {
	upvar 1 { end } end
	lappend end [self]
	trace add variable end unset [namespace code {my DieNicely}]
    }
    destructor {
	catch {
	    upvar 1 { end } end
	    trace remove variable end unset [namespace code {my DieNicely}]
	}
    }
    method return {{level 1}} {
	incr level
	upvar 1 { end } end
	upvar $level { end } parent
	trace remove variable end unset [namespace code {my DieNicely}]
	lappend parent [self]
	trace add variable parent unset [namespace code {my DieNicely}]
	return -level $level [self]
    }
    # Swallows arguments
    method DieNicely args {tailcall my destroy}
}
oo::class create RAII-class {
    superclass oo::class
    method return args {
	[my new {*}$args] return 2
    }
    method unknown {m args} {
	if {[string is double -strict $m]} {
	    return [tailcall my new $m {*}$args]
	}
	next $m {*}$args
    }
    unexport create unknown
    self method create args {
	set c [next {*}$args]
	oo::define $c superclass {*}[info class superclass $c] RAII-support
	return $c
    }
}
# Makes a convenient scope for limiting RAII lifetimes
proc scope {script} {
    foreach v [info global] {
	if {[array exists ::$v] || [string match { * } $v]} continue
	lappend vars $v
	lappend vals [set ::$v]
    }
    tailcall apply [list $vars [list \
	try $script on ok msg {$msg return}
    ] [uplevel 1 {namespace current}]] {*}$vals
}