回答編集履歴
2
バグがあることを明記
test
CHANGED
@@ -218,6 +218,10 @@
|
|
218
218
|
|
219
219
|
|
220
220
|
|
221
|
+
さらなる追記: **バグがありました** 。例えば整数部・小数部とも省略されてしまった場合など。修正は容易ですが、今後修正していくことはできないため、鵜呑みにされることを避ける意味でも、バグがあるままにしておきます。これ以上の編集はしません。
|
222
|
+
|
223
|
+
|
224
|
+
|
221
225
|
```c
|
222
226
|
|
223
227
|
#include <stdio.h>
|
1
より近い実装を追記
test
CHANGED
@@ -203,3 +203,541 @@
|
|
203
203
|
このようなとき、どの部分でつまっているかを確かめるために printf デバッグが有効です。
|
204
204
|
|
205
205
|
私はまずソースの (==A==) の部分で n, i, o を表示させてみました。終了しない原因が integer() 関数にないことや、 integer() 関数の戻り値が正しいかどうかがここで確かめられます。次に入れるとしたら (==B==) の位置です。n と i を表示させながらループを観察します。そうすると `i == -1` のときにループから戻ってこないことが分かります。きっと pow() 関数の、負値のときにバグがあるのだろうと推測することができます。
|
206
|
+
|
207
|
+
|
208
|
+
|
209
|
+
## 追記
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
もっと `strtod()` の挙動に近い `str_to_d()` を書いてみました。。。
|
214
|
+
|
215
|
+
コードが正しい保証・バグなく動く保証はまったくないです。見苦しいコードですが参考に...ならないか。
|
216
|
+
|
217
|
+
とりあえず追記しておきます。
|
218
|
+
|
219
|
+
|
220
|
+
|
221
|
+
```c
|
222
|
+
|
223
|
+
#include <stdio.h>
|
224
|
+
|
225
|
+
#include <stdlib.h>
|
226
|
+
|
227
|
+
#include <math.h>
|
228
|
+
|
229
|
+
#include <stdbool.h>
|
230
|
+
|
231
|
+
|
232
|
+
|
233
|
+
#define WHITESPACE " \t\n\v\f\r"
|
234
|
+
|
235
|
+
|
236
|
+
|
237
|
+
// str の中から ch を探し、インデックスを返す。
|
238
|
+
|
239
|
+
// 見つからないときは -1
|
240
|
+
|
241
|
+
int find_ch(char const *str, char ch);
|
242
|
+
|
243
|
+
|
244
|
+
|
245
|
+
// ch が空白文字 WHITESPACE に含まれているかを返す
|
246
|
+
|
247
|
+
bool is_whitespace(char ch);
|
248
|
+
|
249
|
+
|
250
|
+
|
251
|
+
// 文字を十進整数にする
|
252
|
+
|
253
|
+
// 範囲外の場合は -1
|
254
|
+
|
255
|
+
int dec_to_digit(char ch);
|
256
|
+
|
257
|
+
|
258
|
+
|
259
|
+
// 文字を十六進整数にする
|
260
|
+
|
261
|
+
// 範囲外の場合は -1
|
262
|
+
|
263
|
+
int hex_to_digit(char ch);
|
264
|
+
|
265
|
+
|
266
|
+
|
267
|
+
// 符号を取得し、 nptr を進める
|
268
|
+
|
269
|
+
// 符号は、+なら1、-なら-1
|
270
|
+
|
271
|
+
int sign_and_eat(char const **nptr);
|
272
|
+
|
273
|
+
|
274
|
+
|
275
|
+
// ch が大文字だったら、小文字にする
|
276
|
+
|
277
|
+
char to_lower(char ch);
|
278
|
+
|
279
|
+
|
280
|
+
|
281
|
+
// s と t の先頭 n 文字が (大文字小文字の区別なしで) 等しいか返す。
|
282
|
+
|
283
|
+
bool is_same_n(char const *s, char const *t, size_t n);
|
284
|
+
|
285
|
+
|
286
|
+
|
287
|
+
// 基数 (0x) を取得し、 nptr を進める
|
288
|
+
|
289
|
+
int base_and_eat(char const **nptr);
|
290
|
+
|
291
|
+
|
292
|
+
|
293
|
+
// 整数部分 (小数点 or 指数表示の e, p まで) を取得し、
|
294
|
+
|
295
|
+
// nptr を進める。
|
296
|
+
|
297
|
+
int integer_part_and_eat(char const **nptr, int base);
|
298
|
+
|
299
|
+
|
300
|
+
|
301
|
+
// 小数部分 (小数点から指数表示の e, p か末尾まで) を取得し、
|
302
|
+
|
303
|
+
// nptr を進める。
|
304
|
+
|
305
|
+
double fraction_part_and_eat(char const **nptr, int base);
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
// base の n 乗を求める。
|
310
|
+
|
311
|
+
double pwr(double base, int n);
|
312
|
+
|
313
|
+
|
314
|
+
|
315
|
+
// 文字列を double に変換する。
|
316
|
+
|
317
|
+
double str_to_d(char const *nptr, char const **endptr);
|
318
|
+
|
319
|
+
|
320
|
+
|
321
|
+
int find_ch(char const *str, char ch) {
|
322
|
+
|
323
|
+
for (int i = 0; str[i]; i++) {
|
324
|
+
|
325
|
+
if (str[i] == ch) return i;
|
326
|
+
|
327
|
+
}
|
328
|
+
|
329
|
+
return -1;
|
330
|
+
|
331
|
+
}
|
332
|
+
|
333
|
+
|
334
|
+
|
335
|
+
bool is_whitespace(char ch) {
|
336
|
+
|
337
|
+
return find_ch(WHITESPACE, ch) != -1;
|
338
|
+
|
339
|
+
}
|
340
|
+
|
341
|
+
|
342
|
+
|
343
|
+
int dec_to_digit(char ch) {
|
344
|
+
|
345
|
+
if ('0' <= ch && ch <= '9') return ch - '0';
|
346
|
+
|
347
|
+
return -1;
|
348
|
+
|
349
|
+
}
|
350
|
+
|
351
|
+
|
352
|
+
|
353
|
+
int hex_to_digit(char ch) {
|
354
|
+
|
355
|
+
// まず10進の範囲での変換を試みる
|
356
|
+
|
357
|
+
int dg = dec_to_digit(ch);
|
358
|
+
|
359
|
+
if (dg != -1) return dg;
|
360
|
+
|
361
|
+
|
362
|
+
|
363
|
+
// ダメなら文字を変換する
|
364
|
+
|
365
|
+
if ('A' <= ch && ch <= 'F') return 10 + (ch - 'A');
|
366
|
+
|
367
|
+
if ('a' <= ch && ch <= 'f') return 10 + (ch - 'a');
|
368
|
+
|
369
|
+
return -1;
|
370
|
+
|
371
|
+
}
|
372
|
+
|
373
|
+
|
374
|
+
|
375
|
+
int sign_and_eat(char const **nptr) {
|
376
|
+
|
377
|
+
// 符号がなければ + となるため
|
378
|
+
|
379
|
+
int sign = 1;
|
380
|
+
|
381
|
+
if (**nptr == '+') {
|
382
|
+
|
383
|
+
(*nptr)++;
|
384
|
+
|
385
|
+
} else if (**nptr == '-') {
|
386
|
+
|
387
|
+
sign = -1;
|
388
|
+
|
389
|
+
(*nptr)++;
|
390
|
+
|
391
|
+
}
|
392
|
+
|
393
|
+
return sign;
|
394
|
+
|
395
|
+
}
|
396
|
+
|
397
|
+
|
398
|
+
|
399
|
+
char to_lower(char ch) {
|
400
|
+
|
401
|
+
if ('A' <= ch && ch <= 'Z') {
|
402
|
+
|
403
|
+
ch = ch - 'A' + 'a';
|
404
|
+
|
405
|
+
}
|
406
|
+
|
407
|
+
return ch;
|
408
|
+
|
409
|
+
}
|
410
|
+
|
411
|
+
|
412
|
+
|
413
|
+
bool is_same_n(char const *s, char const *t, size_t n) {
|
414
|
+
|
415
|
+
for (size_t i = 0; i < n; i++) {
|
416
|
+
|
417
|
+
if (!s[i] || !t[i]) return false;
|
418
|
+
|
419
|
+
if (to_lower(s[i]) != to_lower(t[i])) return false;
|
420
|
+
|
421
|
+
}
|
422
|
+
|
423
|
+
return true;
|
424
|
+
|
425
|
+
}
|
426
|
+
|
427
|
+
|
428
|
+
|
429
|
+
int base_and_eat(char const **nptr) {
|
430
|
+
|
431
|
+
if (is_same_n(*nptr, "0x", 2)) {
|
432
|
+
|
433
|
+
// 0x 始まり == 16進数
|
434
|
+
|
435
|
+
*nptr += 2;
|
436
|
+
|
437
|
+
return 16;
|
438
|
+
|
439
|
+
}
|
440
|
+
|
441
|
+
return 10;
|
442
|
+
|
443
|
+
}
|
444
|
+
|
445
|
+
|
446
|
+
|
447
|
+
int integer_part_and_eat(char const **nptr, int base, int (*to_digit)(char)) {
|
448
|
+
|
449
|
+
int ans = 0;
|
450
|
+
|
451
|
+
int dg;
|
452
|
+
|
453
|
+
// 数字である限りは変換
|
454
|
+
|
455
|
+
for ( ; **nptr && (dg = to_digit(**nptr)) != -1; (*nptr)++) {
|
456
|
+
|
457
|
+
ans *= base;
|
458
|
+
|
459
|
+
ans += dg;
|
460
|
+
|
461
|
+
}
|
462
|
+
|
463
|
+
return ans;
|
464
|
+
|
465
|
+
}
|
466
|
+
|
467
|
+
|
468
|
+
|
469
|
+
double fraction_part_and_eat(char const **nptr, int base, int (*to_digit)(char)) {
|
470
|
+
|
471
|
+
double ans = 0;
|
472
|
+
|
473
|
+
int dg;
|
474
|
+
|
475
|
+
double pwr = 1. / base;
|
476
|
+
|
477
|
+
// 数字である限りは変換
|
478
|
+
|
479
|
+
for ( ; **nptr && (dg = to_digit(**nptr)) != -1; (*nptr)++) {
|
480
|
+
|
481
|
+
ans += pwr * dg;
|
482
|
+
|
483
|
+
pwr /= base;
|
484
|
+
|
485
|
+
}
|
486
|
+
|
487
|
+
return ans;
|
488
|
+
|
489
|
+
}
|
490
|
+
|
491
|
+
|
492
|
+
|
493
|
+
double pwr(double base, int n) {
|
494
|
+
|
495
|
+
double ans = 1;
|
496
|
+
|
497
|
+
if (n > 0) {
|
498
|
+
|
499
|
+
for (int i = 0; i < n; i++)
|
500
|
+
|
501
|
+
ans *= base;
|
502
|
+
|
503
|
+
} else if (n < 0) {
|
504
|
+
|
505
|
+
for (int i = 0; i < -n; i++)
|
506
|
+
|
507
|
+
ans /= base;
|
508
|
+
|
509
|
+
}
|
510
|
+
|
511
|
+
return ans;
|
512
|
+
|
513
|
+
}
|
514
|
+
|
515
|
+
|
516
|
+
|
517
|
+
double str_to_d(char const *nptr, char const **endptr) {
|
518
|
+
|
519
|
+
#define FINISH_WHEN_ENDS() do { if (!*nptr) goto finish; } while (false)
|
520
|
+
|
521
|
+
// とりあえず NAN, INFINITY に対応する
|
522
|
+
|
523
|
+
if (is_same_n(nptr, "nan", 3)) {
|
524
|
+
|
525
|
+
*endptr = nptr + 3;
|
526
|
+
|
527
|
+
return NAN;
|
528
|
+
|
529
|
+
} else if (is_same_n(nptr, "infinity", 8)) {
|
530
|
+
|
531
|
+
*endptr = nptr + 8;
|
532
|
+
|
533
|
+
return INFINITY;
|
534
|
+
|
535
|
+
}
|
536
|
+
|
537
|
+
|
538
|
+
|
539
|
+
char const *oldnptr;
|
540
|
+
|
541
|
+
double ans = 0;
|
542
|
+
|
543
|
+
int sign = 1;
|
544
|
+
|
545
|
+
// whitespace の読み飛ばし
|
546
|
+
|
547
|
+
while (*nptr && is_whitespace(*nptr)) nptr++;
|
548
|
+
|
549
|
+
|
550
|
+
|
551
|
+
// 符号ビット
|
552
|
+
|
553
|
+
FINISH_WHEN_ENDS();
|
554
|
+
|
555
|
+
sign = sign_and_eat(&nptr);
|
556
|
+
|
557
|
+
|
558
|
+
|
559
|
+
// 基数
|
560
|
+
|
561
|
+
FINISH_WHEN_ENDS();
|
562
|
+
|
563
|
+
int base; base = base_and_eat(&nptr);
|
564
|
+
|
565
|
+
char const *expchars; expchars = base == 10 ? "eE" : "pP";
|
566
|
+
|
567
|
+
int (*to_digit)(char); to_digit = base == 10 ? dec_to_digit : hex_to_digit;
|
568
|
+
|
569
|
+
|
570
|
+
|
571
|
+
// 数字または小数点でないなら終了
|
572
|
+
|
573
|
+
FINISH_WHEN_ENDS();
|
574
|
+
|
575
|
+
if (to_digit(*nptr) == -1 && *nptr != '.') goto finish;
|
576
|
+
|
577
|
+
|
578
|
+
|
579
|
+
// 整数部分読み込み
|
580
|
+
|
581
|
+
oldnptr = nptr;
|
582
|
+
|
583
|
+
ans += integer_part_and_eat(&nptr, base, to_digit);
|
584
|
+
|
585
|
+
bool read_integer; read_integer = oldnptr != nptr;
|
586
|
+
|
587
|
+
|
588
|
+
|
589
|
+
// 小数点または expchars でなければ終了
|
590
|
+
|
591
|
+
FINISH_WHEN_ENDS();
|
592
|
+
|
593
|
+
bool read_dot; read_dot = false;
|
594
|
+
|
595
|
+
if (*nptr == '.') {
|
596
|
+
|
597
|
+
read_dot = true;
|
598
|
+
|
599
|
+
nptr++;
|
600
|
+
|
601
|
+
} else if (find_ch(expchars, *nptr) == -1) {
|
602
|
+
|
603
|
+
goto finish;
|
604
|
+
|
605
|
+
}
|
606
|
+
|
607
|
+
|
608
|
+
|
609
|
+
// 小数部分読み込み
|
610
|
+
|
611
|
+
oldnptr = nptr; // 変化したか確かめるため、とりあえず保存しておく。
|
612
|
+
|
613
|
+
ans += fraction_part_and_eat(&nptr, base, to_digit);
|
614
|
+
|
615
|
+
bool read_fraction; read_fraction = oldnptr != nptr;
|
616
|
+
|
617
|
+
|
618
|
+
|
619
|
+
// expchars でないか、 expchars であっても、
|
620
|
+
|
621
|
+
// 整数部省略小数であってかつ1つも進んでいない場合は終了
|
622
|
+
|
623
|
+
FINISH_WHEN_ENDS();
|
624
|
+
|
625
|
+
if (find_ch(expchars, *nptr) == -1
|
626
|
+
|
627
|
+
|| (!read_integer && read_dot && !read_fraction)) {
|
628
|
+
|
629
|
+
if (!read_integer && read_dot && !read_fraction) {
|
630
|
+
|
631
|
+
// なぜか標準はドットから返すので、 nptr を一つ戻しておく。
|
632
|
+
|
633
|
+
nptr--;
|
634
|
+
|
635
|
+
}
|
636
|
+
|
637
|
+
goto finish;
|
638
|
+
|
639
|
+
}
|
640
|
+
|
641
|
+
nptr++;
|
642
|
+
|
643
|
+
|
644
|
+
|
645
|
+
// 符号
|
646
|
+
|
647
|
+
FINISH_WHEN_ENDS();
|
648
|
+
|
649
|
+
int exp_sign; exp_sign = sign_and_eat(&nptr);
|
650
|
+
|
651
|
+
|
652
|
+
|
653
|
+
// 値
|
654
|
+
|
655
|
+
FINISH_WHEN_ENDS();
|
656
|
+
|
657
|
+
int exp_num; exp_num = integer_part_and_eat(&nptr, base, to_digit);
|
658
|
+
|
659
|
+
// 16進数の指数表示は底が2なので。
|
660
|
+
|
661
|
+
double mulr; mulr = pwr(base == 10 ? 10 : 2, exp_num * exp_sign);
|
662
|
+
|
663
|
+
ans *= mulr;
|
664
|
+
|
665
|
+
|
666
|
+
|
667
|
+
if (*nptr) {
|
668
|
+
|
669
|
+
// なぜか標準は e を食べないので、戻す。
|
670
|
+
|
671
|
+
nptr--;
|
672
|
+
|
673
|
+
}
|
674
|
+
|
675
|
+
|
676
|
+
|
677
|
+
finish:
|
678
|
+
|
679
|
+
if (endptr) *endptr = nptr;
|
680
|
+
|
681
|
+
return sign * ans;
|
682
|
+
|
683
|
+
}
|
684
|
+
|
685
|
+
|
686
|
+
|
687
|
+
int main(void)
|
688
|
+
|
689
|
+
{
|
690
|
+
|
691
|
+
char const *tests[] = {
|
692
|
+
|
693
|
+
"134", "-542", "4234.342", "-543.435", ".5453", "-.2343", "45e2", "453.34E+4", "545.3e-2", ".e2", "4.e2",
|
694
|
+
|
695
|
+
"0x42", "-0x53", "0x32.42", "-0x543.53", "0x.423", "-0x.432", "0x234p2", "0x512P+4", "0x515p-2", ".p3", "2.p3",
|
696
|
+
|
697
|
+
"asdf", "23fab", "0xabgd", "28.eg", "4.3.3",
|
698
|
+
|
699
|
+
"naN", "InFiNiTy",
|
700
|
+
|
701
|
+
NULL,
|
702
|
+
|
703
|
+
};
|
704
|
+
|
705
|
+
|
706
|
+
|
707
|
+
double const EPS = 1e-8;
|
708
|
+
|
709
|
+
double f, f_std;
|
710
|
+
|
711
|
+
char const *endp;
|
712
|
+
|
713
|
+
char *endp_std;
|
714
|
+
|
715
|
+
for (int i = 0; tests[i] != NULL; i++) {
|
716
|
+
|
717
|
+
f = str_to_d(tests[i], &endp);
|
718
|
+
|
719
|
+
f_std = strtod(tests[i], &endp_std);
|
720
|
+
|
721
|
+
int num_equal = fabs(f - f_std) < EPS;
|
722
|
+
|
723
|
+
int rest_equal = endp == endp_std;
|
724
|
+
|
725
|
+
|
726
|
+
|
727
|
+
if (!num_equal || !rest_equal) {
|
728
|
+
|
729
|
+
printf("[%s] %f : %f (rest: %s : %s), (%d, %d)\n",
|
730
|
+
|
731
|
+
tests[i], f, f_std, endp, endp_std, num_equal, rest_equal);
|
732
|
+
|
733
|
+
}
|
734
|
+
|
735
|
+
}
|
736
|
+
|
737
|
+
|
738
|
+
|
739
|
+
return 0;
|
740
|
+
|
741
|
+
}
|
742
|
+
|
743
|
+
```
|