shithub: mc

Download patch

ref: 4aa0e6c6b8a1a23b144b0382d6156aaa55b2c7fd
parent: ce931f082769f332e545cc477bc85c2bbca71ac2
author: S. Gilles <sgilles@math.umd.edu>
date: Thu May 24 05:02:45 EDT 2018

Implement powr.

This is an attempt to extend the log algorithm of [Tan90] to compute
x^y. I don't intend to keep this algorithm for long, since I didn't
succeed very well. It's tremendously slow (worse even than mpfr!),
and has some truly terrible edge cases.

For example, computing pow(0x3f7ff7f3, 0xc7b58adf) (that is,
(0.99987715)^(-92949.74)) returns infinity, when it should be
0x47b1d362 (91046.765), which is a dozen orders of magnitude below
the infinity border.

In more pedestrian cases, errors of up to ~16 ulps can be observed.
For example, powr(0x3f80a83e, 0xc65492ba) = (1.0051343)^(-13604.682)
should return 0x0d3304a3, but it gives 0x0d3304b4.

--- a/lib/math/bld.sub
+++ b/lib/math/bld.sub
@@ -18,6 +18,9 @@
 	# polynomial evaluation methods
 	poly-impl.myr
 
+	# x^y
+	powr-impl.myr
+
 	# scalb (multiply x by 2^m)
 	scale2-impl.myr
 
--- a/lib/math/fpmath.myr
+++ b/lib/math/fpmath.myr
@@ -18,6 +18,9 @@
 		horner_poly : (x : @f, a : @f[:] -> @f)
 		horner_polyu : (x : @f, a : @u[:] -> @f)
 
+		/* powr-impl */
+		powr : (x : @f, y : @f -> @f)
+
 		/* scale2-impl */
 		scale2 : (x : @f, m : @i -> @f)
 
@@ -83,6 +86,8 @@
 	horner_poly = {x, a; -> horner_poly32(x, a)}
 	horner_polyu = {x, a; -> horner_polyu32(x, a)}
 
+	powr = {x, y; -> powr32(x, y)}
+
 	scale2 = {x, m; -> scale232(x, m)}
 
 	sqrt = {x; -> sqrt32(x)}
@@ -108,6 +113,8 @@
 	horner_poly = {x, a; -> horner_poly64(x, a)}
 	horner_polyu = {x, a; -> horner_polyu64(x, a)}
 
+	powr = {x, y; -> powr64(x, y)}
+
 	scale2 = {x, m; -> scale264(x, m)}
 
 	sqrt = {x; -> sqrt64(x)}
@@ -143,6 +150,9 @@
 
 extern const horner_polyu32 : (x : flt32, a : uint32[:] -> flt32)
 extern const horner_polyu64 : (x : flt64, a : uint64[:] -> flt64)
+
+extern const powr32 : (x : flt32, y : flt32 -> flt32)
+extern const powr64 : (x : flt64, y : flt64 -> flt64)
 
 extern const scale232 : (x : flt32, m : int32 -> flt32)
 extern const scale264 : (x : flt64, m : int64 -> flt64)
--- a/lib/math/log-impl.myr
+++ b/lib/math/log-impl.myr
@@ -12,6 +12,10 @@
 
 	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)
@@ -43,7 +47,7 @@
 	T3exp : @u
 
 	/* For procedure 1 */
-	C : (@u, @u)[129]
+	C : (@u, @u)[:]
 	Ai : @u[:]
 
 	/* For procedure 2 */
@@ -51,6 +55,271 @@
 	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,
@@ -71,137 +340,7 @@
 	.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 representations for log(1 + j/2^7), all j */
