4480aadc689195bde87cf2978e835f0d

We needed to do a bunch of calculations in our Physics labs with numbers that have an error attached. These turned out to be a pain in the ass, so...

By way of example, (3±0.5) + (4±0.1) = (7±0.6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class ENumber
  
  attr_accessor :n, :e
  
  def initialize( number, error )
    @n = number
    @e = error
  end

  # Basic arithmetic. Call like (ENumber1 + ENumber2)
  def +(o); ENumber.new(@n+o.n, @e+o.e); end
  def -(o); ENumber.new(@n-o.n, @e+o.e); end
  def *(o); ENumber.new(@n*o.n, multError(o)); end
  def /(o); ENumber.new(@n/o.n, multError(o)); end

  # Trigonometric functions. Call like "myENumber.cos"
  def tan; trig(:tan); end
  def sin; trig(:sin); end
  def cos; trig(:cos); end

  # Print this ENumber with the number and error rounded to x and y, respectively.
  # Probably should find the real way to print a +/-. since it screws up when I edit
  # this file on a terminal without UTF-8 (ie. almost any terminal)
  def to_s(x=4,y=x)
    "%.#{x}f±%.#{y}f" % [@n, @e]
  end

  private
  def trig(type)
    fcn = Math.method(type)
    ENumber.new(fcn.call(@n), 0.5*(fcn.call(@n+@e)-fcn.call(@n-@e)).abs )
  end

  def multError(o)
    (@n * o.n).abs * ((@e / (@n.abs)) + (o.e / (o.n.abs)))
  end
end

# Normally this file will be included as a library.
# If it's loaded directly, run the below. Demo/example/cheap-ass-unittest.
if __FILE__ == $0
  n = ENumber.new(3,0.5)
  m = ENumber.new(4,0.25)
  
  puts (((m*n).tan)+n).to_s(5,6)
end

Refactorings

No refactoring yet !

729442eea8d8548842a6e0947e333c7b

Chris Jester-Young

June 30, 2008, June 30, 2008 00:08, permalink

No rating. Login to rate!

I don't know whether your specific problem domain can be solved using interval arithmetic, but if so, a search for "ruby interval arithmetic" came up with http://intervals.rubyforge.org/. Hope it helps. :-)

6f1de5092c7feccf2bc3b97a2d4df69d

Alan Hensel

June 30, 2008, June 30, 2008 00:29, permalink

No rating. Login to rate!

Hmm, first of all, are you sure those are the error propagation formulas you want to use? They aren't the ones I remember from school....

See http://instructor.physics.lsa.umich.edu/ip-labs/tutorials/errors/prop.html

In any case, here's one suggestion. You could extend Float with a plusminus method. Which is prettier code? ENumber.new(5.3, 0.1) or 5.3.plusminus(0.1) ?

1
2
3
4
5
class Float
  def plusminus(error)
    ENumber.new(self, error)
  end
end

Your refactoring





Format Copy from initial code

or Cancel