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:29:02 -07:00 committed by Stephen Williams
parent 5fbffe24cc
commit ff309fb04f
4 changed files with 27 additions and 20 deletions

View File

@ -248,9 +248,11 @@ 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);
}

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(ICARUS_VPI_CONST 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

@ -475,7 +475,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);
@ -489,9 +489,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:
@ -953,6 +954,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 "
@ -966,10 +968,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;
}