-		(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 */
-	],
+	.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,
@@ -230,137 +369,7 @@
 	.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 = [
-		(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),
-	],
+	.C = accurate_logs64[:],
 	.Ai = [
 		0x3fb5555555550286,
 		0x3f8999a0bc712416,
--- /dev/null
+++ b/lib/math/powr-impl.myr
@@ -1,0 +1,406 @@
+use std
+
+use "fpmath"
+use "log-impl"
+use "util"
+
+/*
+   This is an implementation of powr, not pow, so the special cases
+   are tailored more closely to the mathematical x^y = e^(y * log(x))
+   than to historical C implementations (pow was aligned to the C99
+   standard, which was aligned to codify existing practice).
+
+   Even then, some parts of the powr specification are unclear. For
+   example, IEEE 754-2008 does not specify what powr(infty, y) must
+   return when y is not 0.0 (an erratum was planned in 2010, but
+   does not appear to have been released as of 2018).
+
+   As a note: unlike many other functions in this library, there
+   has been no serious analysis of the accuracy and speed of this
+   particular implementation. Interested observers wishing to improve
+   this library will probably find this file goldmine of mistakes,
+   both theoretical and practical.
+ */
+pkg math =
+	pkglocal const powr32 : (x : flt32, y : flt32 -> flt32)
+	pkglocal const powr64 : (x : flt64, y : flt64 -> flt64)
+;;
+
+type fltdesc(@f, @u, @i) = struct
+	explode : (f : @f -> (bool, @i, @u))
+	assem : (n : bool, e : @i, s : @u -> @f)
+	tobits : (f : @f -> @u)
+	frombits : (u : @u -> @f)
+	nan : @u
+	inf : @u
+	expmask : @u
+	precision : @u
+	emax : @i
+	emin : @i
+	sgnmask : @u
+	sig8mask : @u
+	sig8last : @u
+	split_prec_mask : @u
+	split_prec_mask2 : @u
+	C : (@u, @u)[:]
+	eps_inf_border : @u
+	eps_zero_border : @u
+	exp_inf_border : @u
+	exp_zero_border : @u
+	exp_subnormal_border : @u
+	itercount : @u
+;;
+
+const desc32 : fltdesc(flt32, uint32, int32) =  [
+	.explode = std.flt32explode,
+	.assem = std.flt32assem,
+	.tobits = std.flt32bits,
+	.frombits = std.flt32frombits,
+	.nan = 0x7fc00000,
+	.inf = 0x7f800000,
+	.expmask = 0x7f800000, /* mask to detect inf or NaN (inf, repeated for clarity) */
+	.precision = 24,
+	.emax = 127,
+	.emin = -126,
+	.sgnmask = 1 << 31,
+	.sig8mask = 0xffff0000, /* Mask to get 8 significant bits */
+	.sig8last = 16, /* Last bit kept when masking */
+	.split_prec_mask = 0xffff0000, /* 16 trailing zeros */
+	.split_prec_mask2 = 0xfffff000, /* 12 trailing zeros */
+	.C = accurate_logs32[0:130], /* See log-impl.myr */
+	.eps_inf_border = 0x4eb00f34, /* maximal y st. (1.00..1)^y < oo */
+	.eps_zero_border = 0x4ecff1b4, /* minimal y st. (0.99..9)^y > 0 */
+	.exp_inf_border = 0x42b17218, /* maximal y such that e^y < oo */
+	.exp_zero_border = 0xc2cff1b4, /* minimal y such that e^y > 0 */
+	.exp_subnormal_border = 0xc2aeac50, /* minimal y such that e^y is normal */
+	.itercount = 4, /* How many iterations of Taylor series for (1 + f)^y' */
+]
+
+const desc64 : fltdesc(flt64, uint64, int64) =  [
+	.explode = std.flt64explode,
+	.assem = std.flt64assem,
+	.tobits = std.flt64bits,
+	.frombits = std.flt64frombits,
+	.nan = 0x7ff8000000000000,
+	.inf = 0x7ff0000000000000,
+	.expmask = 0x7ff0000000000000,
+	.precision = 53,
+	.emax = 1023,
+	.emin = -1022,
+	.sgnmask = 1 << 63,
+	.sig8mask = 0xffffe00000000000, /* Mask to get 8 significant bits */
+	.sig8last = 45, /* Last bit kept when masking */
+	.split_prec_mask = 0xffffff0000000000, /* 40 trailing zeroes */
+	.split_prec_mask2 = 0xfffffffffffc0000, /* 18 trailing zeroes */
+	.C = accurate_logs64[0:130], /* See log-impl.myr */
+	.eps_inf_border = 0x43d628b76e3a7b61, /* maximal y st. (1.00..1)^y < oo */
+	.eps_zero_border = 0x43d74e9c65eceee0, /*  minimal y st. (0.99..9)^y > 0 */
+	.exp_inf_border = 0x40862e42fefa39ef, /* maximal y such that e^y < oo */
+	.exp_zero_border = 0xc0874910d52d3052, /* minimal y such that e^y > 0 */
+	.exp_subnormal_border = 0xc086232bdd7abcd2, /* minimal y such that e^y is normal */
+	.itercount = 8,
+]
+
+const powr32 = {x : flt32, y : flt32
+	-> powrgen(x, y, desc32)
+}
+
+const powr64 = {x : flt64, y : flt64
+	-> powrgen(x, y, desc64)
+}
+
+generic powrgen = {x : @f, y : @f, d : fltdesc(@f, @u, @i) :: numeric,floating,std.equatable @f, numeric,integral @u, numeric,integral @i
+	var xb, yb
+	xb = d.tobits(x)
+	yb = d.tobits(y)
+
+	var xn : bool, xe : @i, xs : @u
+	var yn : bool, ye : @i, ys : @u
+	(xn, xe, xs) = d.explode(x)
+	(yn, ye, ys) = d.explode(y)
+
+	/*
+	   Special cases. Note we do not follow IEEE exceptions.
+	 */
+	if std.isnan(x) || std.isnan(y)
+		/* Propagate NaN */
+		-> d.frombits(d.nan)
+	elif (xb & ~d.sgnmask == 0)
+		if (yb & ~d.sgnmask == 0)
+			/* 0^0 is undefined. */
+			-> d.frombits(d.nan)
+		elif yn
+			/* 0^(< 0) is infinity */
+			-> d.frombits(d.inf)
+		else
+			/* otherwise, 0^y = 0. */
+			-> (0.0 : @f)
+		;;
+	elif xn
+		/*
+		   (< 0)^(anything) is undefined. This comes from
+		   thinking of floating-point numbers as representing
+		   small ranges of real numbers. If you really want
+		   to compute (-1.23)^5, use pown.
+		 */
+		-> d.frombits(d.nan)
+	elif (xb & ~d.sgnmask == d.inf)
+		if (yb & ~d.sgnmask == 0)
+			/* oo^0 is undefined */
+			-> d.frombits(d.nan)
+		elif yn
+			/* +/-oo^(< 0) is +/-0 */
+			-> d.assem(xn, 0, 0)
+		elif xn
+			/* (-oo)^(anything) is undefined */
+			-> d.frombits(d.nan)
+		else
+			/* oo^(> 0) is oo */
+			-> d.frombits(d.inf)
+		;;
+	elif std.eq(y, (1.0 : @f))
+		/* x^1 = x */
+		-> x
+	elif yb & ~d.sgnmask == 0
+		/* (finite, positive)^0 = 1 */
+		-> (1.0 : @f)
+	elif std.eq(x, (1.0 : @f))
+		if yb & ~d.sgnmask == d.inf
+			/* 1^oo is undefined */
+			-> d.frombits(d.nan)
+		else
+			/* 1^(finite, positive) = 1 */
+			-> (1.0 : @f)
+		;;
+	elif yb & ~d.sgnmask == d.inf
+		if xe < 0
+			/* (0 < x < 1)^oo = 0 */
+			-> (0.0 : @f)
+		else
+			/* (x > 1)^oo = oo */
+			-> d.frombits(d.inf)
+		;;
+	;;
+
+	/* Normalize x and y */
+	if xe < d.emin
+		var first_1 = find_first1_64((xs : uint64), (d.precision : int64))
+		var offset = (d.precision : @u) - 1 - (first_1 : @u)
+		xs = xs << offset
+		xe = d.emin - offset
+	;;
+
+	if ye < d.emin
+		var first_1 = find_first1_64((ys : uint64), (d.precision : int64))
+		var offset = (d.precision : @u) - 1 - (first_1 : @u)
+		ys = ys << offset
+		ye = d.emin - offset
+	;;
+
+	/*
+           Split x into 2^N * F * (1 + f), with F = 1 + j/128 (some
+           j) and f tiny. Compute F naively by truncation. Compute
+           f via f = (x' - 1 - F)/(1 + F), where 1/(1 + F) is
+           precomputed and x' is x/2^N. 128 is chosen so that we
+           can borrow some constants from log-impl.myr.
+
+           [Tan90] hints at a method of computing x^y which may be
+           comparable to this approach, but which is unfortunately
+           has not been elaborated on (as far as I can discover).
+	 */
+	var N = xe
+	var j, F, Fn, Fe, Fs
+	var xprime = d.assem(false, 0, xs)
+
+	if need_round_away(0, (xs : uint64), (d.sig8last : int64))
+		F = d.frombits((d.tobits(xprime) & d.sig8mask) + (1 << d.sig8last))
+	else
+		F = d.frombits(d.tobits(xprime) & d.sig8mask)
+	;;
+
+	(Fn, Fe, Fs) = d.explode(F)
+
+	if Fe != 0
+		j = 128
+	else
+		j = 0x7f & ((d.sig8mask & Fs) >> d.sig8last)
+	;;
+
+	var f = (xprime - F)/F
+
+	/*
+	   y could actually be above integer infinity, in which
+	   case x^y is most certainly infinity of 0. More importantly,
+	   we can't safely compute M (below).
+	 */
+	if x > (1.0 : @f)
+		if y > d.frombits(d.eps_inf_border)
+			-> d.frombits(d.inf)
+		elif -y > d.frombits(d.eps_inf_border)
+			-> (0.0 : @f)
+		;;
+	elif x < (1.0 : @f)
+		if y > d.frombits(d.eps_zero_border) && x < (1.0 : @f)
+			-> (0.0 : @f)
+		elif -y > d.frombits(d.eps_zero_border) && x < (1.0 : @f)
+			-> d.frombits(d.inf)
+		;;
+	;;
+
+	/* Split y into M + y', with |y'| <= 0.5 and M an integer */
+	var M = floor(y)
+	var yprime = y - M
+	if yprime > (0.5 : @f)
+		M += (1.0 : @f)
+		yprime = y - M
+	elif yprime < (-0.5 : @f)
+		M -= (1.0: @f)
+		yprime = y - M
+	;;
+
+	/*
+	   We'll multiply y' by log(2) and try to keep extra
+	   precision, so we need to split y'. Since the high word
+	   of C has 24 - 10 = 14 significant bits (53 - 16 = 37 in
+	   flt64 case), we ensure 15 (39) trailing zeroes in
+	   yprime_hi.  (We also need this for y'*N, M, &c).
+	 */
+	var yprime_hi = d.frombits(d.tobits(yprime) & d.split_prec_mask)
+	var yprime_lo = yprime - yprime_hi
+	var yprimeN_hi = d.frombits(d.tobits((N : @f) * yprime) & d.split_prec_mask)
+	var yprimeN_lo = fma((N : @f),  yprime, -yprimeN_hi)
+	var M_hi = d.frombits(d.tobits(M) & d.split_prec_mask)
+	var M_lo = M - M_hi
+
+	/* 
+	   At this point, we've built out
+	   
+	       x^y = [ 2^N * F * (1 + f) ]^(M + y')
+	
+	   where N, M are integers, F is well-known, and f, y' are
+	   tiny. So we can get to computing
+
+	        /-1-\     /-------------------2--------------------------\     /-3--\
+	       2^(N*M) * exp(log(F)*y' + log2*N*y' + log(F)*M + M*log(1+f)) * (1+f)^y'
+
+	   where 1 can be handled by scale2, 2 we can mostly fake
+	   by sticking high-precision values for log(F) and log(2)
+	   through exp(), and 3 is composed of small numbers,
+	   therefore can be reasonably approximated by a Taylor
+	   expansion.
+	 */
+
+	/* t2 */
+	var log2_lo, log2_hi, Cu_hi, Cu_lo
+	(log2_hi, log2_lo) = d.C[128]
+	(Cu_hi, Cu_lo) = d.C[j]
+
+	var es : @f[20]
+	std.slfill(es[:], (0.0 : @f))
+
+	/* log(F) * y' */
+	es[0] = d.frombits(Cu_hi) * yprime_hi
+	es[1] = d.frombits(Cu_lo) * yprime_hi
+	es[2] = d.frombits(Cu_hi) * yprime_lo
+	es[3] = d.frombits(Cu_lo) * yprime_lo
+
+	/* log(2) * N * y' */
+	es[4] = d.frombits(log2_hi) * yprimeN_hi
+	es[5] = d.frombits(log2_lo) * yprimeN_hi
+	es[6] = d.frombits(log2_hi) * yprimeN_lo
+	es[7] = d.frombits(log2_lo) * yprimeN_lo
+
+	/* log(F) * M */
+	es[8] = d.frombits(Cu_hi) * M_hi
+	es[9] = d.frombits(Cu_lo) * M_hi
+	es[10] = d.frombits(Cu_hi) * M_lo
+	es[11] = d.frombits(Cu_lo) * M_lo
+
+	/* log(1 + f) * M */
+	var lf = log1p(f)
+	var lf_hi = d.frombits(d.tobits(lf) & d.split_prec_mask)
+	var lf_lo = lf - lf_hi
+	es[12] = lf_hi * M_hi
+	es[13] = lf_lo * M_hi
+	es[14] = lf_hi * M_lo
+	es[15] = lf_lo * M_lo
+
+	/*
+	   The correct way to handle this would be to compare
+	   magnitudes of eis and parenthesize the additions correctly.
+	   We take the cheap way out.
+	 */
+	var exp_hi = priest_sum(es[0:16])
+
+	/*
+	   We would like to just compute exp(exp_hi) * exp(exp_lo).
+	   However, if that takes us into subnormal territory, yet
+	   N * M is large, that will throw away a few bits of
+	   information. We can correct for this by adding in a few
+	   copies of P*log(2), then subtract off P when we compute
+	   scale2() at the end.
+
+	   We also have to be careful that P doesn't have too many
+	   significant bits, otherwise we throw away some information
+	   of log2_hi.
+	 */
+	var P = -rn(exp_hi / d.frombits(log2_hi))
+	var P_f = (P : @f)
+	P_f = d.frombits(d.tobits(P_f) & d.split_prec_mask2)
+	P = rn(P_f)
+
+	es[16] = P_f * d.frombits(log2_hi)
+	es[17] = P_f * d.frombits(log2_lo)
+	exp_hi = priest_sum(es[0:18])
+	es[18] = -exp_hi
+	var exp_lo = priest_sum(es[0:19])
+
+
+	var t2 = exp(exp_hi) * exp(exp_lo)
+
+	/*
+	   t3: Abbreviated Taylor expansion for (1 + f)^y' - 1.
+	   Since f is on the order of 2^-7 (and y' is on the order
+	   of 2^-1), we need to go up to f^3 for single-precision,
+	   and f^7 for double. We can then compute (1 + t3) * t2
+
+	   The expansion is \Sum_{k=1}^{\infty} {y' \choose k} x^k
+	 */
+	var terms : @f[10] = [
+		(0.0 : @f),  (0.0 : @f),  (0.0 : @f),  (0.0 : @f),  (0.0 : @f),
+		(0.0 : @f),  (0.0 : @f),  (0.0 : @f),  (0.0 : @f),  (0.0 : @f),
+	]
+	var current = (1.0 : @f)
+	for var j = 0; j <= d.itercount; ++j
+		current = current * f * (yprime - (j : @f)) / ((j : @f) + (1.0 : @f))
+		terms[j] = current
+	;;
+	var t3 = priest_sum(terms[0:d.itercount + 1])
+
+	var total_exp_f = (N : @f) * M - (P : @f)
+	if total_exp_f > ((d.emax - d.emin + d.precision + 1) : @f)
+		-> d.frombits(d.inf)
+	elif total_exp_f < -((d.emax - d.emin + d.precision + 1) : @f)
+		-> (0.0 : @f)
+	;;
+
+	/*
+	   Pull t2's exponent out so that we don't hit subnormal
+	   calculation with the t3 multiplication
+	 */
+	var t2n, t2e, t2s
+	(t2n, t2e, t2s) = d.explode(t2)
+
+	if t2e < d.emin
+		var t2_first_1 = find_first1_64((t2s : uint64), (d.precision : int64))
+		var t2_offset = (d.precision : @u) - 1 - (t2_first_1 : @u)
+		t2s = t2s << t2_offset
+		t2e = d.emin - (t2_offset : @i)
+	;;
+
+	t2 = d.assem(t2n, 0, t2s)
+	P -= t2e
+
+	var base = fma(t2, t3, t2)
+	-> scale2(base, N * rn(M) - P)
+}
--- a/lib/math/references
+++ b/lib/math/references
@@ -6,6 +6,10 @@
 Science 351 (1 2006), pp. 101–110. doi:
 https://doi.org/10.1016/j.tcs.2005.09.056.
 
+[Mar00]
+Peter Markstein. IA-64 and elementary functions : speed and precision.
+Upper Saddle River, NJ: Prentice Hall, 2000. isbn: 9780130183484.
+
 [Mul+10]
 Jean-Michel Muller et al. Handbook of floating-point arithmetic.
 Boston: Birkhäuser, 2010. isbn: 9780817647049.
--- a/lib/math/scale2-impl.myr
+++ b/lib/math/scale2-impl.myr
@@ -41,8 +41,11 @@
 				sprime++
 			;;
 			eprime = emin - 1
+		elif e + m < emin - p - 2
+			sprime = 0
+			eprime = emin - 1
 		elif e + m < emin
-			sprime = s >> (emin - m - e)
+			sprime = s >> ((emin - m - e) : @u)
 			if need_round_away(0, (s : uint64), ((emin - m - e) : int64))
 				sprime++
 			;;
--- a/lib/math/sqrt-impl.myr
+++ b/lib/math/sqrt-impl.myr
@@ -38,7 +38,7 @@
 
    In the flt64 case, we need only one more iteration.
  */
-const ab32 : (uint32, uint32)[:] = [
+const ab32 : (uint32, uint32)[7] = [
 	(0x3f800000, 0x3f800000), /* Nothing should ever get normalized to < 1.0 */
 	(0x3fa66666, 0x3f6f30ae), /* [1.0,  1.3 ) -> 0.9343365431 */
 	(0x3fd9999a, 0x3f5173ca), /* [1.3,  1.7 ) -> 0.8181730509 */
@@ -46,9 +46,9 @@
 	(0x40333333, 0x3f215342), /* [2.25, 2.8 ) -> 0.6301766634 */
 	(0x4059999a, 0x3f118e0e), /* [2.8,  3.4 ) -> 0.5685738325 */
 	(0x40800000, 0x3f053049), /* [3.4,  4.0 ) -> 0.520268023  */
-][:]
+]
 
-const ab64 : (uint64, uint64)[:] = [
+const ab64 : (uint64, uint64)[8] = [
 	(0x3ff0000000000000, 0x3ff0000000000000), /* < 1.0 */
 	(0x3ff3333333333333, 0x3fee892ce1608cbc), /* [1.0,  1.2)  -> 0.954245033445111356940060431953 */
 	(0x3ff6666666666666, 0x3fec1513a2184094), /* [1.2,  1.4)  -> 0.877572838393478438234751592972 */
@@ -57,7 +57,7 @@
 	(0x400599999999999a, 0x3fe47717c17cd34f), /* [2.2,  2.7)  -> 0.639537694840876969060161627567 */
 	(0x400b333333333333, 0x3fe258df212a8e9a), /* [2.7,  3.4)  -> 0.573348583963212421465982515656 */
 	(0x4010000000000000, 0x3fe0a5989f2dc59a), /* [3.4,  4.0)  -> 0.520214377304159869552790951275 */
-][:]
+]
 
 const desc32 : fltdesc(flt32, uint32, int32) =  [
 	.explode = std.flt32explode,
@@ -70,7 +70,7 @@
 	.emax = 128,
 	.normmask = 1 << 23,
 	.sgnmask = 1 << 31,
-	.ab = ab32,
+	.ab = ab32[0:7],
 	.iterlim = 3,
 ]
 
@@ -85,7 +85,7 @@
 	.emax = 1024,
 	.normmask = 1 << 52,
 	.sgnmask = 1 << 63,
-	.ab = ab64,
+	.ab = ab64[0:8],
 	.iterlim = 4,
 ]
 
