lib/xirr/bisection.rb in xirr-0.2.5 vs lib/xirr/bisection.rb in xirr-0.2.7
- old
+ new
@@ -18,23 +18,52 @@
# Loops until difference is within error margin
while ((right - left).abs > Xirr::EPS && runs < Xirr.config.iteration_limit.to_i) do
runs += 1
- npv_positive?(midpoint) == npv_positive?(left) ? left = midpoint : right = midpoint
- midpoint = format_irr(left, right)
+ left, midpoint, right = bisection(left, midpoint, right)
end
if runs >= Xirr.config.iteration_limit.to_i
raise ArgumentError, "Did not converge after #{runs} tries."
end
+ # If enabled, will retry XIRR with NewtonMethod
+ if Xirr::FALLBACK && right_limit_reached?(right, midpoint)
+ return NewtonMethod.new(cf).xirr
+ end
+
return midpoint.round Xirr::PRECISION
end
private
+
+ # @param right [BigDecimal]
+ # @param midpoint [BigDecimal]
+ # @return [Boolean]
+ # Checks if result is the right limit.
+ def right_limit_reached?(right, midpoint)
+ (right - midpoint).abs < Xirr::EPS
+ end
+
+ # @param left [BigDecimal]
+ # @param midpoint [BigDecimal]
+ # @param right [BigDecimal]
+ # @return [Array]
+ # Calculates the Bisections
+ def bisection(left, midpoint, right)
+ _left, _mid = npv_positive?(left), npv_positive?(midpoint)
+ if _left && _mid
+ return left, left, left if npv_positive?(right) # Not Enough Precision in the left to find the IRR
+ end
+ if _left == _mid
+ return midpoint, format_irr(midpoint, right), right # Result is to the Right
+ else
+ return left, format_irr(left, midpoint), midpoint # Result is to the Left
+ end
+ end
# @param midpoint [Float]
# @return [Bolean]
# Returns true if result is to the right ot the range
def npv_positive?(midpoint)