is tone() fairly accurate?

I was looking for a 4800hz square wave to drive an old school uart chip and on my scope tone(3,4800) looks more like 4500 hz. I haven't had occasion to carefully check the scope's accuracy so it could be off.

has anyone had occasion to check tone() against an external reference?

Frequency generation on a microcontroller is a clock division process, so if you start with the 16 MHz clock, you can divide by 2 to get 8 MHz, divide by 3 to get 5.333 MHz, divide by 4 to get 4 MHz, etc. You can't get 9.2 MHz, and you can't get 6.75 MHz, etc.

The more you divide, the more finely spaced your frequencies can be. For example, at 4800 Hz you would need a division factor of 3333.333. The closest is 3333 so you actually get 16MHz/3333 = 4800.48 Hz.

Now, this is on the 16-bit Timer 1 which supports division ratios up to 16 bits (and 3333 fits into 16 bits). If you output a tone() on a Timer 0 or Timer 2 pin, you get coarser resolution since those are 8 bit timers. On Timer 2, for example, the closest you could get is to use a divide-by-32 prescale on Timer 2 so it actually counts at 16MHz/32 = 500 kHz, and then set the timer divider to 104 to get 500 kHz/104 = 4807.7 Hz.

4500 Hz does seem to be quite a ways off from what should be achievable. The scope could be suspicious (or perhaps your horizontal resolution setting on the scope?)

--
The Ruggeduino: compatible with Arduino UNO, 24V operation, all I/O's fused and protected