Posted to tcl by sebres at Thu Aug 29 14:15:30 GMT 2019view pretty

/*
 * tclfastinvsqrt.c --
 * 
 * Test module to get native handle of channel.
 *
 * Compile:
 *   mingw: gcc -O2 -DUSE_TCL_STUBS=1 -I$tcl/win  -I$tcl/generic tclfastinvsqrt.c -shared -o tclfastinvsqrt.dll libtclstub86.a
 *   *nix:  gcc -O2 -DUSE_TCL_STUBS=1 -I$tcl/unix -I$tcl/generic tclfastinvsqrt.c -shared -o tclfastinvsqrt.so  libtclstub86.a
 * 
 * Usage:
 *   $ tclsh86
 *   % load tclfastinvsqrt
 *   % expr {invsqrt(10)}
 */

#include "tcl.h"

static inline double
FastInvSqrt(double x, int accuracy) {
  char *buf = (char *)&x;
  double xhalf = 0.5f * x;
  Tcl_WideInt i = *(Tcl_WideInt*)buf;
  i = 0x5fe6eb50c7b537a9 - (i >> 1);
  buf = (char *)&i;
  x = *(double*)buf;
  while (accuracy--) {
    x = x*(1.5-(xhalf*x*x));
  }
  return x;
}

int
FastInvSqrtCmd(
  ClientData dummy,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
) {
  double dbl;
  int accuracy = 4;

  if (objc < 2 || objc > 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "value ?accuracy?");
    return TCL_ERROR;
  }
  
  if (Tcl_GetDoubleFromObj(interp, objv[1], &dbl) != TCL_OK) {
    return TCL_ERROR;
  }
  if (objc > 2 && Tcl_GetIntFromObj(interp, objv[2], &accuracy) != TCL_OK) {
    return TCL_ERROR;
  }
  
  dbl = FastInvSqrt(dbl, accuracy);

  Tcl_SetObjResult(interp, Tcl_NewDoubleObj(dbl));
  return TCL_OK;
}

int
Tclfastinvsqrt_Init(Tcl_Interp *interp) {
  if (!Tcl_InitStubs(interp, "8.5", 0)) {
    return TCL_ERROR;
  }
  Tcl_CreateObjCommand(interp, "::tcl::mathfunc::invsqrt", FastInvSqrtCmd, NULL, NULL);
  return TCL_OK;
}

/*

load tclfastinvsqrt

# speed vs accuracy:

set tm 500
for {set x 2} {$x < 100000} {set x [expr {$x * 10}]} {
  puts $x:\t[timerate { expr { 1 / sqrt($x) } } $tm]
  puts \t[timerate { expr { invsqrt($x,1) } } $tm]
  puts \t[timerate { expr { invsqrt($x) } } $tm]
}

# accuracy :

for {set x 2} {$x < 100000} {set x [expr {$x * 10}]} {
  puts $x:\t[expr { 1 / sqrt($x) }]
  puts \t[expr { invsqrt($x) }]
}
for {set x 2} {$x < 100000} {set x [expr {$x * 10}]} {
  puts $x:\t[expr { 1 / sqrt($x) }]
  puts \t[expr { invsqrt($x,1) }]
}

# Herbie example:

for {set x 2} {$x < 100000} {set x [expr {$x * 10}]} {
  puts $x:\t[expr { sqrt($x+1) - sqrt($x) }]
  puts \t[expr { 1 / (sqrt($x+1) + sqrt($x)) }]
  puts \t[expr { invsqrt( (2*$x + 1 + 2/invsqrt(($x+1)*$x)) ) }]
}

*/

Comments

Posted by sebres at Thu Aug 29 15:02:08 GMT 2019 [text] [code]

the comments about channel was copy&paste, simply ignore it :)