ref: a68e92f1e80f262e04d6e6c1d27af80199afbdc0
dir: /lib/math/log-impl.myr/
use std
use "fpmath"
use "util"
/*
See [Mul16] (6.2.2) and [Tan90].
*/
pkg math =
pkglocal const log32 : (x : flt32 -> flt32)
pkglocal const log64 : (x : flt64 -> flt64)
pkglocal const log1p32 : (x : flt32 -> flt32)
pkglocal const log1p64 : (x : flt64 -> flt64)
/* Constants from [Tan90], note that [128] contains accurate log(2) */
pkglocal const accurate_logs32 : (uint32, uint32)[129]
pkglocal const accurate_logs64 : (uint64, uint64)[129]
;;
extern const horner_polyu32 : (f : flt32, a : uint32[:] -> flt32)
extern const horner_polyu64 : (f : flt64, a : uint64[:] -> flt64)
type fltdesc(@f, @u, @i) = struct
explode : (f : @f -> (bool, @i, @u))
assem : (n : bool, e : @i, s : @u -> @f)
horner : (f : @f, a : @u[:] -> @f)
tobits : (f : @f -> @u)
frombits : (u : @u -> @f)
sgnmask : @u
sig8mask : @u
sig8last : @u
emin : @i
emax : @i
precision : @u
inf : @u
ninf : @u
nan : @u
/* For log */
logT1 : @u
logT2 : @u
/* For log1p */
log1pT1 : @u
log1pT2 : @u
T3exp : @u
/* For procedure 1 */
C : (@u, @u)[:]
Ai : @u[:]
/* For procedure 2 */
Bi : @u[:]
Mtruncmask : @u
;;
/* Accurate representations for log(1 + j/2^7), all j */
const accurate_logs32 = [
(0000000000, 0000000000),
(0x3bff0000, 0x3429ac42),
(0x3c7e0000, 0x35a8b0fc),
(0x3cbdc000, 0x368d83eb),
(0x3cfc2000, 0xb6b278c4),
(0x3d1cf000, 0x3687b9ff),
(0x3d3ba000, 0x3631ec66),
(0x3d5a1000, 0x36dd7119),
(0x3d785000, 0x35c30046),
(0x3d8b2800, 0x365bba8e),
(0x3d9a1000, 0xb621a791),
(0x3da8d800, 0x34e7e0c3),
(0x3db78800, 0xb635d46a),
(0x3dc61800, 0x368bac63),
(0x3dd49000, 0x36da7496),
(0x3de2f000, 0x36a91eb8),
(0x3df13800, 0x34edc55e),
(0x3dff6800, 0xb6dd9c48),
(0x3e06bc00, 0xb44197b9),
(0x3e0db800, 0x36ab54be),
(0x3e14ac00, 0xb6b41f80),
(0x3e1b9000, 0xb4f7f85c),
(0x3e226800, 0x36adb32e),
(0x3e293800, 0xb650e2f2),
(0x3e2ff800, 0x36c1c29e),
(0x3e36b000, 0x35fe719d),
(0x3e3d5c00, 0x3590210e),
(0x3e43fc00, 0x36819483),
(0x3e4a9400, 0xb6958c2f),
(0x3e511c00, 0x36f07f8b),
(0x3e57a000, 0xb6dac5fd),
(0x3e5e1400, 0x354e85b2),
(0x3e648000, 0xb5838656),
(0x3e6ae000, 0x3685ad3f),
(0x3e713800, 0x356dc55e),
(0x3e778400, 0x36b72f71),
(0x3e7dc800, 0x36436af2),
(0x3e820200, 0xb6d35a59),
(0x3e851a00, 0xb6d8ec63),
(0x3e882c00, 0x363f9ae5),
(0x3e8b3a00, 0x36e55d5d),
(0x3e8e4400, 0x36c60b4d),
(0x3e914a00, 0x34fde7bd),
(0x3e944a00, 0x36d09ef4),
(0x3e974800, 0xb6ea28f7),
(0x3e9a3e00, 0x36ecd4c4),
(0x3e9d3200, 0x36455694),
(0x3ea02200, 0xb6779796),
(0x3ea30c00, 0x363c21c6),
(0x3ea5f200, 0x36fcabbc),
(0x3ea8d600, 0xb693c690),
(0x3eabb400, 0xb60e8baa),
(0x3eae8e00, 0xb51029fe),
(0x3eb16400, 0x353cae72),
(0x3eb43600, 0x3601e9b1),
(0x3eb70400, 0x366aa2ba),
(0x3eb9ce00, 0x36bfb5df),
(0x3ebc9600, 0xb6d50116),
(0x3ebf5800, 0xb5f88faa),
(0x3ec21600, 0x368ed0f4),
(0x3ec4d200, 0xb64793ec),
(0x3ec78800, 0x36f439b3),
(0x3eca3c00, 0x36a0e109),
(0x3eccec00, 0x36ac08bf),
(0x3ecf9a00, 0xb6e09a03),
(0x3ed24200, 0x3410e5bb),
(0x3ed4e800, 0xb69b2b30),
(0x3ed78a00, 0xb6b66dc4),
(0x3eda2800, 0xb6084337),
(0x3edcc200, 0x36c4b499),
(0x3edf5a00, 0x3659da72),
(0x3ee1ee00, 0x36bd3e6d),
(0x3ee48000, 0xb6038656),
(0x3ee70e00, 0xb687a3d0),
(0x3ee99800, 0xb4c0ff8a),
(0x3eec2000, 0xb6c6d3af),
(0x3eeea400, 0xb6afd9f2),
(0x3ef12400, 0x3601a7c7),
(0x3ef3a200, 0x351875a2),
(0x3ef61c00, 0x36ce9234),
(0x3ef89400, 0x3675faf0),
(0x3efb0a00, 0xb6e02c7f),
(0x3efd7a00, 0x36c47bc8),
(0x3effea00, 0xb68fbd40),
(0x3f012b00, 0xb6d5a5a3),
(0x3f025f00, 0xb444adb2),
(0x3f039200, 0xb551f190),
(0x3f04c300, 0x36f4f573),
(0x3f05f400, 0xb6d1bdad),
(0x3f072200, 0x36985d1d),
(0x3f085000, 0xb6c61d2b),
(0x3f097c00, 0xb6e6a6c1),
(0x3f0aa600, 0x35f4bd35),
(0x3f0bcf00, 0x36abbd8a),
(0x3f0cf700, 0x36568cf9),
(0x3f0e1e00, 0xb67c11d8),
(0x3f0f4300, 0xb4a18fbf),
(0x3f106700, 0xb5cb9b55),
(0x3f118a00, 0xb6f28414),
(0x3f12ab00, 0xb6062ce1),
(0x3f13cb00, 0xb576bb27),
(0x3f14ea00, 0xb68013d5),
(0x3f160700, 0x369ed449),
(0x3f172400, 0xb6bc91c0),
(0x3f183f00, 0xb68ccb0f),
(0x3f195900, 0xb6cc6ede),
(0x3f1a7100, 0x3689d9ce),
(0x3f1b8900, 0xb684ab8c),
(0x3f1c9f00, 0x34d3562a),
(0x3f1db400, 0x36094000),
(0x3f1ec800, 0x359a9c56),
(0x3f1fdb00, 0xb60f65d2),
(0x3f20ec00, 0x36fe8467),
(0x3f21fd00, 0xb368318d),
(0x3f230c00, 0x36bc21c6),
(0x3f241b00, 0xb6c2e157),
(0x3f252800, 0xb67449f8),
(0x3f263400, 0xb64a0662),
(0x3f273f00, 0xb67dc915),
(0x3f284900, 0xb6c33fe9),
(0x3f295100, 0x36d265bc),
(0x3f2a5900, 0x360cf333),
(0x3f2b6000, 0xb6454982),
(0x3f2c6500, 0x36db5cd8),
(0x3f2d6a00, 0x34186b3e),
(0x3f2e6e00, 0xb6e2393f),
(0x3f2f7000, 0x35aa4906),
(0x3f307200, 0xb6d0bb87),
(0x3f317200, 0x35bfbe8e), /* Note C[128] is log2 */
]
const accurate_logs64 = [
(000000000000000000, 000000000000000000),
(0x3f7fe02a6b200000, 0xbd6f30ee07912df9),
(0x3f8fc0a8b1000000, 0xbd5fe0e183092c59),
(0x3f97b91b07d80000, 0xbd62772ab6c0559c),
(0x3f9f829b0e780000, 0x3d2980267c7e09e4),
(0x3fa39e87ba000000, 0xbd642a056fea4dfd),
(0x3fa77458f6340000, 0xbd62303b9cb0d5e1),
(0x3fab42dd71180000, 0x3d671bec28d14c7e),
(0x3faf0a30c0100000, 0x3d662a6617cc9717),
(0x3fb16536eea40000, 0xbd60a3e2f3b47d18),
(0x3fb341d7961c0000, 0xbd4717b6b33e44f8),
(0x3fb51b073f060000, 0x3d383f69278e686a),
(0x3fb6f0d28ae60000, 0xbd62968c836cc8c2),
(0x3fb8c345d6320000, 0xbd5937c294d2f567),
(0x3fba926d3a4a0000, 0x3d6aac6ca17a4554),
(0x3fbc5e548f5c0000, 0xbd4c5e7514f4083f),
(0x3fbe27076e2a0000, 0x3d6e5cbd3d50fffc),
(0x3fbfec9131dc0000, 0xbd354555d1ae6607),
(0x3fc0d77e7cd10000, 0xbd6c69a65a23a170),
(0x3fc1b72ad52f0000, 0x3d69e80a41811a39),
(0x3fc29552f8200000, 0xbd35b967f4471dfc),
(0x3fc371fc201f0000, 0xbd6c22f10c9a4ea8),
(0x3fc44d2b6ccb0000, 0x3d6f4799f4f6543e),
(0x3fc526e5e3a20000, 0xbd62f21746ff8a47),
(0x3fc5ff3070a80000, 0xbd6b0b0de3077d7e),
(0x3fc6d60fe71a0000, 0xbd56f1b955c4d1da),
(0x3fc7ab8902110000, 0xbd537b720e4a694b),
(0x3fc87fa065210000, 0xbd5b77b7effb7f41),
(0x3fc9525a9cf40000, 0x3d65ad1d904c1d4e),
(0x3fca23bc1fe30000, 0xbd62a739b23b93e1),
(0x3fcaf3c94e810000, 0xbd600349cc67f9b2),
(0x3fcbc286742e0000, 0xbd6cca75818c5dbc),
(0x3fcc8ff7c79b0000, 0xbd697794f689f843),
(0x3fcd5c216b500000, 0xbd611ba91bbca682),
(0x3fce27076e2b0000, 0xbd3a342c2af0003c),
(0x3fcef0adcbdc0000, 0x3d664d948637950e),
(0x3fcfb9186d5e0000, 0x3d5f1546aaa3361c),
(0x3fd0402594b50000, 0xbd67df928ec217a5),
(0x3fd0a324e2738000, 0x3d50e35f73f7a018),
(0x3fd1058bf9ae8000, 0xbd6a9573b02faa5a),
(0x3fd1675cabab8000, 0x3d630701ce63eab9),
(0x3fd1c898c1698000, 0x3d59fafbc68e7540),
(0x3fd22941fbcf8000, 0xbd3a6976f5eb0963),
(0x3fd2895a13de8000, 0x3d3a8d7ad24c13f0),
(0x3fd2e8e2bae10000, 0x3d5d309c2cc91a85),
(0x3fd347dd9a988000, 0xbd25594dd4c58092),
(0x3fd3a64c55698000, 0xbd6d0b1c68651946),
(0x3fd4043086868000, 0x3d63f1de86093efa),
(0x3fd4618bc21c8000, 0xbd609ec17a426426),
(0x3fd4be5f95778000, 0xbd3d7c92cd9ad824),
(0x3fd51aad872e0000, 0xbd3f4bd8db0a7cc1),
(0x3fd5767717458000, 0xbd62c9d5b2a49af9),
(0x3fd5d1bdbf580000, 0x3d4394a11b1c1ee4),
(0x3fd62c82f2ba0000, 0xbd6c356848506ead),
(0x3fd686c81e9b0000, 0x3d54aec442be1015),
(0x3fd6e08eaa2b8000, 0x3d60f1c609c98c6c),
(0x3fd739d7f6bc0000, 0xbd67fcb18ed9d603),
(0x3fd792a55fdd8000, 0xbd6c2ec1f512dc03),
(0x3fd7eaf83b828000, 0x3d67e1b259d2f3da),
(0x3fd842d1da1e8000, 0x3d462e927628cbc2),
(0x3fd89a3386c18000, 0xbd6ed2a52c73bf78),
(0x3fd8f11e87368000, 0xbd5d3881e8962a96),
(0x3fd947941c210000, 0x3d56faba4cdd147d),
(0x3fd99d9581180000, 0xbd5f753456d113b8),
(0x3fd9f323ecbf8000, 0x3d584bf2b68d766f),
(0x3fda484090e58000, 0x3d6d8515fe535b87),
(0x3fda9cec9a9a0000, 0x3d40931a909fea5e),
(0x3fdaf12932478000, 0xbd3e53bb31eed7a9),
(0x3fdb44f77bcc8000, 0x3d4ec5197ddb55d3),
(0x3fdb985896930000, 0x3d50fb598fb14f89),
(0x3fdbeb4d9da70000, 0x3d5b7bf7861d37ac),
(0x3fdc3dd7a7cd8000, 0x3d66a6b9d9e0a5bd),
(0x3fdc8ff7c79a8000, 0x3d5a21ac25d81ef3),
(0x3fdce1af0b860000, 0xbd48290905a86aa6),
(0x3fdd32fe7e010000, 0xbd542a9e21373414),
(0x3fdd83e7258a0000, 0x3d679f2828add176),
(0x3fddd46a04c20000, 0xbd6dafa08cecadb1),
(0x3fde24881a7c8000, 0xbd53d9e34270ba6b),
(0x3fde744261d68000, 0x3d3e1f8df68dbcf3),
(0x3fdec399d2468000, 0x3d49802eb9dca7e7),
(0x3fdf128f5faf0000, 0x3d3bb2cd720ec44c),
(0x3fdf6123fa700000, 0x3d645630a2b61e5b),
(0x3fdfaf588f790000, 0xbd49c24ca098362b),
(0x3fdffd2e08580000, 0xbd46cf54d05f9367),
(0x3fe02552a5a5c000, 0x3d60fec69c695d7f),
(0x3fe04bdf9da94000, 0xbd692d9a033eff75),
(0x3fe0723e5c1cc000, 0x3d6f404e57963891),
(0x3fe0986f4f574000, 0xbd55be8dc04ad601),
(0x3fe0be72e4254000, 0xbd657d49676844cc),
(0x3fe0e44985d1c000, 0x3d5917edd5cbbd2d),
(0x3fe109f39e2d4000, 0x3d592dfbc7d93617),
(0x3fe12f7195940000, 0xbd6043acfedce638),
(0x3fe154c3d2f4c000, 0x3d65e9a98f33a396),
(0x3fe179eabbd88000, 0x3d69a0bfc60e6fa0),
(0x3fe19ee6b467c000, 0x3d52dd98b97baef0),
(0x3fe1c3b81f714000, 0xbd3eda1b58389902),
(0x3fe1e85f5e704000, 0x3d1a07bd8b34be7c),
(0x3fe20cdcd192c000, 0xbd64926cafc2f08a),
(0x3fe23130d7bec000, 0xbd17afa4392f1ba7),
(0x3fe2555bce990000, 0xbd506987f78a4a5e),
(0x3fe2795e1289c000, 0xbd5dca290f81848d),
(0x3fe29d37fec2c000, 0xbd5eea6f465268b4),
(0x3fe2c0e9ed448000, 0x3d5d1772f5386374),
(0x3fe2e47436e40000, 0x3d334202a10c3491),
(0x3fe307d7334f0000, 0x3d60be1fb590a1f5),
(0x3fe32b1339120000, 0x3d6d71320556b67b),
(0x3fe34e289d9d0000, 0xbd6e2ce9146d277a),
(0x3fe37117b5474000, 0x3d4ed71774092113),
(0x3fe393e0d3564000, 0xbd65e6563bbd9fc9),
(0x3fe3b6844a000000, 0xbd3eea838909f3d3),
(0x3fe3d9026a714000, 0x3d66faa404263d0b),
(0x3fe3fb5b84d18000, 0xbd60bda4b162afa3),
(0x3fe41d8fe8468000, 0xbd5aa33736867a17),
(0x3fe43f9fe2f9c000, 0x3d5ccef4e4f736c2),
(0x3fe4618bc21c4000, 0x3d6ec27d0b7b37b3),
(0x3fe48353d1ea8000, 0x3d51bee7abd17660),
(0x3fe4a4f85db04000, 0xbd244fdd840b8591),
(0x3fe4c679afcd0000, 0xbd61c64e971322ce),
(0x3fe4e7d811b74000, 0x3d6bb09cb0985646),
(0x3fe50913cc018000, 0xbd6794b434c5a4f5),
(0x3fe52a2d265bc000, 0x3d46abb9df22bc57),
(0x3fe54b2467998000, 0x3d6497a915428b44),
(0x3fe56bf9d5b40000, 0xbd58cd7dc73bd194),
(0x3fe58cadb5cd8000, 0xbd49db3db43689b4),
(0x3fe5ad404c358000, 0x3d6f2cfb29aaa5f0),
(0x3fe5cdb1dc6c0000, 0x3d67648cf6e3c5d7),
(0x3fe5ee02a9240000, 0x3d667570d6095fd2),
(0x3fe60e32f4478000, 0x3d51b194f912b417),
(0x3fe62e42fefa4000, 0xbd48432a1b0e2634),
]
const desc32 : fltdesc(flt32, uint32, int32) = [
.explode = std.flt32explode,
.assem = std.flt32assem,
.horner = horner_polyu32,
.tobits = std.flt32bits,
.frombits = std.flt32frombits,
.sgnmask = (1 << 31),
.sig8mask = 0xffff0000, /* Mask to get 8 significant bits */
.sig8last = 16, /* Last bit kept when masking */
.emin = -126,
.emax = 127,
.precision = 24,
.inf = 0x7f800000,
.ninf = 0xff800000,
.nan = 0x7fc00000,
.logT1 = 0x3f707d5f, /* Just smaller than e^(-1/16) ~= 0.939413 */
.logT2 = 0x3f88415b, /* Just larger than e^(1/16) ~= 1.06449 */
.log1pT1 = 0xbd782a03, /* Just smaller than e^(-1/16) - 1 ~= -0.0605869 */
.log1pT2 = 0x3d8415ac, /* Just larger than e^(1/16) - 1 ~= 0.06449445 */
.T3exp = 26, /* Beyond 2^T3exp, 1 + x rounds to x */
.C = accurate_logs32[:],
.Ai = [ 0x3daaaac2 ][:], /* Coefficients for log(1 + f/F) */
.Bi = [ /* Coefficients for log(1 + f) in terms of a = 2f/(2 + f) */
0x3daaaaa9,
0x3c4d0095,
][:],
.Mtruncmask = 0xfffff000, /* Mask to get 12 significant bits */
]
const desc64 : fltdesc(flt64, uint64, int64) = [
.explode = std.flt64explode,
.assem = std.flt64assem,
.horner = horner_polyu64,
.tobits = std.flt64bits,
.frombits = std.flt64frombits,
.sgnmask = (1 << 63),
.sig8mask = 0xffffe00000000000, /* Mask to get 8 significant bits */
.sig8last = 45, /* Last bit kept when masking */
.emin = -1022,
.emax = 1023,
.precision = 53,
.inf = 0x7ff0000000000000,
.ninf = 0xfff0000000000000,
.nan = 0x7ff8000000000000,
.logT1 = 0x3fee0fabfbc702a3, /* Just smaller than e^(-1/16) ~= 0.939413 */
.logT2 = 0x3ff1082b577d34ee, /* Just larger than e^(1/16) ~= 1.06449 */
.log1pT1 = 0xbfaf0540428fd5c4, /* Just smaller than e^(-1/16) - 1 ~= -0.0605869 */
.log1pT2 = 0x3fb082b577d34ed8, /* Just larger than e^(1/16) - 1 ~= 0.06449445 */
.T3exp = 55, /* Beyond 2^T3exp, 1 + x rounds to x */
.C = accurate_logs64[:],
.Ai = [
0x3fb5555555550286,
0x3f8999a0bc712416,
][:],
.Bi = [
0x3fb55555555554e6,
0x3f89999999bac6d4,
0x3f62492307f1519f,
0x3f3c8034c85dfff0,
][:],
.Mtruncmask = 0xfffffffff0000000, /* Mask to get 24 significant bits */
]
const log32 = {x : flt32
-> loggen(x, desc32)
}
const log64 = {x : flt64
-> loggen(x, desc64)
}
generic loggen = {x : @f, d : fltdesc(@f, @u, @i) :: numeric,floating @f, numeric,integral @u, numeric,integral @i, roundable @f -> @i
var b = d.tobits(x)
var n : bool, e : @i, s : @u
(n, e, s) = d.explode(x)
/*
Special cases for NaN, +/- 0, < 0, inf, and 1. There are
certain exceptions (inexact, division by 0, &c) that
should be flagged in these cases, which we do not honor
currently. See [Tan90].
*/
if std.isnan(x)
-> d.frombits(d.nan)
elif (b & ~d.sgnmask == 0)
-> d.frombits(d.ninf)
elif n
-> d.frombits(d.nan)
elif (b == d.inf)
-> x
elif std.eq(x, (1.0 : @f))
-> (0.0 : @f)
;;
/* If x is close to 1, polynomial log1p(x - 1) will be sufficient */
if (d.logT1 < b && b < d.logT2)
-> procedure_2(x - (1.0 : @f), d)
;;
/*
Reduce x to 2^m * (F + f), with (F + f) in [1, 2), so
procedure_2's tables work. We also require that F have
only 8 significant bits.
*/
var m : @i, Y : @f, F : @f, f : @f
if e < d.emin
/* Normalize significand */
var first_1 = find_first1_64((s : uint64), (d.precision : int64))
var offset = (d.precision : @u) - 1 - (first_1 : @u)
s = s << offset
e = d.emin - offset
;;
m = e
Y = d.assem(false, 0, s)
if need_round_away(0, (s : uint64), (d.sig8last : int64))
F = d.frombits((d.tobits(Y) & d.sig8mask) + (1 << d.sig8last))
else
F = d.frombits(d.tobits(Y) & d.sig8mask)
;;
f = Y - F
-> procedure_1(m, F, f, Y, d)
}
const log1p32 = {x : flt32
-> log1pgen(x, desc32)
}
const log1p64 = {x : flt64
-> log1pgen(x, desc64)
}
generic log1pgen = {x : @f, d : fltdesc(@f, @u, @i) :: numeric,floating @f, numeric,integral @u, numeric,integral @i, roundable @f -> @i
var b = d.tobits(x)
var n, e, s
(n, e, s) = d.explode(x)
/*
Special cases for NaN, +/- 0, < 0, inf, and 1. There are
certain exceptions (inexact, division by 0, &c) that
should be flagged in these cases, which we do not honor
currently. See [Tan90].
*/
if std.isnan(x)
-> d.frombits(d.nan)
elif (b & ~d.sgnmask == 0)
-> x
elif std.eq(x, (-1.0 : @f))
-> d.frombits(d.nan | d.sgnmask)
elif x < (-1.0 : @f)
-> d.frombits(d.nan)
elif (b == d.inf)
-> x
;;
/* If x is small enough that 1 + x rounds to 1, return x */
if e < (-d.precision : @i)
-> x
;;
/* If x is close to 0, use polynomial */
if (n && b < d.log1pT1) || (!n && b < d.log1pT2)
-> procedure_2(x, d)
;;
/*
Reduce x m, F, f as in log case. However, since we're
approximating 1 + x, more care has to be taken (for
example: 1 + x might be infinity).
*/
var Y, m, F, f
if e > d.T3exp
Y = x
else
Y = (1.0 : @f) + x
;;
/*
y must be normal, otherwise x would have been -1 +
(subnormal), but that would round to -1.
*/
var ny, ey, sy
(ny, ey, sy) = d.explode(Y)
m = ey
Y = d.assem(ny, 0, sy)
if need_round_away(0, (sy : uint64), (d.sig8last : int64))
F = d.frombits((d.tobits(Y) & d.sig8mask) + (1 << d.sig8last))
else
F = d.frombits(d.tobits(Y) & d.sig8mask)
;;
/*
f is trickier to compute than in the exp case, because
the scale of the 1 is unknown near x.
*/
if m <= -2
f = Y - F
elif m <= d.precision - 1
f = (d.assem(false, -m, 0) - F) + scale2(x, -m)
else
f = (scale2(x, -m) - F) + d.assem(false, -m, 0)
;;
-> procedure_1(m, F, f, Y, d)
}
/* Approximate log(2^m * (F + f)) by tables */
generic procedure_1 = {m : @i, F : @f, f : @f, Y : @f, d : fltdesc(@f, @u, @i) :: numeric,floating @f, numeric,integral @u, numeric,integral @i, roundable @f -> @i
/*
We must compute log(2^m * (F + f)) = m log(2) + log(F)
+ log(1 + f/F). Only this last term need be approximated,
since log(2) and log(F) may be precomputed.
For computing log(1 + f/F), [Tan90] gives two alternatives.
We choose step 3', which requires floating-point division,
but allows us to save approximately 2.5 KiB of precomputed
values.
F is some 1 + j2^(-7), so first we compute j. Note that
j could actually be 128 (Ex: x = 0x4effac00.)
*/
var j
var nF, eF, sF
(nF, eF, sF) = d.explode(F)
if eF != 0
j = 128
else
j = 0x7f & (((d.sig8mask & sF) >> d.sig8last) - 0x80)
;;
var Cu_hi, Cu_lo, log2_hi, log2_lo
(Cu_hi, Cu_lo) = d.C[j]
(log2_hi, log2_lo) = d.C[128]
var L_hi = (m : @f) * d.frombits(log2_hi) + d.frombits(Cu_hi)
var L_lo = (m : @f) * d.frombits(log2_lo) + d.frombits(Cu_lo)
var u = ((2.0 : @f) * f)/(Y + F)
var v = u * u
var q = u * v * d.horner(v, d.Ai)
-> L_hi + (u + (q + L_lo))
}
/* Approximate log1p by polynomial */
generic procedure_2 = {f : @f, d : fltdesc(@f, @u, @i) :: numeric,floating @f, numeric,integral @u, numeric,integral @i, roundable @f -> @i
var g = (1.0 : @f)/((2.0 : @f) + f)
var u = (2.0 : @f) * f * g
var v = u * u
var q = u * v * d.horner(v, d.Bi)
/*
1 / (2 + f) in working precision was good enough for the
polynomial evaluation, but to complete the approximation
we need to add 2f/(2 + f) with higher precision than
working. So we go back and compute better, split u.
*/
var u1 = d.frombits(d.Mtruncmask & d.tobits(u))
var f1 = d.frombits(d.Mtruncmask & d.tobits(f))
var f2 = f - f1
var u2 = (((2.0 : @f) * (f - u1) - u1 * f1) - u1 * f2) * g
-> u1 + (u2 + q)
}