#!perl use 5.012; # strict, // use warnings; use Machine::Epsilon; use Data::IEEE754::Tools qw/ulp/; use Test::More; sub dprint { my ($name, $value) = @_; diag sprintf '%-16s => %24.17e', $name, $value } my $eps = machine_epsilon(); # note the docs say that it's relative, _not_ absolute my $ulp1 = ulp(1); # ULP = https://en.wikipedia.org/wiki/Unit_in_the_last_place is $eps, $ulp1, 'machine epsilon is same as ULP(1)'; dprint eps => $eps; dprint 'ulp(1)' => $ulp1; isnt $eps, ulp(2), 'machine epsilon is not same as ULP(2)'; dprint 'ulp(2)'=> ulp(2); is $eps*2, ulp(2), 'machine epsilon scaled by 2 is actually the same as ULP(2)'; dprint '2*eps', 2*$eps; isnt $eps*2.1, ulp(2.1), 'machine epsilon is _not_ scaled by 2.1 to give the same as ULP(2)'; dprint '2.1*eps', 2.1*$eps; dprint 'ulp(2.1)' => ulp(2.1); isnt $eps, ulp(1/10), 'machine epsilon is not the same as ULP(1/10)'; dprint 'ulp(1/10)', ulp(1/10); diag 'using machine epsilon does not give the results you seem to think'; my $start = 1e5; my $x = $start; $x -= (1/10) for 1..1e6; # one million subtractions of 0.1 my $diff = $start - 1e6*(1/10); # subtract one million times the floating point 0.1 isnt $x, $diff, '1 million single steps is not the same as removing one million times the step-value'; dprint 'x' => $x; dprint 'diff' => $diff; my $sectokia = $x + 2*$eps; # this is the fix that sectokia applied isnt $sectokia, $diff; dprint 'sectokia' => $sectokia; diag "stringify sectokia = ".($sectokia)."\n"; diag "in case you don't believe mine is equivalent:"; $start = 0.8; $_ = 0; $x = $start; $sectokia = $x + 2*$eps; # this is adding the two epsilons... $diff = $start - $_ * 0.01; diag sprintf "loop %4d:\n\tx = %24.17e = '%s',\n\tsectokia = %24.17e = '%s',\n\tdiff = %24.17e = '%s'", $_, $x, $x, $sectokia, $sectokia, $diff, $diff; for (1..1000) { $x -= 0.01; # same as your $x -= 0.01 in the for loop $sectokia = $x + 2*$eps; # this is adding the two epsilons... $diff = $start - $_ * 0.01; if( $_ < 10 or 0 == $_ % 100 ) { diag sprintf "loop %4d:\n\tx = %24.17e = '%s',\n\tsectokia = %24.17e = '%s',\n\tdiff = %24.17e = '%s'", $_, $x, $x, $sectokia, $sectokia, $diff, $diff; } if( "$sectokia" ne "$diff" ) { # if the stringified diag sprintf "loop %4d:\n\tx = %24.17e = '%s',\n\tsectokia = %24.17e = '%s',\n\tdiff = %24.17e = '%s'", $_, $x, $x, $sectokia, $sectokia, $diff, $diff; diag "mismatch ends loop at $_\n\n"; last; } } done_testing; #### ok 1 - machine epsilon is same as ULP(1) # eps => 2.22044604925031308e-16 # ulp(1) => 2.22044604925031308e-16 ok 2 - machine epsilon is not same as ULP(2) # ulp(2) => 4.44089209850062616e-16 ok 3 - machine epsilon scaled by 2 is actually the same as ULP(2) # 2*eps => 4.44089209850062616e-16 ok 4 - machine epsilon is _not_ scaled by 2.1 to give the same as ULP(2) # 2.1*eps => 4.66293670342565767e-16 # ulp(2.1) => 4.44089209850062616e-16 ok 5 - machine epsilon is not the same as ULP(1/10) # ulp(1/10) => 1.38777878078144568e-17 # using machine epsilon does not give the results you seem to think ok 6 - 1 million single steps is not the same as removing one million times the step-value # x => -1.33288113454699264e-06 # diff => 0.00000000000000000e+00 ok 7 # sectokia => -1.33288113410290343e-06 # stringify sectokia = -1.3328811341029e-06 # in case you don't believe mine is equivalent: # loop 0: # x = 8.00000000000000044e-01 = '0.8', # sectokia = 8.00000000000000488e-01 = '0.8', # diff = 8.00000000000000044e-01 = '0.8' # loop 1: # x = 7.90000000000000036e-01 = '0.79', # sectokia = 7.90000000000000480e-01 = '0.79', # diff = 7.90000000000000036e-01 = '0.79' # loop 2: # x = 7.80000000000000027e-01 = '0.78', # sectokia = 7.80000000000000471e-01 = '0.78', # diff = 7.80000000000000027e-01 = '0.78' # loop 3: # x = 7.70000000000000018e-01 = '0.77', # sectokia = 7.70000000000000462e-01 = '0.77', # diff = 7.70000000000000018e-01 = '0.77' # loop 4: # x = 7.60000000000000009e-01 = '0.76', # sectokia = 7.60000000000000453e-01 = '0.76', # diff = 7.60000000000000009e-01 = '0.76' # loop 5: # x = 7.50000000000000000e-01 = '0.75', # sectokia = 7.50000000000000444e-01 = '0.75', # diff = 7.50000000000000000e-01 = '0.75' # loop 6: # x = 7.39999999999999991e-01 = '0.74', # sectokia = 7.40000000000000435e-01 = '0.74', # diff = 7.39999999999999991e-01 = '0.74' # loop 7: # x = 7.29999999999999982e-01 = '0.73', # sectokia = 7.30000000000000426e-01 = '0.73', # diff = 7.29999999999999982e-01 = '0.73' # loop 8: # x = 7.19999999999999973e-01 = '0.72', # sectokia = 7.20000000000000417e-01 = '0.72', # diff = 7.20000000000000084e-01 = '0.72' # loop 9: # x = 7.09999999999999964e-01 = '0.71', # sectokia = 7.10000000000000409e-01 = '0.71', # diff = 7.10000000000000075e-01 = '0.71' # loop 70: # x = 9.99999999999994643e-02 = '0.0999999999999995', # sectokia = 9.99999999999999084e-02 = '0.0999999999999999', # diff = 9.99999999999999778e-02 = '0.1' # mismatch ends loop at 70 # 1..7