| 1 | // float_test.cc - Learning by running code from Bruce Dawson's articles!
 | 
| 2 | //
 | 
| 3 | // Index here:
 | 
| 4 | //   https://randomascii.wordpress.com/2013/02/07/float-precision-revisited-nine-digit-float-portability/
 | 
| 5 | //
 | 
| 6 | // union Float_t helper:
 | 
| 7 | //   https://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format/
 | 
| 8 | //
 | 
| 9 | // How to round trip floating numbers - just sprintf() and sscanf!
 | 
| 10 | //   https://randomascii.wordpress.com/2012/03/08/float-precisionfrom-zero-to-100-digits-2/
 | 
| 11 | //
 | 
| 12 | //     printf(“%1.8e\n”, d); // Round-trippable float, always with an exponent
 | 
| 13 | //     printf(“%.9g\n”, d); // Round-trippable float, shortest possible
 | 
| 14 | //
 | 
| 15 | //     printf(“%1.16e\n”, d); // Round-trippable double, always with an exponent
 | 
| 16 | //     printf(“%.17g\n”, d); // Round-trippable double, shortest possible
 | 
| 17 | //
 | 
| 18 | // Good idea - do an exhaustive test of all floats:
 | 
| 19 | //   https://randomascii.wordpress.com/2014/01/27/theres-only-four-billion-floatsso-test-them-all/
 | 
| 20 | //
 | 
| 21 | // But use threads.  A single threaded test took 12 minutes on my machine.
 | 
| 22 | //   https://randomascii.wordpress.com/2012/03/11/c-11-stdasync-for-fast-float-format-finding/
 | 
| 23 | 
 | 
| 24 | #include <inttypes.h>
 | 
| 25 | 
 | 
| 26 | #include "vendor/greatest.h"
 | 
| 27 | 
 | 
| 28 | union Float_t {
 | 
| 29 |   Float_t(float num = 0.0f) : f(num) {
 | 
| 30 |   }
 | 
| 31 |   // Portable extraction of components.
 | 
| 32 |   bool Negative() const {
 | 
| 33 |     return (i >> 31) != 0;
 | 
| 34 |   }
 | 
| 35 |   int32_t RawMantissa() const {
 | 
| 36 |     return i & ((1 << 23) - 1);
 | 
| 37 |   }
 | 
| 38 |   int32_t RawExponent() const {
 | 
| 39 |     return (i >> 23) & 0xFF;
 | 
| 40 |   }
 | 
| 41 | 
 | 
| 42 |   int32_t i;
 | 
| 43 |   float f;
 | 
| 44 | #if 1
 | 
| 45 |   struct {  // Bitfields for exploration. Do not use in production code.
 | 
| 46 |     uint32_t mantissa : 23;
 | 
| 47 |     uint32_t exponent : 8;
 | 
| 48 |     uint32_t sign : 1;
 | 
| 49 |   } parts;
 | 
| 50 | #endif
 | 
| 51 | };
 | 
| 52 | 
 | 
| 53 | void PrintPartsOfFloat(Float_t num) {
 | 
| 54 |   // printf("Float value, representation, sign, exponent, mantissa\n");
 | 
| 55 |   printf("%1.8e   0x%08X   sign %d, exponent %d, mantissa 0x%06X\n", num.f,
 | 
| 56 |          num.i, num.parts.sign, num.parts.exponent, num.parts.mantissa);
 | 
| 57 | }
 | 
| 58 | 
 | 
| 59 | // https://randomascii.wordpress.com/2013/02/07/float-precision-revisited-nine-digit-float-portability/
 | 
| 60 | 
 | 