@@ -174,11 +174,11 @@
 		var r_plus_ulp : @f = d.frombits(d.tobits(r) + 1)
 		var r_minus_ulp : @f = d.frombits(d.tobits(r) - 1)
 
-		var delta_1 = d.fma(r, r_minus_ulp, -1.0 * f)
+		var delta_1 = d.fma(r, r_minus_ulp, -1.0 * x)
 		if d.tobits(delta_1) & d.sgnmask == 0
 			r = r_minus_ulp
 		else
-			var delta_2 = d.fma(r, r_plus_ulp, -1.0 * f)
+			var delta_2 = d.fma(r, r_plus_ulp, -1.0 * x)
 			if d.tobits(delta_2) & d.sgnmask != 0
 				r = r_plus_ulp
 			else
--- /dev/null
+++ b/lib/math/test/powr-impl.myr
@@ -1,0 +1,76 @@
+use std
+use math
+use testr
+
+const main = {
+	math.fptrap(false)
+	testr.run([
+		[.name="powr-01", .fn = powr01],
+		[.name="powr-02", .fn = powr02],
+		[.name="powr-03", .fn = powr03],
+	][:])
+}
+
+const powr01 = {c
+	var inputs : (uint32, uint32, uint32)[:] = [
+		(0x08a38749, 0x2ffb67c0, 0x3f7fffff),
+		(0x01433ed5, 0x367caeda, 0x3f7feaba),
+		(0x7112fd5b, 0x7509b252, 0x7f800000),
+		(0x22b5f461, 0xc7335035, 0x7f800000),
+		(0x29529847, 0x43c6b361, 0x00000000),
+		(0x3fc1cc03, 0x64eb4c95, 0x7f800000),
+		(0x653f944a, 0xbf7c2388, 0x1a3c784b),
+		(0x545ba67c, 0xc0c7e947, 0x00000000),
+		(0x3fca6b0d, 0x44ff18e0, 0x7f800000),
+		(0x3f74c7a7, 0x44feae20, 0x000265c6),
+		(0x3f7ebd6c, 0xc5587884, 0x4bc9ab07),
+	][:]
+
+	for (x, y, z) : inputs
+		var xf : flt32 = std.flt32frombits(x)
+		var yf : flt32 = std.flt32frombits(y)
+		var zf : flt32 = std.flt32frombits(z)
+		var rf = math.powr(xf, yf)
+		testr.check(c, rf == zf,
+			"powr(0x{b=16,w=8,p=0}, 0x{b=16,w=8,p=0}) should be 0x{b=16,w=8,p=0}, was 0x{b=16,w=8,p=0}",
+			x, y, z, std.flt32bits(rf))
+	;;
+}
+
+const powr02 = {c
+	var inputs : (uint64, uint64, uint64)[:] = [
+		(0x0000000000000000, 0x0000000000000000, 0x0000000000000000),
+	][:]
+
+	for (x, y, z) : inputs
+		var xf : flt64 = std.flt64frombits(x)
+		var yf : flt64 = std.flt64frombits(y)
+		var zf : flt64 = std.flt64frombits(z)
+		var rf = math.powr(xf, yf)
+		testr.check(c, rf == zf,
+			"powr(0x{b=16,w=16,p=0}, 0x{b=16,w=16,p=0}) should be 0x{b=16,w=16,p=0}, was 0x{b=16,w=16,p=0}",
+			x, y, z, std.flt64bits(rf))
+	;;
+}
+
+const powr03 = {c
+	var inputs : (uint32, uint32, uint32, uint32)[:] = [
+		(0x1bd2244e, 0x3a647973, 0x3f7535a1, 0x3f7535a0),
+		(0x3f264a46, 0x423c927a, 0x30c9b8d3, 0x30c9b8d4),
+		(0x61fb73d0, 0xbfd2666c, 0x06c539f6, 0x06c539f7),
+		(0x3bbd11f6, 0x3cc159b1, 0x3f62ac1b, 0x3f62ac1a),
+		(0x3f7ca5b7, 0xc309857a, 0x40c41bbf, 0x40c41bc0),
+		(0x3f6a1a65, 0x43e16065, 0x226731e2, 0x226731e3),
+	][:]
+
+	for (x, y, z_perfect, z_accepted) : inputs
+		var xf : flt32 = std.flt32frombits(x)
+		var yf : flt32 = std.flt32frombits(y)
+		var zf_perfect : flt32 = std.flt32frombits(z_perfect)
+		var zf_accepted : flt32 = std.flt32frombits(z_accepted)
+		var rf = math.powr(xf, yf)
+		testr.check(c, rf == zf_perfect || rf == zf_accepted,
+			"powr(0x{b=16,w=8,p=0}, 0x{b=16,w=8,p=0}) should be 0x{b=16,w=8,p=0}, will also accept 0x{b=16,w=8,p=0}, was 0x{b=16,w=8,p=0}",
+			x, y, z_perfect, z_accepted, std.flt32bits(rf))
+	;;
+}
--- a/lib/math/test/scale2-impl.myr
+++ b/lib/math/test/scale2-impl.myr
@@ -13,6 +13,7 @@
 
 const scale201 = {c
 	var inputsf : (flt32, int32, flt32)[:] = [
+		(0.000000011971715, -246, 0.0),
 		(0.0, 1, 0.0),
 		(-0.0, 2, -0.0),
 		(1.0, 3, 8.0),
@@ -25,7 +26,9 @@
 	][:]
 
 	for (f, m, g) : inputsf
-		testr.eq(c, math.scale2(f, m), g)
+		var r = math.scale2(f, m)
+		testr.check(c, r == g, "scale2(0x{w=8,b=16,p=0}, {}) should be 0x{w=8,b=16,p=0}, was 0x{w=8,b=16,p=0}",
+		std.flt32bits(f), m, std.flt32bits(g), std.flt32bits(r))
 	;;
 }
 
--- a/lib/math/trunc-impl.myr
+++ b/lib/math/trunc-impl.myr
@@ -33,10 +33,10 @@
 	if n
 		var fractional_mask = Flt32SigMask >> (e : uint32)
 		if s & fractional_mask == 0
-			-> f
+			-> x
 		else
 			/* Turns out the packing of exp and sig is useful */
-			var u : uint32 = std.flt32bits(f) & ~fractional_mask
+			var u : uint32 = std.flt32bits(x) & ~fractional_mask
 			u += ((1 << 23) >> (e : uint32))
 			-> std.flt32frombits(u)
 		;;