timeval_subtract解释

使用“timeval_subtract”函数来查找两个struct timeval类型之间经过的时间,有人可以解释一下用于“通过更新y执行后续减法的执行”和其他部分的目的和一步一步的数学运算吗? 我理解该函数的目的以及如何在程序中实现它,但我想了解它是如何工作的,并且无法在任何地方找到任何解释,我似乎无法绕过它。

int timeval_subtract (struct timeval *result, struct timeval *x,struct timeval *y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (y->tv_usec - x->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. tv_usec is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ return x->tv_sec tv_sec; } 

这是一个与GNU C库相关的函数,用于确定经过的时间http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html所以我不是在寻找改进而只是一个解释为什么划分和加减以及在其中相乘。 这些特定算术运算实现了什么?/为什么要完成/不完成? 我已经完成了踩踏但仍然无法绕过它。 我将继续这样做,直到我这样做(甚至在有人向我解释之后),但我希望从已经了解它的人那里得到一些见解。 该平台是UNIX,我不习惯使用它,但我认为它不会改变函数内部发生的操作。 关于正在执行的算法比使用的算法更具问题。

乍一看,它看起来像struct timeval包含分为两部分的时间:

  • tv_usec – 微秒,理想情况下应始终低于1000000,但似乎允许更大的值,如代码所示
  • tv_sec – 秒(倍数为1000000)

以微秒为单位的时间是tv_usec + tv_sec * 1000000。

相反,人们会认为这是真的:

  • tv_sec =以微秒为单位的时间/ 1000000
  • tv_usec =以微秒为单位的时间%1000000。

该函数似乎计算*x*y之间的时间差(逻辑上, *x*y )并将其存储在另一个struct timeval*result

一个简单的测试程序给我们一些提示:

 #include  struct timeval { long tv_sec; long tv_usec; }; int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) { // preserve *y struct timeval yy = *y; y = &yy; /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (y->tv_usec - x->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. tv_usec is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ return x->tv_sec < y->tv_sec; } struct timeval testData00 = { 0, 0 }; struct timeval testData01 = { 0, 1 }; int main(void) { struct timeval diff; int res; res = timeval_subtract(&diff, &testData00, &testData00); printf("%d %ld:%ld\n", res, diff.tv_sec, diff.tv_usec); res = timeval_subtract(&diff, &testData01, &testData01); printf("%d %ld:%ld\n", res, diff.tv_sec, diff.tv_usec); res = timeval_subtract(&diff, &testData01, &testData00); printf("%d %ld:%ld\n", res, diff.tv_sec, diff.tv_usec); res = timeval_subtract(&diff, &testData00, &testData01); printf("%d %ld:%ld\n", res, diff.tv_sec, diff.tv_usec); return 0; } 

输出( ideone ):

 0 0:0 0 0:0 0 0:1 1 -1:999999 

从上一个测试结果看,函数返回(-1):999999而不是 – (0:1)。 两个值表示相同的负时间(或时间差),以微秒为单位:

  • -1 * 1000000 + 999999 = -1
  • – (0 * 1000000 + 1)= -1

那么,它是如何运作的?

如果x->tv_usec > = y->tv_usec那么只有第二个if可能*执行:

  if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (y->tv_usec - x->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } 

if检查微秒部分的差异是否大于1秒。 如果是,它从y->tv_usec (以微秒为单位)减去这个差异的整秒,并将其添加到y->tv_sec (以秒为单位)。 这只是在*y重新分配时间而不真正改变它。 你可以重写这个, if等效,可以更清楚地看到它:

  if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } 

这里要注意的一件重要事情是,当输入*x*ytv_usec在0到999999(含)范围内时,这个if的主体不执行(因此,实际上*实际上从来都不x->tv_usec > = y->tv_usec ,当tv_usecs在0到999999范围内时)。

现在这个问题的净效果还不是很明显。

但是,这里可以看到一件有趣的事情。 如果我们用*x = 0:1000001和*y = 0:0调用这个函数,结果将是错误的: 差值=( – 1):2000001(而不是1:1)和函数的返回值= 1(而不是0) 。 这表明该函数不适用于tv_usec > 1000000甚至tv_usec > 999999 。 并且由于这种行为,我将声称该函数不适合输入中的负tv_usec 。 面对这种行为,我只会忽略这些情况。 它看起来已经错了。

让我们来看看第一个if

  /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } 

正如评论和代码所暗示的那样,当x->tv_usec < y->tv_usec我们需要处理“数字”之间的“进位”,好像我们正在添加而不是减去。 但没关系,我们会看到它。

我们回到学校一会儿。

你怎么做37 – 12?

你这样做:

 7 - 2 = 5 3 - 1 = 2 

所以37 – 12 = 25。

现在,你怎么做57-38?

你这样做:

 10/*because 7 < 8*/ + 7 - 8 = 9 5 - 3 - 1/*borrow, because of the above*/ = 1 

所以57 - 38 = 19.看?

检查:

  if (x->tv_usec < y->tv_usec) { 

检查我们是否需要照顾这笔借款。

那么,这里发生了什么? 让我们再来看看:

  if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } 

如果y->tv_usec > x->tv_usec ,它会计算两者之间的差异, if将整数秒加到y->tv_sec并从y->tv_usec减去它们,就像另一个一样,只需重新分配时间在*y ,没有改变它。

最后添加到y->tv_sec的额外的一个( + 1 )将从函数末尾的x->tv_sec中减去( result->tv_sec = x->tv_sec - y->tv_sec; )因此这个1作为借用我在57-38 = 19的例子中提醒你。

除了借用本身和一些时间再分配之外还发生了什么呢?

就像我之前说过的那样,我只是会忽略负面的tv_usecs并且大于999999,因为可能处理不当。

有了这个,我把(y->tv_usec - x->tv_usec) / 1000000变为0,我只留下真正有意义的tv_usecs值(0到999999,含)。

所以,如果if's条件为真,我基本上从y->tv_usec减去1000000并将1(借用)加到y->tv_sec

这与我们在57 - 38 = 19时的情况相同:

 10/*because 7 < 8*/ + 7 - 8 = 9 5 - 3 - 1/*borrow, because of the above*/ = 1 

与此10类似,稍后将在此处添加1000000: result->tv_usec = x->tv_usec - y->tv_usec;

而这首先是function的肉。

如果我必须写一个具有类似行为的函数,我要求输入时间是非负的,微秒部分不大于999999,我只写这个:

 int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) { result->tv_sec = x->tv_sec - y->tv_sec; if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) { result->tv_usec += 1000000; result->tv_sec--; // borrow } return result->tv_sec < 0; } 

如果由于一些奇怪的原因我想在输入中支持tv_usec > 999999,我首先将多余的从tv_usec移到tv_sec ,然后执行上述操作,如下所示:

 int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) { struct timeval xx = *x; struct timeval yy = *y; x = &xx; y = &yy; if (x->tv_usec > 999999) { x->tv_sec += x->tv_usec / 1000000; x->tv_usec %= 1000000; } if (y->tv_usec > 999999) { y->tv_sec += y->tv_usec / 1000000; y->tv_usec %= 1000000; } result->tv_sec = x->tv_sec - y->tv_sec; if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) { result->tv_usec += 1000000; result->tv_sec--; // borrow } return result->tv_sec < 0; } 

这里的意图很明确,代码很容易理解。