Skip to content

StrFromF64

Description

Convert a double to string

Parameters

Name Direction Description
str out String to store the result in
value in Value to convert
config in Formatting configuration. May be NULL (uses the default: 6 decimal places).

Success

Returns str

Failure

Returns NULL on allocator OOM while appending to str, or when a non-NULL config has an invalid precision (> 17). The destination string is untouched.

Usage example (Cross-references)

Usage examples (Cross-references)
            // covers it with margin. No allocator -- `o` may be a stack Str.
            StrInitStack(temp, 1024) {
                if (!StrFromF64(&temp, *v, &config)) {
                    return false;
                }
    }
    
    Str *StrFromF64(Str *str, f64 value, const StrFloatFormat *config) {
        ValidateStr(str);
    // Test StrFromF64 function
    bool test_str_from_f64(void) {
        WriteFmt("Testing StrFromF64\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        // Test integer conversion
        StrFloatFormat config = {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 123.0, &config);
        bool result = (ZstrCompare(StrBegin(&s), "123.00") == 0);
        if (!result) {
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 3, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 123.456, &config);
        result = result && (ZstrCompare(StrBegin(&s), "123.456") == 0);
        if (!result) {
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 3, .force_sci = false, .uppercase = false};
        StrFromF64(&s, -123.456, &config);
        result = result && (ZstrCompare(StrBegin(&s), "-123.456") == 0);
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 3, .force_sci = true, .uppercase = false};
        StrFromF64(&s, 123.456, &config);
        result = result && (ZstrCompare(StrBegin(&s), "1.235e+02") == 0);
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 3, .force_sci = true, .uppercase = true};
        StrFromF64(&s, 123.456, &config);
        result = result && (ZstrCompare(StrBegin(&s), "1.235E+02") == 0);
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 3, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 0.0000123, &config);
        result = result && (ZstrCompare(StrBegin(&s), "1.230e-05") == 0);
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 1234567890123.0, &config);
        result = result && (ZstrCompare(StrBegin(&s), "1.23e+12") == 0);
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 0.0, &config);
        result = result && (ZstrCompare(StrBegin(&s), "0.00") == 0);
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, F64_INFINITY, &config);
        result = result && (ZstrCompare(StrBegin(&s), "inf") == 0);
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, -F64_INFINITY, &config);
        result = result && (ZstrCompare(StrBegin(&s), "-inf") == 0);
        StrClear(&s);
        config = (StrFloatFormat) {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, F64_NAN, &config);
        result = result && (ZstrCompare(StrBegin(&s), "nan") == 0);
    
                StrFloatFormat config = {.precision = precision, .force_sci = false, .uppercase = false};
                StrFromF64(&s, f64_values[i], &config);
                f64  recovered_f64 = 0.0;
                bool success       = StrToF64(&s, &recovered_f64, NULL);
        StrClear(&s);
        StrFloatFormat fconfig = {.precision = 3, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 1e-300, &fconfig);
        f64 recovered_small = 0.0;
        success             = StrToF64(&s, &recovered_small, NULL);
        StrClear(&s);
        fconfig = (StrFloatFormat) {.precision = 3, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 1e300, &fconfig);
        f64 recovered_large = 0.0;
        success             = StrToF64(&s, &recovered_large, NULL);
    
            StrFloatFormat config = {.precision = precision, .force_sci = false, .uppercase = false};
            StrFromF64(&s, test_value, &config);
    
            // String should have expected decimal places
            // Force scientific notation
            StrFloatFormat config = {.precision = 3, .force_sci = true, .uppercase = false};
            StrFromF64(&s, sci_values[i], &config);
            bool has_e = (ZstrFindChar(StrBegin(&s), 'e') != NULL);
            result     = result && has_e;
            StrClear(&s);
            config = (StrFloatFormat) {.precision = 3, .force_sci = true, .uppercase = true};
            StrFromF64(&s, sci_values[i], &config);
            bool has_E = (ZstrFindChar(StrBegin(&s), 'E') != NULL);
            result     = result && has_E;
                Str            s      = StrInit(&alloc);
                StrFloatFormat config = {.precision = 6, .force_sci = false, .uppercase = false};
                StrFromF64(&s, test_value, &config);
    
                f64  recovered = 0.0;
        // Test StrFromF64 with NULL pointer - should abort
        StrFloatFormat config = {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(NULL, 123.45, &config);
    
        return false;
    // 4-byte "nan\0" instead of the 3-byte "nan".
    static bool test_nan_length_and_bytes(void) {
        WriteFmt("Testing StrFromF64 NaN emits exactly 3 bytes\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, F64_NAN, &config);
    
        bool result = (StrLen(&s) == 3) && (ZstrCompare(StrBegin(&s), "nan") == 0);
    // pushes it, giving 4-byte "inf\0" instead of 3-byte "inf".
    static bool test_inf_length_and_bytes(void) {
        WriteFmt("Testing StrFromF64 inf emits exactly 3 bytes\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, F64_INFINITY, &config);
    
        bool result = (StrLen(&s) == 3) && (ZstrCompare(StrBegin(&s), "inf") == 0);
    // fixed notation ("0.000100") to scientific ("1.000000e-04").
    static bool test_small_threshold_stays_fixed(void) {
        WriteFmt("Testing StrFromF64 0.0001 stays fixed notation\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 6, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 0.0001, &config);
    
        bool result = (ZstrCompare(StrBegin(&s), "0.000100") == 0);
    // of scientific ("1.00e+07").
    static bool test_large_threshold_uses_sci(void) {
        WriteFmt("Testing StrFromF64 1e7 uses scientific notation\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 1e7, &config);
    
        bool result = (ZstrCompare(StrBegin(&s), "1.00e+07") == 0);
    // zero-iteration fraction loop, giving "5.e+00" instead of "5e+00".
    static bool test_sci_precision_zero_no_dot(void) {
        WriteFmt("Testing StrFromF64 scientific precision 0 omits dot\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 0, .force_sci = true, .uppercase = false};
        StrFromF64(&s, 5.0, &config);
    
        bool result = (ZstrCompare(StrBegin(&s), "5e+00") == 0);
    // the else branch: emits '-' and "00", giving "5.00e-00" instead of "5.00e+00".
    static bool test_sci_zero_exponent_sign(void) {
        WriteFmt("Testing StrFromF64 zero exponent prints +00\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 2, .force_sci = true, .uppercase = false};
        StrFromF64(&s, 5.0, &config);
    
        bool result = (ZstrCompare(StrBegin(&s), "5.00e+00") == 0);
    // must return the destination handle, not NULL.
    static bool test_sci_exponent_success_returns_handle(void) {
        WriteFmt("Testing StrFromF64 scientific success returns handle\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 2, .force_sci = true, .uppercase = false};
        Str           *r      = StrFromF64(&s, 123.456, &config);
    
        bool result = (r == &s) && (ZstrCompare(StrBegin(&s), "1.23e+02") == 0);
    // successful call must return the destination handle.
    static bool test_fixed_integer_success_returns_handle(void) {
        WriteFmt("Testing StrFromF64 fixed success returns handle\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 2, .force_sci = false, .uppercase = false};
        Str           *r      = StrFromF64(&s, 123.0, &config);
    
        bool result = (r == &s) && (ZstrCompare(StrBegin(&s), "123.00") == 0);
    // zero-iteration fraction loop, giving "123." instead of "123".
    static bool test_fixed_precision_zero_no_dot(void) {
        WriteFmt("Testing StrFromF64 fixed precision 0 omits dot\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 0, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 123.0, &config);
    
        bool result = (ZstrCompare(StrBegin(&s), "123") == 0);
    // the fractional digits become garbage instead of "456".
    static bool test_fixed_fraction_scale_seed(void) {
        WriteFmt("Testing StrFromF64 fixed fraction scale seed\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 3, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 123.456, &config);
    
        bool result = (ZstrCompare(StrBegin(&s), "123.456") == 0);
    // raw value and emits "0.123".
    static bool test_fixed_round_half_up(void) {
        WriteFmt("Testing StrFromF64 fixed half-up rounding\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str              s     = StrInit(&alloc);
    
        StrFloatFormat config = {.precision = 3, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 0.1235, &config);
    
        bool result = (ZstrCompare(StrBegin(&s), "0.124") == 0);
    // past the fail-fast barrier. Deadend: a NULL str must abort.
    static bool test_from_f64_null_aborts(void) {
        WriteFmt("Testing StrFromF64 NULL aborts\n");
        StrFloatFormat config = {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(NULL, 1.0, &config);
        WriteFmt("Testing StrFromF64 NULL aborts\n");
        StrFloatFormat config = {.precision = 2, .force_sci = false, .uppercase = false};
        StrFromF64(NULL, 1.0, &config);
        return false;
    }
    
    static bool test_from_f64_null_aborts_high_precision(void) {
        WriteFmt("Testing StrFromF64 NULL aborts before the precision guard\n");
        StrFloatFormat config = {.precision = 18, .force_sci = false, .uppercase = false};
        StrFromF64(NULL, 1.0, &config);
        WriteFmt("Testing StrFromF64 NULL aborts before the precision guard\n");
        StrFloatFormat config = {.precision = 18, .force_sci = false, .uppercase = false};
        StrFromF64(NULL, 1.0, &config);
        return false;
    }
    // truncates to "2.2".
    static bool test_round_f64_guard_first_term(void) {
        WriteFmt("Testing StrFromF64 rounds 2.25@p1 to 2.3 (round_f64 guard, first term)\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str            s      = StrInit(&alloc);
        StrFloatFormat config = {.precision = 1, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 2.25, &config);
        bool result = (ZstrCompare(StrBegin(&s), "2.3") == 0);
        if (!result) {
    // is skipped and 2.25@p1 prints "2.2" instead of "2.3".
    static bool test_round_f64_guard_second_term(void) {
        WriteFmt("Testing StrFromF64 rounds 2.25@p1 to 2.3 (round_f64 guard, second term)\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str            s      = StrInit(&alloc);
        StrFloatFormat config = {.precision = 1, .force_sci = false, .uppercase = false};
        StrFromF64(&s, 2.25, &config);
        bool result = (ZstrCompare(StrBegin(&s), "2.3") == 0);
        if (!result) {
    //   mutant : `ok` becomes truthy -> falls through -> returns &s (non-NULL).
    static bool test_from_f64_int_oom_returns_null(void) {
        WriteFmt("Testing StrFromF64 returns NULL when an integer-digit push fails (817:28)\n");
    
        u8              buf[64] = {0};
        // push needs a 9-byte buffer and fails inside the integer-digit loop. No
        // later direct-return push can mask the mutated `ok`.
        Str *ret = StrFromF64(&s, 123456789012345.0, &config);
    
        bool result = (ret == NULL);
    //   mutant : `ok` becomes truthy -> falls through -> returns &s (non-NULL).
    static bool test_from_f64_exp_oom_returns_null(void) {
        WriteFmt("Testing StrFromF64 returns NULL when an exponent-digit push fails (790:28)\n");
    
        u8              buf[512] = {0};
        Str            s      = StrInit(&bp);
        StrFloatFormat config = {.precision = 2, .force_sci = true, .uppercase = false};
        Str           *ret    = StrFromF64(&s, 1.23e150, &config);
    
        bool result = (ret == NULL);
            test_validate_str_guard,           // StrToF64 (Str.Mutants1)
            test_str_to_u64_validate_barrier,  // StrToU64 (Str.Mutants2)
            test_from_f64_null_aborts,         // StrFromF64 (Str.Mutants3)
            test_from_f64_null_aborts_high_precision,
            test_str_to_i64_corrupt_str_aborts // StrToI64 (Str.Mutants5)
Last updated on