Skip to content

Commit b7e1f52

Browse files
committed
replace double with fp_t, replace int64_t with fp_component, refactor code to use the new typedefs
1 parent 36402dc commit b7e1f52

File tree

1 file changed

+100
-77
lines changed

1 file changed

+100
-77
lines changed

src/printf/printf.c

+100-77
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ extern "C" {
103103
#define PRINTF_DEFAULT_FLOAT_PRECISION 6
104104
#endif
105105

106+
// Enable/disable high precission double type for internal processing of floating point values (i.e. va_arg(double))
107+
// If 0, uses float which has less precision, but may be faster and reduce code size on devices without double FPU
108+
// default is 1 as this corresponds to the C standard implementation
109+
#ifndef PRINTF_USE_DOUBLE_FLOATING_POINT_PRECISION
110+
#define PRINTF_USE_DOUBLE_FLOATING_POINT_PRECISION 1
111+
#endif
112+
106113
// According to the C languages standard, printf() and related functions must be able to print any
107114
// integral number in floating-point notation, regardless of length, when using the %f specifier -
108115
// possibly hundreds of characters, potentially overflowing your buffers. In this implementation,
@@ -204,24 +211,40 @@ typedef unsigned int printf_flags_t;
204211
#error "Non-binary-radix floating-point types are unsupported."
205212
#endif
206213

207-
#if DBL_MANT_DIG == 24
214+
#if PRINTF_USE_DOUBLE_FLOATING_POINT_PRECISION == 1
215+
typedef double fp_t;
216+
#else
217+
typedef float fp_t;
218+
#endif
219+
220+
#if (DBL_MANT_DIG == 24) || (PRINTF_USE_DOUBLE_FLOATING_POINT_PRECISION == 0)
208221

209-
#define DOUBLE_SIZE_IN_BITS 32
210-
typedef uint32_t double_uint_t;
211-
#define DOUBLE_EXPONENT_MASK 0xFFU
212-
#define DOUBLE_BASE_EXPONENT 127
222+
#define FLOATING_POINT_SIZE_IN_BITS 32
223+
typedef uint32_t floating_point_uint_t;
224+
#define FLOATING_POINT_EXPONENT_MASK 0xFFU
225+
#define FLOATING_POINT_BASE_EXPONENT 127
226+
#define FLOATING_POINT_MANT_DIG 24
227+
#define FLOATING_POINT_MAX FLT_MAX
213228

214229
#elif DBL_MANT_DIG == 53
215230

216-
#define DOUBLE_SIZE_IN_BITS 64
217-
typedef uint64_t double_uint_t;
218-
#define DOUBLE_EXPONENT_MASK 0x7FFU
219-
#define DOUBLE_BASE_EXPONENT 1023
231+
#define FLOATING_POINT_SIZE_IN_BITS 64
232+
typedef uint64_t floating_point_uint_t;
233+
#define FLOATING_POINT_EXPONENT_MASK 0x7FFU
234+
#define FLOATING_POINT_BASE_EXPONENT 1023
235+
#define FLOATING_POINT_MANT_DIG 53
236+
#define FLOATING_POINT_MAX DBL_MAX
220237

221238
#else
222239
#error "Unsupported double type configuration"
223240
#endif
224-
#define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1)
241+
#define FLOATING_POINT_STORED_MANTISSA_BITS (FLOATING_POINT_MANT_DIG - 1)
242+
243+
#if PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL <= 9
244+
typedef int_fast32_t fp_component;
245+
#else
246+
typedef int_fast64_t fp_component;
247+
#endif
225248

226249
#if PRINTF_SUPPORT_LONG_LONG
227250
typedef unsigned long long printf_unsigned_value_t;
@@ -245,35 +268,35 @@ typedef unsigned int printf_size_t;
245268
// trailing '\0'.
246269

247270
typedef union {
248-
double_uint_t U;
249-
double F;
250-
} double_with_bit_access;
271+
floating_point_uint_t U;
272+
fp_t F;
273+
} floating_point_with_bit_access;
251274

