/* $OpenBSD: s_log1p.S,v 1.6 2018/07/03 22:43:34 mortimer Exp $ */
/*
* Written by J.T. Conklin .
* Public domain.
*/
/*
* Modified by Lex Wennmacher
* Still public domain.
*/
#include
#include "abi.h"
/*
* The log1p() function is provided to compute an accurate value of
* log(1 + x), even for tiny values of x. The i387 FPU provides the
* fyl2xp1 instruction for this purpose. However, the range of this
* instruction is limited to:
* -(1 - (sqrt(2) / 2)) <= x <= sqrt(2) - 1
* -0.292893 <= x <= 0.414214
* at least on older processor versions.
*
* log1p() is implemented by testing the range of the argument.
* If it is appropriate for fyl2xp1, this instruction is used.
* Else, we compute log1p(x) = ln(2)*ld(1 + x) the traditional way
* (using fyl2x).
*
* The range testing costs speed, but as the rationale for the very
* existence of this function is accuracy, we accept that.
*
* In order to reduce the cost for testing the range, we check if
* the argument is in the range
* -0.25 <= x <= 0.25
* which can be done with just one conditional branch. If x is
* inside this range, we use fyl2xp1. Outside of this range,
* the use of fyl2x is accurate enough.
*
*/
ENTRY(log1p)
RETGUARD_SETUP(log1p, r11)
XMM_ONE_ARG_DOUBLE_PROLOGUE
fldl ARG_DOUBLE_ONE
fabs
fld1 /* ... x 1 */
fadd %st(0) /* ... x 2 */
fadd %st(0) /* ... x 4 */
fld1 /* ... 4 1 */
fdivp /* ... x 0.25 */
fcompp
fnstsw %ax
andb $69,%ah
jne use_fyl2x
jmp use_fyl2xp1
.align 4,0xcc
use_fyl2x:
fldln2
fldl ARG_DOUBLE_ONE
fld1
faddp
fyl2x
XMM_DOUBLE_EPILOGUE
RETGUARD_CHECK(log1p, r11)
ret
.align 4,0xcc
use_fyl2xp1:
fldln2
fldl ARG_DOUBLE_ONE
fyl2xp1
XMM_DOUBLE_EPILOGUE
RETGUARD_CHECK(log1p, r11)
ret
END_STD(log1p)