#!/usr/bin/perl use strict; use warnings; package IntervalFloat; use Class::InsideOut qw/:std/; use List::Util qw/min max/; use overload q/""/ => \&stringify, q/0+/ => \&numerify, q/+/ => \&add, q/-/ => \&subtract, ; private min => my %min_of; private max => my %max_of; sub create_interval { my @numbers = @_; # figure out minimum and its epsilon... my $min = min(@numbers); my $epsmin = abs $min; $epsmin /= 2.0 while ($min != $min - $epsmin / 2.0); # and maximum... my $max = max(@numbers); my $epsmax = abs $max; $epsmax /= 2.0 while ($max != $max + $epsmax / 2.0); # compute the interval... return ($min - $epsmin, $max + $epsmax); } sub new { # create object... my ($class, @args) = @_; my $self = \(my $dummy); bless $self, $class; register($self); # initialize it... ($min_of{id $self}, $max_of{id $self}) = create_interval(@args); # return it... return $self; } # figure out half of the interval length... sub halferror { my $self = shift; return ($max_of{id $self} - $min_of{id $self}) / 2.0; } sub stringify { my $self = shift; # return sprintf("[ %-65.65g\n %-65.65g]", # $min_of{id $self}, # $max_of{id $self}); return $self->numerify . ' (+/-' . $self->halferror . ')'; } sub numerify { my $self = shift; return ($min_of{id $self} + $self->halferror); } sub convert { my ($left, $right, $reversed, @rest) = @_; # see if righthand needs to be converted... $right = IntervalFloat->new($right) unless ref $right and $right->isa('IntervalFloat'); # see if parameters need to be swapped... ($left, $right) = ($right, $left) if $reversed; return ($left, $right, $reversed, @rest); } sub add { my ($left, $right) = convert(@_); return IntervalFloat->new( $min_of{id $left} + $min_of{id $right}, $max_of{id $left} + $max_of{id $right}); } sub subtract { my ($left, $right) = convert(@_); return IntervalFloat->new( $min_of{id $left} - $max_of{id $right}, $max_of{id $left} - $min_of{id $right}); } sub convert_const { print 'converting constant: [', join('] [', @_[0, 1]), '] => '; my $r = IntervalFloat->new(shift); print $r, "\n"; return $r; } sub import { overload::constant(float => \&convert_const); } sub unimport { overload::remove_constant(float => \&convert_const); } 1;