1 /*
2 * ratio.c
3 * Exact rational numbers.
4 *
5 * Copyright (C) 2003-2017 Cosmin Truta.
6 *
7 * This software is distributed under the zlib license.
8 * Please see the accompanying LICENSE file.
9 */
10
11 #include "ratio.h"
12
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18
19 #ifdef OPNG_LLONG_T_DEFINED
20 typedef opng_llong_t opng_xlong_impl_t;
21 typedef opng_ullong_t opng_uxlong_impl_t;
22 #define OPNG_XLONG_IMPL_FORMAT_PREFIX OPNG_LLONG_FORMAT_PREFIX
23 #else
24 typedef long opng_xlong_impl_t;
25 typedef unsigned long opng_uxlong_impl_t;
26 #define OPNG_XLONG_IMPL_FORMAT_PREFIX "l"
27 #endif
28
29
30 /*
31 * Writes formatted output to a memory buffer.
32 * This is a wrapper to [v]snprintf which avoids well-known defects
33 * occurring in some of the underlying snprintf implementations.
34 * The function returns the number of characters written, excluding the
35 * null-termination character, if the buffer size is large enough, or -1
36 * otherwise. (Unlike the proper snprintf, this function does not return
37 * a number larger than zero if the buffer size is too small.)
38 */
39 static int
opng_snprintf_impl(char * buffer,size_t buffer_size,const char * format,...)40 opng_snprintf_impl(char *buffer, size_t buffer_size, const char *format, ...)
41 {
42
43 #if defined _WIN32 || defined __WIN32__ || defined _WIN64 || defined __WIN64__
44 #define OPNG_VSNPRINTF _vsnprintf
45 #else
46 #define OPNG_VSNPRINTF vsnprintf
47 #endif
48
49 va_list arg_ptr;
50 int result;
51
52 va_start(arg_ptr, format);
53 result = OPNG_VSNPRINTF(buffer, buffer_size, format, arg_ptr);
54 va_end(arg_ptr);
55
56 if (result < 0 || (size_t)result >= buffer_size)
57 {
58 /* Guard against broken [v]snprintf implementations. */
59 if (buffer_size > 0)
60 buffer[buffer_size - 1] = '\0';
61 return -1;
62 }
63 return result;
64
65 #undef OPNG_VSNPRINTF
66
67 }
68
69 /*
70 * Writes a decomposed rational value to a memory buffer.
71 * This is the base implementation used internally by the the other
72 * ratio-to-string conversion functions.
73 */
74 static int
opng_sprint_uratio_impl(char * buffer,size_t buffer_size,opng_uxlong_impl_t num,opng_uxlong_impl_t denom,int always_percent)75 opng_sprint_uratio_impl(char *buffer, size_t buffer_size,
76 opng_uxlong_impl_t num, opng_uxlong_impl_t denom,
77 int always_percent)
78 {
79 /* (1) num/denom == 0/0 ==> print "??%"
80 * (2) num/denom == INFINITY ==> print "INFINITY%"
81 * (3) 0 <= num/denom < 99.995% ==> use the percent format "99.99%"
82 * if always_percent:
83 * (4) 0.995 <= num/denom < INFINITY ==> use the percent format "999%"
84 * else:
85 * (5) 0.995 <= num/denom < 99.995 ==> use the factor format "9.99x"
86 * (6) 99.5 <= num/denom < INFINITY ==> use the factor format "999x"
87 * end if
88 */
89
90 opng_uxlong_impl_t integer_part, remainder;
91 unsigned int fractional_part, scale;
92 double scaled_ratio;
93
94 /* (1,2): num/denom == 0/0 or num/denom == INFINITY */
95 if (denom == 0)
96 return opng_snprintf_impl(buffer, buffer_size,
97 num == 0 ? "??%%" : "INFINITY%%");
98
99 /* (3): 0 <= num/denom < 99.995% */
100 /* num/denom < 99.995% <==> denom/(denom-num) < 20000 */
101 if (num < denom && denom / (denom - num) < 20000)
102 {
103 scale = 10000;
104 scaled_ratio = ((double)num * (double)scale) / (double)denom;
105 fractional_part = (unsigned int)(scaled_ratio + 0.5);
106 /* Adjust the scaled result in the event of a roundoff error. */
107 /* Such error may occur only if the numerator is extremely large. */
108 if (fractional_part >= scale)
109 fractional_part = scale - 1;
110 return opng_snprintf_impl(buffer, buffer_size,
111 "%u.%02u%%",
112 fractional_part / 100,
113 fractional_part % 100);
114 }
115
116 /* Extract the integer part out of the fraction for the remaining cases. */
117 integer_part = num / denom;
118 remainder = num % denom;
119 scale = 100;
120 scaled_ratio = ((double)remainder * (double)scale) / (double)denom;
121 fractional_part = (unsigned int)(scaled_ratio + 0.5);
122 if (fractional_part >= scale)
123 {
124 fractional_part = 0;
125 ++integer_part;
126 }
127
128 /* (4): 0.995 <= num/denom < INFINITY */
129 if (always_percent)
130 return opng_snprintf_impl(buffer, buffer_size,
131 "%" OPNG_XLONG_IMPL_FORMAT_PREFIX "u%02u%%",
132 integer_part, fractional_part);
133
134 /* (5): 0.995 <= num/denom < 99.995 */
135 if (integer_part < 100)
136 return opng_snprintf_impl(buffer, buffer_size,
137 "%" OPNG_XLONG_IMPL_FORMAT_PREFIX "u.%02ux",
138 integer_part, fractional_part);
139
140 /* (6): 99.5 <= num/denom < INFINITY */
141 /* Round to the nearest integer. */
142 /* Recalculate the integer part, for corner cases like 123.999. */
143 integer_part = num / denom;
144 if (remainder > (denom - 1) / 2)
145 ++integer_part;
146 return opng_snprintf_impl(buffer, buffer_size,
147 "%" OPNG_XLONG_IMPL_FORMAT_PREFIX "ux",
148 integer_part);
149 }
150
151 /*
152 * Converts a rational value to a compact factor string representation.
153 */
154 int
opng_ulratio_to_factor_string(char * buffer,size_t buffer_size,const struct opng_ulratio * ratio)155 opng_ulratio_to_factor_string(char *buffer, size_t buffer_size,
156 const struct opng_ulratio *ratio)
157 {
158 opng_uxlong_impl_t num = ratio->num;
159 opng_uxlong_impl_t denom = ratio->denom;
160 return opng_sprint_uratio_impl(buffer, buffer_size, num, denom, 0);
161 }
162
163 /*
164 * Converts a rational value to a compact percent string representation.
165 */
166 int
opng_ulratio_to_percent_string(char * buffer,size_t buffer_size,const struct opng_ulratio * ratio)167 opng_ulratio_to_percent_string(char *buffer, size_t buffer_size,
168 const struct opng_ulratio *ratio)
169 {
170 opng_uxlong_impl_t num = ratio->num;
171 opng_uxlong_impl_t denom = ratio->denom;
172 return opng_sprint_uratio_impl(buffer, buffer_size, num, denom, 1);
173 }
174
175 #ifdef OPNG_LLONG_T_DEFINED
176
177 /*
178 * Converts a rational value to a compact factor string representation.
179 */
180 int
opng_ullratio_to_factor_string(char * buffer,size_t buffer_size,const struct opng_ullratio * ratio)181 opng_ullratio_to_factor_string(char *buffer, size_t buffer_size,
182 const struct opng_ullratio *ratio)
183 {
184 opng_uxlong_impl_t num = ratio->num;
185 opng_uxlong_impl_t denom = ratio->denom;
186 return opng_sprint_uratio_impl(buffer, buffer_size, num, denom, 0);
187 }
188
189 /*
190 * Converts a rational value to a compact percent string representation.
191 */
192 int
opng_ullratio_to_percent_string(char * buffer,size_t buffer_size,const struct opng_ullratio * ratio)193 opng_ullratio_to_percent_string(char *buffer, size_t buffer_size,
194 const struct opng_ullratio *ratio)
195 {
196 opng_uxlong_impl_t num = ratio->num;
197 opng_uxlong_impl_t denom = ratio->denom;
198 return opng_sprint_uratio_impl(buffer, buffer_size, num, denom, 1);
199 }
200
201 #endif /* OPNG_LLONG_T_DEFINED */
202