252275
// This is unnecessary in C99, since compound initializers can be used,
253276
// but:
254277
// 1. Some compilers are finicky about this;
255278
// 2. Some people may want to convert this to C89;
256279
// 3. If you try to use it as C++, only C++20 supports compound literals
257-
static inline double_with_bit_access get_bit_access(double x)
280+
static inline floating_point_with_bit_access get_bit_access(fp_t x)
258281
{
259-
double_with_bit_access dwba;
282+
floating_point_with_bit_access dwba;
260283
dwba.F = x;
261284
return dwba;
262285
}
263286

264-
static inline int get_sign_bit(double x)
287+
static inline int get_sign_bit(fp_t x)
265288
{
266289
// The sign is stored in the highest bit
267-
return (int) (get_bit_access(x).U >> (DOUBLE_SIZE_IN_BITS - 1));
290+
return (int) (get_bit_access(x).U >> (FLOATING_POINT_SIZE_IN_BITS - 1));
268291
}
269292

270-
static inline int get_exp2(double_with_bit_access x)
293+
static inline int get_exp2(floating_point_with_bit_access x)
271294
{
272295
// The exponent in an IEEE-754 floating-point number occupies a contiguous
273296
// sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An
274297
// unsigned offset from some negative value (with the extremal offset values reserved for
275298
// special use).
276-
return (int)((x.U >> DOUBLE_STORED_MANTISSA_BITS ) & DOUBLE_EXPONENT_MASK) - DOUBLE_BASE_EXPONENT;
299+
return (int)((x.U >> FLOATING_POINT_STORED_MANTISSA_BITS ) & FLOATING_POINT_EXPONENT_MASK) - FLOATING_POINT_BASE_EXPONENT;
277300
}
278301
#define PRINTF_ABS(_x) ( (_x) > 0 ? (_x) : -(_x) )
279302

