回答編集履歴
2
バグがあることを明記
answer
CHANGED
@@ -108,6 +108,8 @@
|
|
108
108
|
コードが正しい保証・バグなく動く保証はまったくないです。見苦しいコードですが参考に...ならないか。
|
109
109
|
とりあえず追記しておきます。
|
110
110
|
|
111
|
+
さらなる追記: **バグがありました** 。例えば整数部・小数部とも省略されてしまった場合など。修正は容易ですが、今後修正していくことはできないため、鵜呑みにされることを避ける意味でも、バグがあるままにしておきます。これ以上の編集はしません。
|
112
|
+
|
111
113
|
```c
|
112
114
|
#include <stdio.h>
|
113
115
|
#include <stdlib.h>
|
1
より近い実装を追記
answer
CHANGED
@@ -100,4 +100,273 @@
|
|
100
100
|
---
|
101
101
|
|
102
102
|
このようなとき、どの部分でつまっているかを確かめるために printf デバッグが有効です。
|
103
|
-
私はまずソースの (==A==) の部分で n, i, o を表示させてみました。終了しない原因が integer() 関数にないことや、 integer() 関数の戻り値が正しいかどうかがここで確かめられます。次に入れるとしたら (==B==) の位置です。n と i を表示させながらループを観察します。そうすると `i == -1` のときにループから戻ってこないことが分かります。きっと pow() 関数の、負値のときにバグがあるのだろうと推測することができます。
|
103
|
+
私はまずソースの (==A==) の部分で n, i, o を表示させてみました。終了しない原因が integer() 関数にないことや、 integer() 関数の戻り値が正しいかどうかがここで確かめられます。次に入れるとしたら (==B==) の位置です。n と i を表示させながらループを観察します。そうすると `i == -1` のときにループから戻ってこないことが分かります。きっと pow() 関数の、負値のときにバグがあるのだろうと推測することができます。
|
104
|
+
|
105
|
+
## 追記
|
106
|
+
|
107
|
+
もっと `strtod()` の挙動に近い `str_to_d()` を書いてみました。。。
|
108
|
+
コードが正しい保証・バグなく動く保証はまったくないです。見苦しいコードですが参考に...ならないか。
|
109
|
+
とりあえず追記しておきます。
|
110
|
+
|
111
|
+
```c
|
112
|
+
#include <stdio.h>
|
113
|
+
#include <stdlib.h>
|
114
|
+
#include <math.h>
|
115
|
+
#include <stdbool.h>
|
116
|
+
|
117
|
+
#define WHITESPACE " \t\n\v\f\r"
|
118
|
+
|
119
|
+
// str の中から ch を探し、インデックスを返す。
|
120
|
+
// 見つからないときは -1
|
121
|
+
int find_ch(char const *str, char ch);
|
122
|
+
|
123
|
+
// ch が空白文字 WHITESPACE に含まれているかを返す
|
124
|
+
bool is_whitespace(char ch);
|
125
|
+
|
126
|
+
// 文字を十進整数にする
|
127
|
+
// 範囲外の場合は -1
|
128
|
+
int dec_to_digit(char ch);
|
129
|
+
|
130
|
+
// 文字を十六進整数にする
|
131
|
+
// 範囲外の場合は -1
|
132
|
+
int hex_to_digit(char ch);
|
133
|
+
|
134
|
+
// 符号を取得し、 nptr を進める
|
135
|
+
// 符号は、+なら1、-なら-1
|
136
|
+
int sign_and_eat(char const **nptr);
|
137
|
+
|
138
|
+
// ch が大文字だったら、小文字にする
|
139
|
+
char to_lower(char ch);
|
140
|
+
|
141
|
+
// s と t の先頭 n 文字が (大文字小文字の区別なしで) 等しいか返す。
|
142
|
+
bool is_same_n(char const *s, char const *t, size_t n);
|
143
|
+
|
144
|
+
// 基数 (0x) を取得し、 nptr を進める
|
145
|
+
int base_and_eat(char const **nptr);
|
146
|
+
|
147
|
+
// 整数部分 (小数点 or 指数表示の e, p まで) を取得し、
|
148
|
+
// nptr を進める。
|
149
|
+
int integer_part_and_eat(char const **nptr, int base);
|
150
|
+
|
151
|
+
// 小数部分 (小数点から指数表示の e, p か末尾まで) を取得し、
|
152
|
+
// nptr を進める。
|
153
|
+
double fraction_part_and_eat(char const **nptr, int base);
|
154
|
+
|
155
|
+
// base の n 乗を求める。
|
156
|
+
double pwr(double base, int n);
|
157
|
+
|
158
|
+
// 文字列を double に変換する。
|
159
|
+
double str_to_d(char const *nptr, char const **endptr);
|
160
|
+
|
161
|
+
int find_ch(char const *str, char ch) {
|
162
|
+
for (int i = 0; str[i]; i++) {
|
163
|
+
if (str[i] == ch) return i;
|
164
|
+
}
|
165
|
+
return -1;
|
166
|
+
}
|
167
|
+
|
168
|
+
bool is_whitespace(char ch) {
|
169
|
+
return find_ch(WHITESPACE, ch) != -1;
|
170
|
+
}
|
171
|
+
|
172
|
+
int dec_to_digit(char ch) {
|
173
|
+
if ('0' <= ch && ch <= '9') return ch - '0';
|
174
|
+
return -1;
|
175
|
+
}
|
176
|
+
|
177
|
+
int hex_to_digit(char ch) {
|
178
|
+
// まず10進の範囲での変換を試みる
|
179
|
+
int dg = dec_to_digit(ch);
|
180
|
+
if (dg != -1) return dg;
|
181
|
+
|
182
|
+
// ダメなら文字を変換する
|
183
|
+
if ('A' <= ch && ch <= 'F') return 10 + (ch - 'A');
|
184
|
+
if ('a' <= ch && ch <= 'f') return 10 + (ch - 'a');
|
185
|
+
return -1;
|
186
|
+
}
|
187
|
+
|
188
|
+
int sign_and_eat(char const **nptr) {
|
189
|
+
// 符号がなければ + となるため
|
190
|
+
int sign = 1;
|
191
|
+
if (**nptr == '+') {
|
192
|
+
(*nptr)++;
|
193
|
+
} else if (**nptr == '-') {
|
194
|
+
sign = -1;
|
195
|
+
(*nptr)++;
|
196
|
+
}
|
197
|
+
return sign;
|
198
|
+
}
|
199
|
+
|
200
|
+
char to_lower(char ch) {
|
201
|
+
if ('A' <= ch && ch <= 'Z') {
|
202
|
+
ch = ch - 'A' + 'a';
|
203
|
+
}
|
204
|
+
return ch;
|
205
|
+
}
|
206
|
+
|
207
|
+
bool is_same_n(char const *s, char const *t, size_t n) {
|
208
|
+
for (size_t i = 0; i < n; i++) {
|
209
|
+
if (!s[i] || !t[i]) return false;
|
210
|
+
if (to_lower(s[i]) != to_lower(t[i])) return false;
|
211
|
+
}
|
212
|
+
return true;
|
213
|
+
}
|
214
|
+
|
215
|
+
int base_and_eat(char const **nptr) {
|
216
|
+
if (is_same_n(*nptr, "0x", 2)) {
|
217
|
+
// 0x 始まり == 16進数
|
218
|
+
*nptr += 2;
|
219
|
+
return 16;
|
220
|
+
}
|
221
|
+
return 10;
|
222
|
+
}
|
223
|
+
|
224
|
+
int integer_part_and_eat(char const **nptr, int base, int (*to_digit)(char)) {
|
225
|
+
int ans = 0;
|
226
|
+
int dg;
|
227
|
+
// 数字である限りは変換
|
228
|
+
for ( ; **nptr && (dg = to_digit(**nptr)) != -1; (*nptr)++) {
|
229
|
+
ans *= base;
|
230
|
+
ans += dg;
|
231
|
+
}
|
232
|
+
return ans;
|
233
|
+
}
|
234
|
+
|
235
|
+
double fraction_part_and_eat(char const **nptr, int base, int (*to_digit)(char)) {
|
236
|
+
double ans = 0;
|
237
|
+
int dg;
|
238
|
+
double pwr = 1. / base;
|
239
|
+
// 数字である限りは変換
|
240
|
+
for ( ; **nptr && (dg = to_digit(**nptr)) != -1; (*nptr)++) {
|
241
|
+
ans += pwr * dg;
|
242
|
+
pwr /= base;
|
243
|
+
}
|
244
|
+
return ans;
|
245
|
+
}
|
246
|
+
|
247
|
+
double pwr(double base, int n) {
|
248
|
+
double ans = 1;
|
249
|
+
if (n > 0) {
|
250
|
+
for (int i = 0; i < n; i++)
|
251
|
+
ans *= base;
|
252
|
+
} else if (n < 0) {
|
253
|
+
for (int i = 0; i < -n; i++)
|
254
|
+
ans /= base;
|
255
|
+
}
|
256
|
+
return ans;
|
257
|
+
}
|
258
|
+
|
259
|
+
double str_to_d(char const *nptr, char const **endptr) {
|
260
|
+
#define FINISH_WHEN_ENDS() do { if (!*nptr) goto finish; } while (false)
|
261
|
+
// とりあえず NAN, INFINITY に対応する
|
262
|
+
if (is_same_n(nptr, "nan", 3)) {
|
263
|
+
*endptr = nptr + 3;
|
264
|
+
return NAN;
|
265
|
+
} else if (is_same_n(nptr, "infinity", 8)) {
|
266
|
+
*endptr = nptr + 8;
|
267
|
+
return INFINITY;
|
268
|
+
}
|
269
|
+
|
270
|
+
char const *oldnptr;
|
271
|
+
double ans = 0;
|
272
|
+
int sign = 1;
|
273
|
+
// whitespace の読み飛ばし
|
274
|
+
while (*nptr && is_whitespace(*nptr)) nptr++;
|
275
|
+
|
276
|
+
// 符号ビット
|
277
|
+
FINISH_WHEN_ENDS();
|
278
|
+
sign = sign_and_eat(&nptr);
|
279
|
+
|
280
|
+
// 基数
|
281
|
+
FINISH_WHEN_ENDS();
|
282
|
+
int base; base = base_and_eat(&nptr);
|
283
|
+
char const *expchars; expchars = base == 10 ? "eE" : "pP";
|
284
|
+
int (*to_digit)(char); to_digit = base == 10 ? dec_to_digit : hex_to_digit;
|
285
|
+
|
286
|
+
// 数字または小数点でないなら終了
|
287
|
+
FINISH_WHEN_ENDS();
|
288
|
+
if (to_digit(*nptr) == -1 && *nptr != '.') goto finish;
|
289
|
+
|
290
|
+
// 整数部分読み込み
|
291
|
+
oldnptr = nptr;
|
292
|
+
ans += integer_part_and_eat(&nptr, base, to_digit);
|
293
|
+
bool read_integer; read_integer = oldnptr != nptr;
|
294
|
+
|
295
|
+
// 小数点または expchars でなければ終了
|
296
|
+
FINISH_WHEN_ENDS();
|
297
|
+
bool read_dot; read_dot = false;
|
298
|
+
if (*nptr == '.') {
|
299
|
+
read_dot = true;
|
300
|
+
nptr++;
|
301
|
+
} else if (find_ch(expchars, *nptr) == -1) {
|
302
|
+
goto finish;
|
303
|
+
}
|
304
|
+
|
305
|
+
// 小数部分読み込み
|
306
|
+
oldnptr = nptr; // 変化したか確かめるため、とりあえず保存しておく。
|
307
|
+
ans += fraction_part_and_eat(&nptr, base, to_digit);
|
308
|
+
bool read_fraction; read_fraction = oldnptr != nptr;
|
309
|
+
|
310
|
+
// expchars でないか、 expchars であっても、
|
311
|
+
// 整数部省略小数であってかつ1つも進んでいない場合は終了
|
312
|
+
FINISH_WHEN_ENDS();
|
313
|
+
if (find_ch(expchars, *nptr) == -1
|
314
|
+
|| (!read_integer && read_dot && !read_fraction)) {
|
315
|
+
if (!read_integer && read_dot && !read_fraction) {
|
316
|
+
// なぜか標準はドットから返すので、 nptr を一つ戻しておく。
|
317
|
+
nptr--;
|
318
|
+
}
|
319
|
+
goto finish;
|
320
|
+
}
|
321
|
+
nptr++;
|
322
|
+
|
323
|
+
// 符号
|
324
|
+
FINISH_WHEN_ENDS();
|
325
|
+
int exp_sign; exp_sign = sign_and_eat(&nptr);
|
326
|
+
|
327
|
+
// 値
|
328
|
+
FINISH_WHEN_ENDS();
|
329
|
+
int exp_num; exp_num = integer_part_and_eat(&nptr, base, to_digit);
|
330
|
+
// 16進数の指数表示は底が2なので。
|
331
|
+
double mulr; mulr = pwr(base == 10 ? 10 : 2, exp_num * exp_sign);
|
332
|
+
ans *= mulr;
|
333
|
+
|
334
|
+
if (*nptr) {
|
335
|
+
// なぜか標準は e を食べないので、戻す。
|
336
|
+
nptr--;
|
337
|
+
}
|
338
|
+
|
339
|
+
finish:
|
340
|
+
if (endptr) *endptr = nptr;
|
341
|
+
return sign * ans;
|
342
|
+
}
|
343
|
+
|
344
|
+
int main(void)
|
345
|
+
{
|
346
|
+
char const *tests[] = {
|
347
|
+
"134", "-542", "4234.342", "-543.435", ".5453", "-.2343", "45e2", "453.34E+4", "545.3e-2", ".e2", "4.e2",
|
348
|
+
"0x42", "-0x53", "0x32.42", "-0x543.53", "0x.423", "-0x.432", "0x234p2", "0x512P+4", "0x515p-2", ".p3", "2.p3",
|
349
|
+
"asdf", "23fab", "0xabgd", "28.eg", "4.3.3",
|
350
|
+
"naN", "InFiNiTy",
|
351
|
+
NULL,
|
352
|
+
};
|
353
|
+
|
354
|
+
double const EPS = 1e-8;
|
355
|
+
double f, f_std;
|
356
|
+
char const *endp;
|
357
|
+
char *endp_std;
|
358
|
+
for (int i = 0; tests[i] != NULL; i++) {
|
359
|
+
f = str_to_d(tests[i], &endp);
|
360
|
+
f_std = strtod(tests[i], &endp_std);
|
361
|
+
int num_equal = fabs(f - f_std) < EPS;
|
362
|
+
int rest_equal = endp == endp_std;
|
363
|
+
|
364
|
+
if (!num_equal || !rest_equal) {
|
365
|
+
printf("[%s] %f : %f (rest: %s : %s), (%d, %d)\n",
|
366
|
+
tests[i], f, f_std, endp, endp_std, num_equal, rest_equal);
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
return 0;
|
371
|
+
}
|
372
|
+
```
|