lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


> That's not good enough.
> 
> The asinh and atanh formulas are known to be inaccurate near the
> origin because of smearing.  When x*x+1 is formed, digits get thrown
> away. When x is added to the sqrt of that, more digits get thrown away.
> When log of something close to 1 is calculated, 1 is subtracted in the
> process.  This cancellation does not by itself introduce a new error,
> but exposes the fact that the digits are already irretrievably gone.
<snip>

This is slightly off-topic, but here's a pure Lua implementation of inverse
hyperbolic trig functions. It also provides log1p since it's needed to keep
some precision. Unfortunately, log1p does not give "full" precision, but it
should be good enough (of course, it's possible to implement a more precise
log1p, but that would be more convoluted.)

Cheers,
Luis


-- invhyp.lua: inverse hyperbolic trig functions
-- Adapted from glibc
local abs, log, sqrt = math.abs, math.log, math.sqrt
local log2 = log(2)

-- good for IEEE754, double precision
local function islarge (x) return x > 2 ^ 28 end
local function issmall (x) return x < 2 ^ (-28) end

local INF = math.huge
local function isinfornan (x)
  return x ~= x or x == INF or x == -INF
end

local function log1p (x) -- not very precise, but works well
  local u = 1 + x
  if u == 1 then return x end -- x < eps?
  return log(u) * x / (u - 1)
end

local function acosh (x)
  if x < 1 then return (x - x) / (x - x) end -- nan
  if islarge(x) then
    if isinfornan(x) then return x + x end
    return log2 + log(x)
  end
  if x + 1 == 1 then return 0 end -- acosh(1) == 0
  if x > 2 then
    local x2 = x * x
    return log(2 * x - 1 / (x + sqrt(x2 - 1)))
  end
  -- 1 < x < 2:
  local t = x - 1
  return log1p(t + sqrt(2 * t + t * t))
end

local function asinh (x)
  local y = abs(x)
  if issmall(y) then return x end
  local a
  if islarge(y) then -- very large?
    if isinfornan(x) then return x + x end
    a = log2 + log(y)
  else
    if y > 2 then
      a = log(2 * y + 1 / (y + sqrt(1 + y * y)))
    else
      local y2 = y * y
      a = log1p(y + y2 / (1 + sqrt(1 + y2)))
    end
  end
  return x < 0 and -a or a -- transfer sign
end

local function atanh (x)
  local y = abs(x)
  local a
  if y < .5 then
    if issmall(y) then return x end
    a = 2 * y
    a = .5 * log1p(a + a * y / (1 - y))
  else
    if y < 1 then
      a = .5 * log1p(2 * y / (1 - y))
    elseif y > 1 then
      return (x - x) / (x - x) -- nan
    else -- y == 1
      return x / 0 -- inf with sign
    end
  end
  return x < 0 and -a or a -- transfer sign
end

return {log1p = log1p, asinh = asinh, acosh = acosh, atanh = atanh}


-- 
Computers are useless. They can only give you answers.
                -- Pablo Picasso

-- 
Luis Carvalho (Kozure)
lua -e 'print((("lexcarvalho@NO.gmail.SPAM.com"):gsub("(%u+%.)","")))'