@@ -539,56 +562,56 @@ static void print_integer(output_gadget_t* output, printf_unsigned_value_t value
539562

540563
#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS)
541564

542-
// Stores a fixed-precision representation of a double relative
565+
// Stores a fixed-precision representation of a float/double relative
543566
// to a fixed precision (which cannot be determined by examining this structure)
544-
struct double_components {
545-
int_fast64_t integral;
546-
int_fast64_t fractional;
547-
// ... truncation of the actual fractional part of the double value, scaled
567+
struct floating_point_components {
568+
fp_component integral;
569+
fp_component fractional;
570+
// ... truncation of the actual fractional part of the float/double value, scaled
548571
// by the precision value
549572
bool is_negative;
550573
};
551574

552575
#define NUM_DECIMAL_DIGITS_IN_INT64_T 18
553576
#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T
554-
static const double powers_of_10[NUM_DECIMAL_DIGITS_IN_INT64_T] = {
555-
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08,
556-
1e09, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17
577+
static const fp_t powers_of_10[NUM_DECIMAL_DIGITS_IN_INT64_T] = {
578+
(fp_t) 1e00, (fp_t) 1e01, (fp_t) 1e02, (fp_t) 1e03, (fp_t) 1e04, (fp_t) 1e05, (fp_t) 1e06, (fp_t) 1e07, (fp_t) 1e08,
579+
(fp_t) 1e09, (fp_t) 1e10, (fp_t) 1e11, (fp_t) 1e12, (fp_t) 1e13, (fp_t) 1e14, (fp_t) 1e15, (fp_t) 1e16, (fp_t) 1e17
557580
};
558581

559582
#define PRINTF_MAX_SUPPORTED_PRECISION NUM_DECIMAL_DIGITS_IN_INT64_T - 1
560583

561584

562-
// Break up a double number - which is known to be a finite non-negative number -
585+
// Break up a float/double number - which is known to be a finite non-negative number -
563586
// into its base-10 parts: integral - before the decimal point, and fractional - after it.
564587
// Taken the precision into account, but does not change it even internally.
565-
static struct double_components get_components(double number, printf_size_t precision)
588+
static struct floating_point_components get_components(fp_t number, printf_size_t precision)
566589
{
567-
struct double_components number_;
590+
struct floating_point_components number_;
568591
number_.is_negative = get_sign_bit(number);
569-
double abs_number = (number_.is_negative) ? -number : number;
570-
number_.integral = (int_fast64_t)abs_number;
571-
double remainder = (abs_number - (double) number_.integral) * powers_of_10[precision];
572-
number_.fractional = (int_fast64_t)remainder;
592+
fp_t abs_number = (number_.is_negative) ? -number : number;
593+
number_.integral = (fp_component)abs_number;
594+
fp_t remainder = (abs_number - (fp_t) number_.integral) * powers_of_10[precision];
595+
number_.fractional = (fp_component)remainder;
573596

574-
remainder -= (double) number_.fractional;
597+
remainder -= (fp_t) number_.fractional;
575598

576-
if (remainder > 0.5) {
599+
if (remainder > (fp_t) 0.5) {
577600
++number_.fractional;
578601
// handle rollover, e.g. case 0.99 with precision 1 is 1.0
579-
if ((double) number_.fractional >= powers_of_10[precision]) {
602+
if ((fp_t) number_.fractional >= powers_of_10[precision]) {
580603
number_.fractional = 0;
581604
++number_.integral;
582605
}
583606
}
584-
else if ((remainder == 0.5) && ((number_.fractional == 0U) || (number_.fractional & 1U))) {
607+
else if ((remainder == (fp_t) 0.5) && ((number_.fractional == 0U) || (number_.fractional & 1U))) {
585608
// if halfway, round up if odd OR if last digit is 0
586609
++number_.fractional;
587610
}
588611

589612
if (precision == 0U) {
590-
remainder = abs_number - (double) number_.integral;
591-
if ((!(remainder < 0.5) || (remainder > 0.5)) && (number_.integral & 1)) {
613+
remainder = abs_number - (fp_t) number_.integral;
614+
if ((!(remainder < (fp_t) 0.5) || (remainder > (fp_t) 0.5)) && (number_.integral & 1)) {
592615
// exactly 0.5 and ODD, then round up
593616
// 1.5 -> 2, but 2.5 -> 2
594617
++number_.integral;
@@ -599,21 +622,21 @@ static struct double_components get_components(double number, printf_size_t prec
599622

600623
#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS
601624
struct scaling_factor {
602-
double raw_factor;
625+
fp_t raw_factor;
603626
bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it
604627
};
605628

606-
static double apply_scaling(double num, struct scaling_factor normalization)
629+
static fp_t apply_scaling(fp_t num, struct scaling_factor normalization)
607630
{
608631
return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor;
609632
}
610633

611-
static double unapply_scaling(double normalized, struct scaling_factor normalization)
634+
static fp_t unapply_scaling(fp_t normalized, struct scaling_factor normalization)
612635
{
613636
return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor;
614637
}
615638

616-
static struct scaling_factor update_normalization(struct scaling_factor sf, double extra_multiplicative_factor)
639+
static struct scaling_factor update_normalization(struct scaling_factor sf, fp_t extra_multiplicative_factor)
617640
{
618641
struct scaling_factor result;
619642
if (sf.multiply) {
@@ -637,37 +660,37 @@ static struct scaling_factor update_normalization(struct scaling_factor sf, doub
637660
return result;
638661
}
639662

640-
static struct double_components get_normalized_components(bool negative, printf_size_t precision, double non_normalized, struct scaling_factor normalization)
663+
static struct floating_point_components get_normalized_components(bool negative, printf_size_t precision, fp_t non_normalized, struct scaling_factor normalization)
641664
{
642-
struct double_components components;
665+
struct floating_point_components components;
643666
components.is_negative = negative;
644-
components.integral = (int_fast64_t) apply_scaling(non_normalized, normalization);
645-
double remainder = non_normalized - unapply_scaling((double) components.integral, normalization);
646-
double prec_power_of_10 = powers_of_10[precision];
667+
components.integral = (fp_component) apply_scaling(non_normalized, normalization);
668+
fp_t remainder = non_normalized - unapply_scaling((fp_t) components.integral, normalization);
669+
fp_t prec_power_of_10 = powers_of_10[precision];
647670
struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10);
648-
double scaled_remainder = apply_scaling(remainder, account_for_precision);
649-
double rounding_threshold = 0.5;
671+
fp_t scaled_remainder = apply_scaling(remainder, account_for_precision);
672+
fp_t rounding_threshold = 0.5;
650673

651674
if (precision == 0U) {
652675
components.fractional = 0;
653676
components.integral += (scaled_remainder >= rounding_threshold);
654677
if (scaled_remainder == rounding_threshold) {
655678
// banker's rounding: Round towards the even number (making the mean error 0)
656-
components.integral &= ~((int_fast64_t) 0x1);
679+
components.integral &= ~((fp_component) 0x1);
657680
}
658681
}
659682
else {
660-
components.fractional = (int_fast64_t) scaled_remainder;
661-
scaled_remainder -= (double) components.fractional;
683+
components.fractional = (fp_component) scaled_remainder;
684+
scaled_remainder -= (fp_t) components.fractional;
662685

663686
components.fractional += (scaled_remainder >= rounding_threshold);
664687
if (scaled_remainder == rounding_threshold) {
665688
// banker's rounding: Round towards the even number (making the mean error 0)
666-
components.fractional &= ~((int_fast64_t) 0x1);
689+
components.fractional &= ~((fp_component) 0x1);
667690
}
668691
// handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100),
669692
// and must then be corrected into (1, 0).
670-
if ((double) components.fractional >= prec_power_of_10) {
693+
if ((fp_t) components.fractional >= prec_power_of_10) {
671694
components.fractional = 0;
672695
++components.integral;
673696
}
@@ -677,7 +700,7 @@ static struct double_components get_normalized_components(bool negative, printf_
677700
#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS
678701

679702
static void print_broken_up_decimal(
680-
struct double_components number_, output_gadget_t* output, printf_size_t precision,
703+
struct floating_point_components number_, output_gadget_t* output, printf_size_t precision,
681704
printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len)
682705
{
683706
if (precision != 0U) {
@@ -688,7 +711,7 @@ static void print_broken_up_decimal(
688711
// %g/%G mandates we skip the trailing 0 digits...
689712
if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) {
690713
while(true) {
691-
int_fast64_t digit = number_.fractional % 10U;
714+
fp_component digit = number_.fractional % 10U;
692715
if (digit != 0) {
693716
break;
694717
}
@@ -759,44 +782,44 @@ static void print_broken_up_decimal(
759782
}
760783

761784
// internal ftoa for fixed decimal floating point
762-
static void print_decimal_number(output_gadget_t* output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len)
785+
static void print_decimal_number(output_gadget_t* output, fp_t number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len)
763786
{
764-
struct double_components value_ = get_components(number, precision);
787+
struct floating_point_components value_ = get_components(number, precision);
765788
print_broken_up_decimal(value_, output, precision, width, flags, buf, len);
766789
}
767790

768791
#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS
769792
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <[email protected]>
770-
static void print_exponential_number(output_gadget_t* output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len)
793+
static void print_exponential_number(output_gadget_t* output, fp_t number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len)
771794
{
772795
const bool negative = get_sign_bit(number);
773796
// This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it
774-
double abs_number = negative ? -number : number;
797+
fp_t abs_number = negative ? -number : number;
775798

776799
int exp10;
777800
bool abs_exp10_covered_by_powers_table;
778801
struct scaling_factor normalization;
779802

780803

781804
// Determine the decimal exponent
782-
if (abs_number == 0.0) {
805+
if (abs_number == (fp_t) 0.0) {
783806
// TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally.
784807
exp10 = 0; // ... and no need to set a normalization factor or check the powers table
785808
}
786809
else {
787-
double_with_bit_access conv = get_bit_access(abs_number);
810+
floating_point_with_bit_access conv = get_bit_access(abs_number);
788811
{
789812
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
790813
int exp2 = get_exp2(conv);
791814
// drop the exponent, so conv.F comes into the range [1,2)
792-
conv.U = (conv.U & (( (double_uint_t)(1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) | ((double_uint_t) DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS);
815+
conv.U = (conv.U & (( (floating_point_uint_t)(1) << FLOATING_POINT_STORED_MANTISSA_BITS) - 1U)) | ((floating_point_uint_t) FLOATING_POINT_BASE_EXPONENT << FLOATING_POINT_STORED_MANTISSA_BITS);
793816
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
794-
exp10 = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
817+
exp10 = (int)((fp_t) 0.1760912590558 + ((fp_t) exp2) * ((fp_t) 0.301029995663981) + (conv.F - (fp_t) 1.5) * ((fp_t) 0.289529654602168));
795818
// now we want to compute 10^exp10 but we want to be sure it won't overflow
796-
exp2 = (int)(exp10 * 3.321928094887362 + 0.5);
797-
const double z = exp10 * 2.302585092994046 - exp2 * 0.6931471805599453;
798-
const double z2 = z * z;
799-
conv.U = ((double_uint_t)(exp2) + DOUBLE_BASE_EXPONENT) << DOUBLE_STORED_MANTISSA_BITS;
819+
exp2 = (int)(((fp_t) exp10) * ((fp_t) 3.321928094887362) + (fp_t) 0.5);
820+
const fp_t z = ((fp_t) exp10) * ((fp_t) 2.302585092994046) - ((fp_t) exp2) * ((fp_t) 0.6931471805599453);
821+
const fp_t z2 = z * z;
822+
conv.U = ((floating_point_uint_t)(exp2) + FLOATING_POINT_BASE_EXPONENT) << FLOATING_POINT_STORED_MANTISSA_BITS;
800823
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
801824
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
802825
// correct for rounding errors
@@ -833,7 +856,7 @@ static void print_exponential_number(output_gadget_t* output, double number, pri
833856

834857
normalization.multiply = (exp10 < 0 && abs_exp10_covered_by_powers_table);
835858
bool should_skip_normalization = (fall_back_to_decimal_only_mode || exp10 == 0);
836-
struct double_components decimal_part_components =
859+
struct floating_point_components decimal_part_components =
837860
should_skip_normalization ?
838861
get_components(negative ? -abs_number : abs_number, precision) :
839862
get_normalized_components(negative, precision, abs_number, normalization);
@@ -894,7 +917,7 @@ static void print_exponential_number(output_gadget_t* output, double number, pri
894917
}
895918
#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS
896919

897-
static void print_floating_point(output_gadget_t* output, double value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential)
920+
static void print_floating_point(output_gadget_t* output, fp_t value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential)
898921
{
899922
char buf[PRINTF_FTOA_BUFFER_SIZE];
900923
printf_size_t len = 0U;
@@ -904,17 +927,17 @@ static void print_floating_point(output_gadget_t* output, double value, printf_s
904927
out_rev_(output, "nan", 3, width, flags);
905928
return;
906929
}
907-
if (value < -DBL_MAX) {
930+
if (value < -FLOATING_POINT_MAX) {
908931
out_rev_(output, "fni-", 4, width, flags);
909932
return;
910933
}
911-
if (value > DBL_MAX) {
934+
if (value > FLOATING_POINT_MAX) {
912935
out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
913936
return;
914937
}
915938

916939
if (!prefer_exponential &&
917-
((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) {
940+
((value > (fp_t) PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < - (fp_t) PRINTF_FLOAT_NOTATION_THRESHOLD))) {
918941
// The required behavior of standard printf is to print _every_ integral-part digit -- which could mean
919942
// printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated
920943
// implementation.
@@ -1171,7 +1194,7 @@ static int _vsnprintf(output_gadget_t* output, const char* format, va_list args)
11711194
case 'f' :
11721195
case 'F' :
11731196
if (*format == 'F') flags |= FLAGS_UPPERCASE;
1174-
print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL);
1197+
print_floating_point(output, (fp_t) va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL);
11751198
format++;
11761199
break;
11771200
#endif
@@ -1182,7 +1205,7 @@ static int _vsnprintf(output_gadget_t* output, const char* format, va_list args)
11821205
case 'G':
11831206
if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
11841207
if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
1185-
print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL);
1208+
print_floating_point(output, (fp_t) va_arg(args, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL);
11861209
format++;
11871210
break;
11881211
#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS

0 commit comments

Comments
 (0)