On Wed, Nov 23, 2016 at 02:34:03PM +0100, Andrei Pistirica wrote: > From what I understand, your suggestion is: > (ns | frac) * ppb = (total_ns | total_frac) > (total_ns | total_frac) / 10^9 = (adj_ns | adj_frac) > This is correct iff total_ns/10^9 >= 1, but the problem is that there are > missed fractions due to the following approximation: > frac*ppb =~ (ns*ppb+frac*ppb*2^16)*2^16-10^9*2^16*flor(ns*ppb+frac*ppb*2^16, > 10^9).
-ENOPARSE; > An example which uses values from a real test: > let ppb=4891, ns=12 and frac=3158 That is a very strange example for nominal frequency. The clock period is 12.048187255859375 nanoseconds, and so the frequency is 83000037.99 Hz. But hey, let's go with it... > - using suggested algorithm, yields: adj_ns = 0 and adj_frac = 0 > - using in-place algorithm, yields: adj_ns = 0, adj_frac = 4 > You can check the calculus. The test program, below, shows you what I meant. (Of course, you should adjust this to fit the adjfine() method.) Unfortunately, this device has a very coarse frequency resolution. Using a nominal period of ns=12 as an example, the resolution is 2^-16 / 12 or 1.27 ppm. The 24 bit device is much better in this repect. The output using your example numbers is: $ ./a.out 12 3158 4891 ns=12 frac=3158 ns=12 frac=3162 $ ./a.out 12 3158 -4891 ns=12 frac=3158 ns=12 frac=3154 See how you get a result of +/- 4 with just one division? Thanks, Richard --- #include <stdint.h> #include <stdio.h> #include <stdlib.h> static void adjfreq(uint32_t ns, uint32_t frac, int32_t ppb) { uint64_t adj; uint32_t diff, word; int neg_adj = 0; printf("ns=%u frac=%u\n", ns, frac); if (ppb < 0) { neg_adj = 1; ppb = -ppb; } word = (ns << 16) + frac; adj = word; adj *= ppb; adj += 500000000UL; diff = adj / 1000000000UL; word = neg_adj ? word - diff : word + diff; printf("ns=%u frac=%u\n", word >> 16, word & 0xffff); } int main(int argc, char *argv[]) { uint32_t ns, frac; int32_t ppb; if (argc != 4) { puts("need ns, frac, and ppb"); return -1; } ns = atoi(argv[1]); frac = atoi(argv[2]); ppb = atoi(argv[3]); adjfreq(ns, frac, ppb); return 0; }