| 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 | }
|