This post is an indirect follow-up to an earlier post I made about floating point numbers and the frequency 902.1 Mhz. If you check out that original posting, there was a follow-up to it as well.
My day job is maintaining software that runs solid-state microwave generators. As such, it deals with frequencies such as 902.1 MHz or 902100000 Hz. The Windows-based generator control software initially used 32-bit floating point values to represent frequencies in MHz, such as:
float start_frequency_mhz = 902.0;
float end_frequency_mhz = 928.0;
The user interface would round frequencies to the nearest 100,000 Hz, such as 902.1 MHz or 915.4 MHz. This allowed the limitations of a 32-bit floating point to not be a problem.
Limitations of 32-bit floating point
As discussed in my 902.1 posts referenced at the start of this post, certain values cannot be represented as a 32-bit floating point. 902.1 turns into 902.099976, for example:
float mhz = 902.1;
printf ("mhz = %f\n", mhz);
Comparing floating point values as one would compare integers may not work as expected:
float mhz = 902.1;
if (mhz == 902.1)
{
printf ("mhz IS 902.1\n");
}
else
{
printf ("mhz is NOT 902.1\n");
}
Running that on a 32-bit compiler will print the message:
mhz is NOT 902.1
But if you change the variable type from float to double (64-bit floating point), that gives it enough precision for this to work:
double mhz = 902.1; // changed from float to double
printf ("mhz = %f\n", mhz);
if (mhz == 902.1)
{
printf ("mhz IS 902.1\n");
}
else
{
printf ("mhz is NOT 902.1\n");
}
Running that will print:
mhz IS 902.1
So, yeah. Folks who don’t do much with floating point may find this surprising. I sure did when I first learned it.
Floating point is great if you have it…
Few of the embedded systems I have programmed on were using CPUs with hardware floating point. Instead, any floating point calculations were done using a software library. Bringing in floating point library code made the program grow larger. This is not optimal when dealing with systems with limited program memory. It also made the code slower, which was not optimal on systems with slower processors.
While the Windows program, running on a modern 64-bit CPU, could use floating point easily, the embedded firmware, running on 16-bit CPUs, was written to use integers for smaller code size and faster operation. (No, of course this is not the case on every system. This is just an example you are reading on some random blog on the internet…)
Instead of trying to represent a frequency as MHz in floating point, the firmware would use a 32-bit integer as hz.
float f_mhz = 902.1; // on 64-bit Windows system
uint32_t hz = 902100000; // on 16-bit firmware
When I explain this to someone who is unfamiliar of “the problems with floating point,” I show them quick examples like this. For fun (everybody needs a hobby), tonight I wrote up a longer example that prints out the frequencies of 902 MHz to 928 MHz (matching the L-Band RF frequency used by the microwave systems at my work) in steps of .1 MHz.
The example then converts that frequency value to a float and a double, then converts each back to a 32-bit unsigned integer and prints them all out.
Some comparisons are done (float compared to double, and float converted to 32-bit integer compared to double converted to 32-bit integer) with the output marking values that differ as floating points (“F”) and/or unsigned integers (“U”).
#include <inttypes.h> // for PRIu32
#include <stdio.h> // for printf()
#include <stdlib.h> // for EXIT_SUCCESS
#define MHZ_TO_HZ(f) ((f) * 1000000UL)
#define HZ_TO_MHZ(f) ((f) / 1000000.0)
#define FREQUENCY_START_HZ 902000000
#define FREQUENCY_END_HZ 928000000
#define FREQUENCY_STEPS_HZ 100000
int main()
{
printf ("mhz float uint32_t double uint32_t ==?\n"
"----- ---------- --------- ---------- --------- ---\n"
);
for (uint32_t hz=FREQUENCY_START_HZ; hz <= FREQUENCY_END_HZ;
hz+=FREQUENCY_STEPS_HZ)
{
float mhz_as_float = HZ_TO_MHZ(hz);
double mhz_as_double = HZ_TO_MHZ(hz);
uint32_t mhz_float_as_u32 = MHZ_TO_HZ(mhz_as_float);
uint32_t mhz_double_as_u32 = MHZ_TO_HZ(mhz_as_double);
printf ("%.1f %f %9" PRIu32 " %f %9" PRIu32 " ",
HZ_TO_MHZ(hz),
mhz_as_float, mhz_float_as_u32,
mhz_as_double, mhz_double_as_u32
);
// Do floating points match?
if (mhz_as_float != mhz_as_double)
{
printf ("F");
}
else
{
printf (" ");
}
// Do converted U32s match?
if (mhz_float_as_u32 != mhz_double_as_u32)
{
printf (" U");
}
printf ("\n");
}
return EXIT_SUCCESS;
}
And here is the output:
mhz float uint32_t double uint32_t ==?
----- ---------- --------- ---------- --------- ---
902.0 902.000000 902000000 902.000000 902000000
902.1 902.099976 902099968 902.100000 902100000 F U
902.2 902.200012 902200000 902.200000 902200000 F
902.3 902.299988 902299968 902.300000 902300000 F U
902.4 902.400024 902400000 902.400000 902400000 F
902.5 902.500000 902499968 902.500000 902500000 U
902.6 902.599976 902600000 902.600000 902600000 F
902.7 902.700012 902700032 902.700000 902700000 F U
902.8 902.799988 902800000 902.800000 902800000 F
902.9 902.900024 902900032 902.900000 902900000 F U
903.0 903.000000 903000000 903.000000 903000000
903.1 903.099976 903099968 903.100000 903100000 F U
903.2 903.200012 903200000 903.200000 903200000 F
903.3 903.299988 903299968 903.300000 903300000 F U
903.4 903.400024 903400000 903.400000 903400000 F
903.5 903.500000 903500032 903.500000 903500000 U
903.6 903.599976 903600000 903.600000 903600000 F
903.7 903.700012 903700032 903.700000 903700000 F U
903.8 903.799988 903800000 903.800000 903800000 F
903.9 903.900024 903900032 903.900000 903900000 F U
904.0 904.000000 904000000 904.000000 904000000
904.1 904.099976 904099968 904.100000 904100000 F U
904.2 904.200012 904200000 904.200000 904200000 F
904.3 904.299988 904299968 904.300000 904300000 F U
904.4 904.400024 904400000 904.400000 904400000 F
904.5 904.500000 904499968 904.500000 904500000 U
904.6 904.599976 904600000 904.600000 904600000 F
904.7 904.700012 904700032 904.700000 904700000 F U
904.8 904.799988 904800000 904.800000 904800000 F
904.9 904.900024 904900032 904.900000 904900000 F U
905.0 905.000000 905000000 905.000000 905000000
905.1 905.099976 905099968 905.100000 905100000 F U
905.2 905.200012 905200000 905.200000 905200000 F
905.3 905.299988 905299968 905.300000 905300000 F U
905.4 905.400024 905400000 905.400000 905400000 F
905.5 905.500000 905500032 905.500000 905500000 U
905.6 905.599976 905600000 905.600000 905600000 F
905.7 905.700012 905700032 905.700000 905700000 F U
905.8 905.799988 905800000 905.800000 905800000 F
905.9 905.900024 905900032 905.900000 905900000 F U
906.0 906.000000 906000000 906.000000 906000000
906.1 906.099976 906099968 906.100000 906100000 F U
906.2 906.200012 906200000 906.200000 906200000 F
906.3 906.299988 906299968 906.300000 906300000 F U
906.4 906.400024 906400000 906.400000 906400000 F
906.5 906.500000 906499968 906.500000 906500000 U
906.6 906.599976 906600000 906.600000 906600000 F
906.7 906.700012 906700032 906.700000 906700000 F U
906.8 906.799988 906800000 906.800000 906800000 F
906.9 906.900024 906900032 906.900000 906900000 F U
907.0 907.000000 907000000 907.000000 907000000
907.1 907.099976 907099968 907.100000 907100000 F U
907.2 907.200012 907200000 907.200000 907200000 F
907.3 907.299988 907299968 907.300000 907300000 F U
907.4 907.400024 907400000 907.400000 907400000 F
907.5 907.500000 907500032 907.500000 907500000 U
907.6 907.599976 907600000 907.600000 907600000 F
907.7 907.700012 907700032 907.700000 907700000 F U
907.8 907.799988 907800000 907.800000 907800000 F
907.9 907.900024 907900032 907.900000 907900000 F U
908.0 908.000000 908000000 908.000000 908000000
908.1 908.099976 908099968 908.100000 908100000 F U
908.2 908.200012 908200000 908.200000 908200000 F
908.3 908.299988 908299968 908.300000 908300000 F U
908.4 908.400024 908400000 908.400000 908400000 F
908.5 908.500000 908499968 908.500000 908500000 U
908.6 908.599976 908600000 908.600000 908600000 F
908.7 908.700012 908700032 908.700000 908700000 F U
908.8 908.799988 908800000 908.800000 908800000 F
908.9 908.900024 908900032 908.900000 908900000 F U
909.0 909.000000 909000000 909.000000 909000000
909.1 909.099976 909099968 909.100000 909100000 F U
909.2 909.200012 909200000 909.200000 909200000 F
909.3 909.299988 909299968 909.300000 909300000 F U
909.4 909.400024 909400000 909.400000 909400000 F
909.5 909.500000 909500032 909.500000 909500000 U
909.6 909.599976 909600000 909.600000 909600000 F
909.7 909.700012 909700032 909.700000 909700000 F U
909.8 909.799988 909800000 909.800000 909800000 F
909.9 909.900024 909900032 909.900000 909900000 F U
910.0 910.000000 910000000 910.000000 910000000
910.1 910.099976 910099968 910.100000 910100000 F U
910.2 910.200012 910200000 910.200000 910200000 F
910.3 910.299988 910299968 910.300000 910300000 F U
910.4 910.400024 910400000 910.400000 910400000 F
910.5 910.500000 910499968 910.500000 910500000 U
910.6 910.599976 910600000 910.600000 910600000 F
910.7 910.700012 910700032 910.700000 910700000 F U
910.8 910.799988 910800000 910.800000 910800000 F
910.9 910.900024 910900032 910.900000 910900000 F U
911.0 911.000000 911000000 911.000000 911000000
911.1 911.099976 911099968 911.100000 911100000 F U
911.2 911.200012 911200000 911.200000 911200000 F
911.3 911.299988 911299968 911.300000 911300000 F U
911.4 911.400024 911400000 911.400000 911400000 F
911.5 911.500000 911500032 911.500000 911500000 U
911.6 911.599976 911600000 911.600000 911600000 F
911.7 911.700012 911700032 911.700000 911700000 F U
911.8 911.799988 911800000 911.800000 911800000 F
911.9 911.900024 911900032 911.900000 911900000 F U
912.0 912.000000 912000000 912.000000 912000000
912.1 912.099976 912099968 912.100000 912100000 F U
912.2 912.200012 912200000 912.200000 912200000 F
912.3 912.299988 912299968 912.300000 912300000 F U
912.4 912.400024 912400000 912.400000 912400000 F
912.5 912.500000 912499968 912.500000 912500000 U
912.6 912.599976 912600000 912.600000 912600000 F
912.7 912.700012 912700032 912.700000 912700000 F U
912.8 912.799988 912800000 912.800000 912800000 F
912.9 912.900024 912900032 912.900000 912900000 F U
913.0 913.000000 913000000 913.000000 913000000
913.1 913.099976 913099968 913.100000 913100000 F U
913.2 913.200012 913200000 913.200000 913200000 F
913.3 913.299988 913299968 913.300000 913300000 F U
913.4 913.400024 913400000 913.400000 913400000 F
913.5 913.500000 913500032 913.500000 913500000 U
913.6 913.599976 913600000 913.600000 913600000 F
913.7 913.700012 913700032 913.700000 913700000 F U
913.8 913.799988 913800000 913.800000 913800000 F
913.9 913.900024 913900032 913.900000 913900000 F U
914.0 914.000000 914000000 914.000000 914000000
914.1 914.099976 914099968 914.100000 914100000 F U
914.2 914.200012 914200000 914.200000 914200000 F
914.3 914.299988 914299968 914.300000 914300000 F U
914.4 914.400024 914400000 914.400000 914400000 F
914.5 914.500000 914499968 914.500000 914500000 U
914.6 914.599976 914600000 914.600000 914600000 F
914.7 914.700012 914700032 914.700000 914700000 F U
914.8 914.799988 914800000 914.800000 914800000 F
914.9 914.900024 914900032 914.900000 914900000 F U
915.0 915.000000 915000000 915.000000 915000000
915.1 915.099976 915099968 915.100000 915100000 F U
915.2 915.200012 915200000 915.200000 915200000 F
915.3 915.299988 915299968 915.300000 915300000 F U
915.4 915.400024 915400000 915.400000 915400000 F
915.5 915.500000 915500032 915.500000 915500000 U
915.6 915.599976 915600000 915.600000 915600000 F
915.7 915.700012 915700032 915.700000 915700000 F U
915.8 915.799988 915800000 915.800000 915800000 F
915.9 915.900024 915900032 915.900000 915900000 F U
916.0 916.000000 916000000 916.000000 916000000
916.1 916.099976 916099968 916.100000 916100000 F U
916.2 916.200012 916200000 916.200000 916200000 F
916.3 916.299988 916299968 916.300000 916300000 F U
916.4 916.400024 916400000 916.400000 916400000 F
916.5 916.500000 916499968 916.500000 916500000 U
916.6 916.599976 916600000 916.600000 916600000 F
916.7 916.700012 916700032 916.700000 916700000 F U
916.8 916.799988 916800000 916.800000 916800000 F
916.9 916.900024 916900032 916.900000 916900000 F U
917.0 917.000000 917000000 917.000000 917000000
917.1 917.099976 917099968 917.100000 917100000 F U
917.2 917.200012 917200000 917.200000 917200000 F
917.3 917.299988 917299968 917.300000 917300000 F U
917.4 917.400024 917400000 917.400000 917400000 F
917.5 917.500000 917500032 917.500000 917500000 U
917.6 917.599976 917600000 917.600000 917600000 F
917.7 917.700012 917700032 917.700000 917700000 F U
917.8 917.799988 917800000 917.800000 917800000 F
917.9 917.900024 917900032 917.900000 917900000 F U
918.0 918.000000 918000000 918.000000 918000000
918.1 918.099976 918099968 918.100000 918100000 F U
918.2 918.200012 918200000 918.200000 918200000 F
918.3 918.299988 918299968 918.300000 918300000 F U
918.4 918.400024 918400000 918.400000 918400000 F
918.5 918.500000 918499968 918.500000 918500000 U
918.6 918.599976 918600000 918.600000 918600000 F
918.7 918.700012 918700032 918.700000 918700000 F U
918.8 918.799988 918800000 918.800000 918800000 F
918.9 918.900024 918900032 918.900000 918900000 F U
919.0 919.000000 919000000 919.000000 919000000
919.1 919.099976 919099968 919.100000 919100000 F U
919.2 919.200012 919200000 919.200000 919200000 F
919.3 919.299988 919299968 919.300000 919300000 F U
919.4 919.400024 919400000 919.400000 919400000 F
919.5 919.500000 919500032 919.500000 919500000 U
919.6 919.599976 919600000 919.600000 919600000 F
919.7 919.700012 919700032 919.700000 919700000 F U
919.8 919.799988 919800000 919.800000 919800000 F
919.9 919.900024 919900032 919.900000 919900000 F U
920.0 920.000000 920000000 920.000000 920000000
920.1 920.099976 920099968 920.100000 920100000 F U
920.2 920.200012 920200000 920.200000 920200000 F
920.3 920.299988 920299968 920.300000 920300000 F U
920.4 920.400024 920400000 920.400000 920400000 F
920.5 920.500000 920499968 920.500000 920500000 U
920.6 920.599976 920600000 920.600000 920600000 F
920.7 920.700012 920700032 920.700000 920700000 F U
920.8 920.799988 920800000 920.800000 920800000 F
920.9 920.900024 920900032 920.900000 920900000 F U
921.0 921.000000 921000000 921.000000 921000000
921.1 921.099976 921099968 921.100000 921100000 F U
921.2 921.200012 921200000 921.200000 921200000 F
921.3 921.299988 921299968 921.300000 921300000 F U
921.4 921.400024 921400000 921.400000 921400000 F
921.5 921.500000 921500032 921.500000 921500000 U
921.6 921.599976 921600000 921.600000 921600000 F
921.7 921.700012 921700032 921.700000 921700000 F U
921.8 921.799988 921800000 921.800000 921800000 F
921.9 921.900024 921900032 921.900000 921900000 F U
922.0 922.000000 922000000 922.000000 922000000
922.1 922.099976 922099968 922.100000 922100000 F U
922.2 922.200012 922200000 922.200000 922200000 F
922.3 922.299988 922299968 922.300000 922300000 F U
922.4 922.400024 922400000 922.400000 922400000 F
922.5 922.500000 922499968 922.500000 922500000 U
922.6 922.599976 922600000 922.600000 922600000 F
922.7 922.700012 922700032 922.700000 922700000 F U
922.8 922.799988 922800000 922.800000 922800000 F
922.9 922.900024 922900032 922.900000 922900000 F U
923.0 923.000000 923000000 923.000000 923000000
923.1 923.099976 923099968 923.100000 923100000 F U
923.2 923.200012 923200000 923.200000 923200000 F
923.3 923.299988 923299968 923.300000 923300000 F U
923.4 923.400024 923400000 923.400000 923400000 F
923.5 923.500000 923500032 923.500000 923500000 U
923.6 923.599976 923600000 923.600000 923600000 F
923.7 923.700012 923700032 923.700000 923700000 F U
923.8 923.799988 923800000 923.800000 923800000 F
923.9 923.900024 923900032 923.900000 923900000 F U
924.0 924.000000 924000000 924.000000 924000000
924.1 924.099976 924099968 924.100000 924100000 F U
924.2 924.200012 924200000 924.200000 924200000 F
924.3 924.299988 924299968 924.300000 924300000 F U
924.4 924.400024 924400000 924.400000 924400000 F
924.5 924.500000 924499968 924.500000 924500000 U
924.6 924.599976 924600000 924.600000 924600000 F
924.7 924.700012 924700032 924.700000 924700000 F U
924.8 924.799988 924800000 924.800000 924800000 F
924.9 924.900024 924900032 924.900000 924900000 F U
925.0 925.000000 925000000 925.000000 925000000
925.1 925.099976 925099968 925.100000 925100000 F U
925.2 925.200012 925200000 925.200000 925200000 F
925.3 925.299988 925299968 925.300000 925300000 F U
925.4 925.400024 925400000 925.400000 925400000 F
925.5 925.500000 925500032 925.500000 925500000 U
925.6 925.599976 925600000 925.600000 925600000 F
925.7 925.700012 925700032 925.700000 925700000 F U
925.8 925.799988 925800000 925.800000 925800000 F
925.9 925.900024 925900032 925.900000 925900000 F U
926.0 926.000000 926000000 926.000000 926000000
926.1 926.099976 926099968 926.100000 926100000 F U
926.2 926.200012 926200000 926.200000 926200000 F
926.3 926.299988 926299968 926.300000 926300000 F U
926.4 926.400024 926400000 926.400000 926400000 F
926.5 926.500000 926499968 926.500000 926500000 U
926.6 926.599976 926600000 926.600000 926600000 F
926.7 926.700012 926700032 926.700000 926700000 F U
926.8 926.799988 926800000 926.800000 926800000 F
926.9 926.900024 926900032 926.900000 926900000 F U
927.0 927.000000 927000000 927.000000 927000000
927.1 927.099976 927099968 927.100000 927100000 F U
927.2 927.200012 927200000 927.200000 927200000 F
927.3 927.299988 927299968 927.300000 927300000 F U
927.4 927.400024 927400000 927.400000 927400000 F
927.5 927.500000 927500032 927.500000 927500000 U
927.6 927.599976 927600000 927.600000 927600000 F
927.7 927.700012 927700032 927.700000 927700000 F U
927.8 927.799988 927800000 927.800000 927800000 F
927.9 927.900024 927900032 927.900000 927900000 F U
928.0 928.000000 928000000 928.000000 928000000
In the last column, F means that the float (32-bit) floating point and the double (64-bit) floating point do not match. U means the float (32-bit) converted to 32-bit integer does not match the double (64-bit) converted to integer.
All this to say, as I am beginning to experiment with new routines dealing with frequencies, I am creating all of them to handle those frequencies as Hz using 32-bit unsigned integers. This will allow these routines to also work well on embedded systems that lack floating point hardware, as well as ensure any weird “902.1” issues won’t cause someone else to waste hours of their time dealing with some math that is slightly off. . .
To be continued…
