V0.9: Reduce the error when scaling a real time value.

When scaling a time value we would often use the power operator to
create constants 10**-N that were then multiplied with the original
value. The problem with this is that there is some errors in the
representation of the fractional number. It is better to create a
integer value 10**N and then divide the original value by this
exact constant. You still have the calculation error, but the scale
value is now an exactly real value.
This commit is contained in:
Cary R 2011-10-30 23:30:50 -07:00 committed by Stephen Williams
parent e160f5c562
commit 85faab8a08
4 changed files with 39 additions and 32 deletions

View File

@ -50,7 +50,7 @@ static PLI_INT32 my_mcd_printf(PLI_UINT32 mcd, const char *fmt, ...)
struct timeformat_info_s timeformat_info = { 0, 0, 0, 20 };
struct strobe_cb_info {
char*name;
const char*name;
char*filename;
int lineno;
int default_format;
@ -129,7 +129,7 @@ static void array_from_iterator(struct strobe_cb_info*info, vpiHandle argv)
}
}
static int get_default_format(char *name)
static int get_default_format(const char *name)
{
int default_format;
@ -247,16 +247,18 @@ static void get_time(char *rtn, const char *value, int prec,
static void get_time_real(char *rtn, double value, int prec,
PLI_INT32 time_units)
{
/* Scale the value if its time units differ from the format units. */
if (time_units != timeformat_info.units) {
/* Scale the value from its time units to the format time units. */
if (time_units >= timeformat_info.units) {
value *= pow(10.0, time_units - timeformat_info.units);
} else {
value /= pow(10.0, timeformat_info.units - time_units);
}
sprintf(rtn, "%0.*f%s", prec, value, timeformat_info.suff);
}
static unsigned int get_format_char(char **rtn, int ljust, int plus,
int ld_zero, int width, int prec,
char fmt, struct strobe_cb_info *info,
char fmt, const struct strobe_cb_info *info,
unsigned int *idx)
{
s_vpi_value value;
@ -609,7 +611,7 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus,
/* The 512 (513-1 for EOL) is more than enough for any double
* value (309 digits plus a decimal point maximum). Because of
* scaling this could be larger. For decimal values you can
* have an arbitraty value so you can overflow the buffer, but
* have an arbitrary value so you can overflow the buffer, but
* for now we will assume the user will use this as intended
* (pass a time variable or the result of a time function). */
tbuf = malloc((513+suff_len)*sizeof(char));
@ -807,7 +809,7 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus,
/* We can't use the normal str functions on the return value since
* %u and %z can insert NULL characters into the stream. */
static unsigned int get_format(char **rtn, char *fmt,
struct strobe_cb_info *info, unsigned int *idx)
const struct strobe_cb_info *info, unsigned int *idx)
{
char *cp = fmt;
unsigned int size;
@ -854,7 +856,7 @@ static unsigned int get_format(char **rtn, char *fmt,
return size - 1;
}
static unsigned int get_numeric(char **rtn, struct strobe_cb_info *info,
static unsigned int get_numeric(char **rtn, const struct strobe_cb_info *info,
vpiHandle item)
{
int size, min;
@ -883,7 +885,7 @@ static unsigned int get_numeric(char **rtn, struct strobe_cb_info *info,
/* In many places we can't use the normal str functions since %u and %z
* can insert NULL characters into the stream. */
static char *get_display(unsigned int *rtnsz, struct strobe_cb_info *info)
static char *get_display(unsigned int *rtnsz, const struct strobe_cb_info *info)
{
char *result, *fmt, *rtn, *func_name;
const char *cresult;
@ -1020,7 +1022,7 @@ static char *get_display(unsigned int *rtnsz, struct strobe_cb_info *info)
return rtn;
}
static int sys_check_args(vpiHandle callh, vpiHandle argv, PLI_BYTE8*name,
static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name,
int no_auto, int is_monitor)
{
vpiHandle arg;
@ -1197,8 +1199,6 @@ static PLI_INT32 sys_display_calltf(PLI_BYTE8 *name)
*/
static PLI_INT32 strobe_cb(p_cb_data cb)
{
char* result = NULL;
unsigned int size, location=0;
struct strobe_cb_info*info = (struct strobe_cb_info*)cb->user_data;
/* We really need to cancel any $fstrobe() calls for a file when it
@ -1206,6 +1206,8 @@ static PLI_INT32 strobe_cb(p_cb_data cb)
* Which has the same basic effect. */
if ((! IS_MCD(info->fd_mcd) && vpi_get_file(info->fd_mcd) != NULL) ||
( IS_MCD(info->fd_mcd) && my_mcd_printf(info->fd_mcd, "") != EOF)) {
char* result = NULL;
unsigned int size, location=0;
/* Because %u and %z may put embedded NULL characters into the
* returned string strlen() may not match the real size! */
result = get_display(&size, info);
@ -1219,12 +1221,12 @@ static PLI_INT32 strobe_cb(p_cb_data cb)
}
}
my_mcd_printf(info->fd_mcd, "\n");
free(result);
}
free(info->filename);
free(info->items);
free(info);
free(result);
return 0;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -95,7 +95,9 @@ static PLI_INT32 sys_realtime_calltf(PLI_BYTE8*name)
/* For $abstime() we return the time in second. */
if (strcmp(name, "$abstime") == 0) {
now.real *= pow(10.0, vpi_get(vpiTimeUnit, mod));
PLI_INT32 scale = vpi_get(vpiTimeUnit, mod);
if (scale >= 0) now.real *= pow(10.0, scale);
else now.real /= pow(10.0, -scale);
}
val.format = vpiRealVal;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 2008-2011 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -407,7 +407,7 @@ int vpip_time_precision_from_handle(vpiHandle obj)
void vpi_get_time(vpiHandle obj, s_vpi_time*vp)
{
int units;
int scale;
vvp_time64_t time;
assert(vp);
@ -421,9 +421,10 @@ void vpi_get_time(vpiHandle obj, s_vpi_time*vp)
break;
case vpiScaledRealTime:
units = vpip_time_units_from_handle(obj);
vp->real = pow(10.0L, vpip_get_time_precision() - units);
vp->real *= time;
scale = vpip_get_time_precision() -
vpip_time_units_from_handle(obj);
if (scale >= 0) vp->real = (double)time * pow(10.0, scale);
else vp->real = (double)time / pow(10.0, -scale);
break;
default:
@ -840,6 +841,7 @@ vpiHandle vpi_put_value(vpiHandle obj, s_vpi_value*vp,
if (flags!=vpiNoDelay && flags!=vpiForceFlag && flags!=vpiReleaseFlag) {
vvp_time64_t dly;
int scale;
if (vpi_get(vpiAutomatic, obj)) {
fprintf(stderr, "vpi error: cannot put a value with "
@ -853,10 +855,13 @@ vpiHandle vpi_put_value(vpiHandle obj, s_vpi_value*vp,
switch (when->type) {
case vpiScaledRealTime:
dly = (vvp_time64_t)(when->real *
(pow(10.0L,
vpip_time_units_from_handle(obj) -
vpip_get_time_precision())));
scale = vpip_time_units_from_handle(obj) -
vpip_get_time_precision();
if (scale >= 0) {
dly = (vvp_time64_t)(when->real * pow(10.0, scale));
} else {
dly = (vvp_time64_t)(when->real / pow(10.0, -scale));
}
break;
case vpiSimTime:
dly = vpip_timestruct_to_time(when);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -55,14 +55,12 @@ vvp_time64_t vpip_timestruct_to_time(const struct t_vpi_time*ts)
double vpip_time_to_scaled_real(vvp_time64_t ti, struct __vpiScope*scope)
{
int units;
if (scope)
units = scope->time_units;
else
units = vpi_time_precision;
double val;
int scale = 0;
if (scope) scale = vpi_time_precision - scope->time_units;
double val = pow(10.0L, vpi_time_precision - units);
val *= ti;
if (scale >= 0) val = (double)ti * pow(10.0, scale);
else val = (double) ti / pow(10.0, -scale);
return val;
}