/** @file
  Compute the base 10 logrithm of x.

  Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
  This program and the accompanying materials are licensed and made available under
  the terms and conditions of the BSD License that accompanies this distribution.
  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================

  e_log10.c 5.1 93/09/24
  NetBSD: e_log10.c,v 1.12 2002/05/26 22:01:51 wiz Exp
**/
#include  <LibConfig.h>
#include  <sys/EfiCdefs.h>

/* __ieee754_log10(x)
 * Return the base 10 logarithm of x
 *
 * Method :
 *  Let log10_2hi = leading 40 bits of log10(2) and
 *      log10_2lo = log10(2) - log10_2hi,
 *      ivln10   = 1/log(10) rounded.
 *  Then
 *    n = ilogb(x),
 *    if(n<0)  n = n+1;
 *    x = scalbn(x,-n);
 *    log10(x) := n*log10_2hi + (n*log10_2lo + ivln10*log(x))
 *
 * Note 1:
 *  To guarantee log10(10**n)=n, where 10**n is normal, the rounding
 *  mode must set to Round-to-Nearest.
 * Note 2:
 *  [1/log(10)] rounded to 53 bits has error  .198   ulps;
 *  log10 is monotonic at all binary break points.
 *
 * Special cases:
 *  log10(x) is NaN with signal if x < 0;
 *  log10(+INF) is +INF with no signal; log10(0) is -INF with signal;
 *  log10(NaN) is that NaN with no signal;
 *  log10(10**N) = N  for N=0,1,...,22.
 *
 * Constants:
 * The hexadecimal values are the intended ones for the following constants.
 * The decimal values may be used, provided that the compiler will convert
 * from decimal to binary accurately enough to produce the hexadecimal values
 * shown.
 */

#include "math.h"
#include "math_private.h"
#include  <errno.h>

#if defined(_MSC_VER)           /* Handle Microsoft VC++ compiler specifics. */
  // potential divide by 0 -- near line 80, (x-x)/zero is on purpose
  #pragma warning ( disable : 4723 )
#endif

static const double
two54      =  1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */
ivln10     =  4.34294481903251816668e-01, /* 0x3FDBCB7B, 0x1526E50E */
log10_2hi  =  3.01029995663611771306e-01, /* 0x3FD34413, 0x509F6000 */
log10_2lo  =  3.69423907715893078616e-13; /* 0x3D59FEF3, 0x11F12B36 */

static const double zero   =  0.0;

double
__ieee754_log10(double x)
{
  double y,z;
  int32_t i,k,hx;
  u_int32_t lx;

  EXTRACT_WORDS(hx,lx,x);

  k=0;
  if (hx < 0x00100000) {            /* x < 2**-1022  */
    if (((hx&0x7fffffff)|lx)==0)
      return -two54/zero;           /* log(+-0)=-inf */
    if (hx<0) {
      errno = EDOM;
      return (x-x)/zero;            /* log(-#) = NaN */
    }
    k -= 54; x *= two54;            /* subnormal number, scale up x */
    GET_HIGH_WORD(hx,x);
  }
  if (hx >= 0x7ff00000) return x+x;
  k += (hx>>20)-1023;
  i  = ((u_int32_t)k&0x80000000)>>31;
  hx = (hx&0x000fffff)|((0x3ff-i)<<20);
  y  = (double)(k+i);
  SET_HIGH_WORD(x,hx);
  z  = y*log10_2lo + ivln10*__ieee754_log(x);
  return  z+y*log10_2hi;
}