Home | History | Annotate | Download | only in strconv
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package strconv_test
      6 
      7 import (
      8 	"bufio"
      9 	"fmt"
     10 	"os"
     11 	"strconv"
     12 	"strings"
     13 	"testing"
     14 )
     15 
     16 func pow2(i int) float64 {
     17 	switch {
     18 	case i < 0:
     19 		return 1 / pow2(-i)
     20 	case i == 0:
     21 		return 1
     22 	case i == 1:
     23 		return 2
     24 	}
     25 	return pow2(i/2) * pow2(i-i/2)
     26 }
     27 
     28 // Wrapper around strconv.ParseFloat(x, 64).  Handles dddddp+ddd (binary exponent)
     29 // itself, passes the rest on to strconv.ParseFloat.
     30 func myatof64(s string) (f float64, ok bool) {
     31 	a := strings.SplitN(s, "p", 2)
     32 	if len(a) == 2 {
     33 		n, err := strconv.ParseInt(a[0], 10, 64)
     34 		if err != nil {
     35 			return 0, false
     36 		}
     37 		e, err1 := strconv.Atoi(a[1])
     38 		if err1 != nil {
     39 			println("bad e", a[1])
     40 			return 0, false
     41 		}
     42 		v := float64(n)
     43 		// We expect that v*pow2(e) fits in a float64,
     44 		// but pow2(e) by itself may not. Be careful.
     45 		if e <= -1000 {
     46 			v *= pow2(-1000)
     47 			e += 1000
     48 			for e < 0 {
     49 				v /= 2
     50 				e++
     51 			}
     52 			return v, true
     53 		}
     54 		if e >= 1000 {
     55 			v *= pow2(1000)
     56 			e -= 1000
     57 			for e > 0 {
     58 				v *= 2
     59 				e--
     60 			}
     61 			return v, true
     62 		}
     63 		return v * pow2(e), true
     64 	}
     65 	f1, err := strconv.ParseFloat(s, 64)
     66 	if err != nil {
     67 		return 0, false
     68 	}
     69 	return f1, true
     70 }
     71 
     72 // Wrapper around strconv.ParseFloat(x, 32).  Handles dddddp+ddd (binary exponent)
     73 // itself, passes the rest on to strconv.ParseFloat.
     74 func myatof32(s string) (f float32, ok bool) {
     75 	a := strings.SplitN(s, "p", 2)
     76 	if len(a) == 2 {
     77 		n, err := strconv.Atoi(a[0])
     78 		if err != nil {
     79 			println("bad n", a[0])
     80 			return 0, false
     81 		}
     82 		e, err1 := strconv.Atoi(a[1])
     83 		if err1 != nil {
     84 			println("bad p", a[1])
     85 			return 0, false
     86 		}
     87 		return float32(float64(n) * pow2(e)), true
     88 	}
     89 	f64, err1 := strconv.ParseFloat(s, 32)
     90 	f1 := float32(f64)
     91 	if err1 != nil {
     92 		return 0, false
     93 	}
     94 	return f1, true
     95 }
     96 
     97 func TestFp(t *testing.T) {
     98 	f, err := os.Open("testdata/testfp.txt")
     99 	if err != nil {
    100 		t.Fatal("testfp: open testdata/testfp.txt:", err)
    101 	}
    102 	defer f.Close()
    103 
    104 	s := bufio.NewScanner(f)
    105 
    106 	for lineno := 1; s.Scan(); lineno++ {
    107 		line := s.Text()
    108 		if len(line) == 0 || line[0] == '#' {
    109 			continue
    110 		}
    111 		a := strings.Split(line, " ")
    112 		if len(a) != 4 {
    113 			t.Error("testdata/testfp.txt:", lineno, ": wrong field count")
    114 			continue
    115 		}
    116 		var s string
    117 		var v float64
    118 		switch a[0] {
    119 		case "float64":
    120 			var ok bool
    121 			v, ok = myatof64(a[2])
    122 			if !ok {
    123 				t.Error("testdata/testfp.txt:", lineno, ": cannot atof64 ", a[2])
    124 				continue
    125 			}
    126 			s = fmt.Sprintf(a[1], v)
    127 		case "float32":
    128 			v1, ok := myatof32(a[2])
    129 			if !ok {
    130 				t.Error("testdata/testfp.txt:", lineno, ": cannot atof32 ", a[2])
    131 				continue
    132 			}
    133 			s = fmt.Sprintf(a[1], v1)
    134 			v = float64(v1)
    135 		}
    136 		if s != a[3] {
    137 			t.Error("testdata/testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ",
    138 				"want ", a[3], " got ", s)
    139 		}
    140 	}
    141 	if s.Err() != nil {
    142 		t.Fatal("testfp: read testdata/testfp.txt: ", s.Err())
    143 	}
    144 }
    145