| 61 | TEST print_float_test() {
 | 
| 62 |   Float_t num(1.0f);
 | 
| 63 |   for (int i = 0; i < 10; ++i) {
 | 
| 64 |     PrintPartsOfFloat(num);
 | 
| 65 | 
 | 
| 66 |     // change it to an adjacent float
 | 
| 67 |     num.i -= 1;
 | 
| 68 | 
 | 
| 69 | #if 0
 | 
| 70 |     char s[20];
 | 
| 71 | 
 | 
| 72 |     // He recommends both of these - what is the difference?
 | 
| 73 |     // Oh one of them uses e-01
 | 
| 74 | 
 | 
| 75 |     sprintf(s, "%1.8e\n", num.f);
 | 
| 76 |     printf("%s", s);
 | 
| 77 | 
 | 
| 78 |     sprintf(s, "%.9g\n", num.f);
 | 
| 79 |     printf("%s", s);
 | 
| 80 | 
 | 
| 81 |     float f;
 | 
| 82 |     sscanf(s, "%f", &f);
 | 
| 83 |     Float_t parsed(f);
 | 
| 84 | 
 | 
| 85 |     sprintf(s, "%.9g\n", parsed.f);
 | 
| 86 |     printf("parsed %s", s);
 | 
| 87 | 
 | 
| 88 |     ASSERT_EQ_FMT(num.i, parsed.i, "%d");
 | 
| 89 | #endif
 | 
| 90 |   }
 | 
| 91 | 
 | 
| 92 |   PASS();
 | 
| 93 | }
 | 
| 94 | 
 | 
| 95 | TEST decimal_round_trip_test() {
 | 
| 96 |   // Test that sprintf() and sscanf() round trip all floats!
 | 
| 97 |   // Presumably strtof() uses the same algorithm as sscanf().
 | 
| 98 | 
 | 
| 99 |   // This is the biggest number that can be represented in both float and
 | 
| 100 |   // int32_t. It’s 2^31-128.
 | 
| 101 |   Float_t max_float(2147483520.0f);
 | 
| 102 | 
 | 
| 103 |   // 22 bits out of 32, so we print 2**10 or ~1000 lines of progress
 | 
| 104 |   const int interval = 1 << 22;
 | 
| 105 | 
 | 
| 106 |   // Use long long to let us loop over all positive integers.
 | 
| 107 |   long long i = 0;
 | 
| 108 |   while (i <= max_float.i) {
 | 
| 109 |     Float_t num;
 | 
| 110 |     num.i = (int32_t)i;
 | 
| 111 | 
 | 
| 112 |     char s[20];
 | 
| 113 |     sprintf(s, "%.9g\n", num.f);  // preserves all information
 | 
| 114 |     // printf("%s", s);
 | 
| 115 | 
 | 
| 116 |     float f;
 | 
| 117 |     sscanf(s, "%f", &f);  // recover all information
 | 
| 118 |     Float_t parsed(f);
 | 
| 119 | 
 | 
| 120 |     sprintf(s, "%.9g\n", parsed.f);
 | 
| 121 |     // printf("parsed %s", s);
 | 
| 122 | 
 | 
| 123 |     ASSERT_EQ_FMT(num.i, parsed.i, "%d");
 | 
| 124 | 
 | 
| 125 |     i++;
 | 
| 126 | 
 | 
| 127 |     if (i % interval == 0) {
 | 
| 128 |       printf("%lld iterations done\n", i);
 | 
| 129 |     }
 | 
| 130 | 
 | 
| 131 |     // Comment this out to do more
 | 
| 132 |     if (i == 1000) {
 | 
| 133 |       printf("STOPPING EARLY\n");
 | 
| 134 |       break;
 | 
| 135 |     }
 | 
| 136 |   }
 | 
| 137 |   PASS();
 | 
| 138 | }
 | 
| 139 | 
 | 
| 140 | GREATEST_MAIN_DEFS();
 | 
| 141 | 
 | 
| 142 | int main(int argc, char** argv) {
 | 
| 143 |   GREATEST_MAIN_BEGIN();
 | 
| 144 | 
 | 
| 145 |   RUN_TEST(print_float_test);
 | 
| 146 |   RUN_TEST(decimal_round_trip_test);
 | 
| 147 | 
 | 
| 148 |   GREATEST_MAIN_END();
 | 
| 149 |   return 0;
 | 
| 150 | }
 |