快速浮点数求和

lua-users home
wiki

精确的浮点数求和。类似 Python 的 fsum。

此版本以逆序收集部分和,每个条目(最后一个除外)都用完所有 53 位。

由于部分和数量减少,它的速度是我旧版本的两倍(参见 FloatSum)。

要查看我的 C/C++ 版本(参见 https://github.com/achan001/fsum)。

--[[
> fsum = require 'fsum'
> = fsum(0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1) - 1
0
> fadd, ftotal = fsum()
> for i = 1, 10 do fadd(0.1) end
> = ftotal() - 1
0
> ftotal(0)    -- clear calculator
> for i = 1, 10 do fadd(0.1) end
> = ftotal() - 1
0
--]]

local function fsum(...)
    local p, abs = {1}, math.abs    -- p[1] == #p
    local function fadd(x)
        local p, i = p, 2
        for j = 2, p[1] do
            local y = p[j]
            if abs(x) > abs(y) then x, y = y, x end
            local hi = x + y
            local lo = x - (hi - y)
            x = hi
            if lo ~= 0 then p[i] = x; x = lo; i = i + 1 end
        end
        if x ~= x then p[1] = 2 return end  -- Inf or NaN
        p[1] = i
        p[i] = x
    end
    local function ftotal(clear)
        if clear then p[1] = 1 end  -- clear all partials
        repeat
            local n, overlap = p[1], false
            local prev = {unpack(p, 1, n)}
            fadd(0)                 -- remove partials overlap
            if n <= 3 then return p[2] end
            for i = 1, n do
                if p[i] ~= prev[i] then overlap = true; break end
            end
        until not overlap
        local x, lo, err = unpack(p, 2, 4)
        if (lo < 0) == (err < 0) then
            lo = lo * 2             -- |lo| = 1/2 ULP
            local hi = x + lo       -- -> x off 1 ULP
            if lo == hi - x then x = hi end
        end
        return x
    end
    if select('#', ...) == 0 then return fadd, ftotal end
    for i = 1, select('#', ...) do fadd(select(i, ...)) end
    return ftotal()
end

if select(1, ...) ~= 'fsum' then    -- test code
    local read, fadd, ftotal = io.read, fsum()
    io.input(select(1, ...))        -- read from file
    pcall(function() while true do fadd(read('*n')) end end)
    print(ftotal())
end
return fsum


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2018 年 6 月 24 日凌晨 4:29 GMT (差异)