Skip to content

Usage

OptiFloat.jl defines a macro @optifloat which kicks of the floating point expression optimization and defines a new, potentially more accurrate function:

julia
using OptiFloat

# original
f(b, c) = (-b - sqrt(b^2 - 4c)) / (2c)

# improved
g = @optifloat (-b - sqrt(b^2 - 4c)) / (2c) batchsize=1000 T=Float16
#1 (generic function with 1 method)

The original function f is inaccurate for large, negative b:

julia
julia> f(Float16(-200), Float16(-0.1))  # low-precision, inaccurate
Float16(-0.0)

julia> f(-200.0, -0.1)                  # high-precision, accurate
0.004999987500013958

The improved function g gives almost the same result as f:

julia
julia> g(Float16(-200), Float16(-0.1))  # low-precision, accurate
Float16(0.004997)

If we are interested in the underlying expression of g, we can use the function optifloat (as opposed to the macro @optifloat) which returns an OptiFloatResult that is pretty printed as a informative report:

julia
julia> expr = :((-b - sqrt(b^2 - 4c)) / (2c));

julia> result = optifloat(expr; batchsize=1000, T=Float16)
 ────────────────────────────────────────────────────────────────────────────── 
                                OptiFloat Result                                
 ────────────────────────────────────────────────────────────────────────────── 

  Original Expression:
╭──────────────┬─────────┬──────────────────────────────────╮
 Interval      Error    Expression                       
├──────────────┼─────────┼──────────────────────────────────┤
 b: (-∞, ∞)    1.098    (-b - sqrt(b ^ 2 - 4c)) / (2c)   
╰──────────────┴─────────┴──────────────────────────────────╯

  Optimized PiecewiseRegime:
╭───────────────────┬───────────┬──────────────────────────────────────────╮
 Intervals          Error      Expression                               
├───────────────────┼───────────┼──────────────────────────────────────────┤
 b: (-∞, -1.592)    0.01743    ((4c) / (sqrt(b ^ 2 - 4c) - b)) / (2c)   
├───────────────────┼───────────┼──────────────────────────────────────────┤
 b: (-1.592, ∞)     0.00924    (-b - sqrt(b ^ 2 - 4c)) / (2c)           
├───────────────────┼───────────┼──────────────────────────────────────────┤
     Combined       0.013                         %                     
╰───────────────────┴───────────┴──────────────────────────────────────────╯

  Improved function:
 ────────────────────────────────────────────────────────────────────────────── 
   function f(b, c)                                                             
       begin                                                                    
           if -Inf16 < b <= Float16(-1.592)                                     
               return ((4c) / (sqrt(b ^ 2 - 4c) - b)) / (2c)                    
           end                                                                  
           if Float16(-1.592) < b <= Inf16                                      
               return (-b - sqrt(b ^ 2 - 4c)) / (2c)                            
           end                                                                  
       end                                                                      
   end                                                                          
 ────────────────────────────────────────────────────────────────────────────── 

The result holds e.g. the expression of the final, improved function:

julia
julia> result.improved
:((b, c)->begin
          if -Inf16 < b <= Float16(-1.592)
              return ((4c) / (sqrt(b ^ 2 - 4c) - b)) / (2c)
          end
          if Float16(-1.592) < b <= Inf16
              return (-b - sqrt(b ^ 2 - 4c)) / (2c)
          end
      end)

Verbose output / debug logs

For more verbose outputs of OptiFloats optimization process you can enable debug logging for the package:

julia
# enable debug logging for OptiFloat only
ENV["JULIA_DEBUG"] = OptiFloat

# calls to optifloat/@optifloat will print debug information
optifloat(expr; batchsize=1000, T=Float16)