github.com/tobgu/qframe@v0.4.0/internal/ryu/ryu.go (about)

     1  // Copyright 2018 Ulf Adams
     2  // Modifications copyright 2019 Caleb Spare
     3  //
     4  // The contents of this file may be used under the terms of the Apache License,
     5  // Version 2.0.
     6  //
     7  //    (See accompanying file LICENSE or copy at
     8  //     http://www.apache.org/licenses/LICENSE-2.0)
     9  //
    10  // Unless required by applicable law or agreed to in writing, this software
    11  // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    12  // KIND, either express or implied.
    13  //
    14  // The code in this file is part of a Go translation of the C code written by
    15  // Ulf Adams which may be found at https://github.com/ulfjack/ryu. That source
    16  // code is licensed under Apache 2.0 and this code is derivative work thereof.
    17  
    18  // Package ryu implements the Ryu algorithm for quickly converting floating
    19  // point numbers into strings.
    20  package ryu
    21  
    22  import (
    23  	"math"
    24  	"reflect"
    25  	"unsafe"
    26  )
    27  
    28  const (
    29  	mantBits32 = 23
    30  	expBits32  = 8
    31  	bias32     = 127
    32  
    33  	mantBits64 = 52
    34  	expBits64  = 11
    35  	bias64     = 1023
    36  )
    37  
    38  // FormatFloat32 converts a 32-bit floating point number f to a string.
    39  // It behaves like strconv.FormatFloat(float64(f), 'e', -1, 32).
    40  func FormatFloat32(f float32) string {
    41  	b := make([]byte, 0, 15)
    42  	b = AppendFloat32(b, f)
    43  
    44  	// Convert the output to a string without copying.
    45  	var s string
    46  	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
    47  	sh.Data = uintptr(unsafe.Pointer(&b[0]))
    48  	sh.Len = len(b)
    49  	return s
    50  }
    51  
    52  // AppendFloat32 appends the string form of the 32-bit floating point number f,
    53  // as generated by FormatFloat32, to b and returns the extended buffer.
    54  func AppendFloat32(b []byte, f float32) []byte {
    55  	// Step 1: Decode the floating-point number.
    56  	// Unify normalized and subnormal cases.
    57  	u := math.Float32bits(f)
    58  	neg := u>>(mantBits32+expBits32) != 0
    59  	mant := u & (uint32(1)<<mantBits32 - 1)
    60  	exp := (u >> mantBits32) & (uint32(1)<<expBits32 - 1)
    61  
    62  	// Exit early for easy cases.
    63  	if exp == uint32(1)<<expBits32-1 || (exp == 0 && mant == 0) {
    64  		return appendSpecial(b, neg, exp == 0, mant == 0)
    65  	}
    66  
    67  	d, ok := float32ToDecimalExactInt(mant, exp)
    68  	if !ok {
    69  		d = float32ToDecimal(mant, exp)
    70  	}
    71  	return d.append(b, neg)
    72  }
    73  
    74  // FormatFloat64 converts a 64-bit floating point number f to a string.
    75  // It behaves like strconv.FormatFloat(f, 'e', -1, 64).
    76  func FormatFloat64(f float64) string {
    77  	b := make([]byte, 0, 24)
    78  	b = AppendFloat64(b, f)
    79  	return byteSliceToString(b)
    80  }
    81  
    82  func byteSliceToString(b []byte) string {
    83  	// Zero alloc conversion following pattern found in stdlib strings.Builder.
    84  	return *(*string)(unsafe.Pointer(&b))
    85  }
    86  
    87  // AppendFloat64 appends the string form of the 64-bit floating point number f,
    88  // as generated by FormatFloat64, to b and returns the extended buffer.
    89  // It behaves like strconv.AppendFloat(b, f, 'e', -1, 64).
    90  func AppendFloat64(b []byte, f float64) []byte {
    91  	// Step 1: Decode the floating-point number.
    92  	// Unify normalized and subnormal cases.
    93  	u := math.Float64bits(f)
    94  	neg := u>>(mantBits64+expBits64) != 0
    95  	mant := u & (uint64(1)<<mantBits64 - 1)
    96  	exp := (u >> mantBits64) & (uint64(1)<<expBits64 - 1)
    97  
    98  	// Exit early for easy cases.
    99  	if exp == uint64(1)<<expBits64-1 || (exp == 0 && mant == 0) {
   100  		return appendSpecial(b, neg, exp == 0, mant == 0)
   101  	}
   102  
   103  	d, ok := float64ToDecimalExactInt(mant, exp)
   104  	if !ok {
   105  		d = float64ToDecimal(mant, exp)
   106  	}
   107  	return d.append(b, neg)
   108  }
   109  
   110  func appendSpecial(b []byte, neg, expZero, mantZero bool) []byte {
   111  	if !mantZero {
   112  		return append(b, "NaN"...)
   113  	}
   114  	if !expZero {
   115  		if neg {
   116  			return append(b, "-Inf"...)
   117  		} else {
   118  			return append(b, "+Inf"...)
   119  		}
   120  	}
   121  	if neg {
   122  		b = append(b, '-')
   123  	}
   124  	return append(b, "0e+00"...)
   125  }
   126  
   127  // FormatFloat64 converts a 64-bit floating point number f to a string.
   128  // It behaves like strconv.FormatFloat(f, 'f', -1, 64).
   129  func FormatFloat64f(f float64) string {
   130  	b := make([]byte, 0, 24)
   131  	b = AppendFloat64f(b, f)
   132  	return byteSliceToString(b)
   133  }
   134  
   135  // AppendFloat64 appends the string form of the 64-bit floating point number f,
   136  // as generated by FormatFloat64, to b and returns the extended buffer.
   137  // It behaves like strconv.AppendFloat(b, f, 'f', -1, 64).
   138  func AppendFloat64f(b []byte, f float64) []byte {
   139  	// Step 1: Decode the floating-point number.
   140  	// Unify normalized and subnormal cases.
   141  	u := math.Float64bits(f)
   142  	neg := u>>(mantBits64+expBits64) != 0
   143  	mant := u & (uint64(1)<<mantBits64 - 1)
   144  	exp := (u >> mantBits64) & (uint64(1)<<expBits64 - 1)
   145  
   146  	// Exit early for easy cases.
   147  	if exp == uint64(1)<<expBits64-1 || (exp == 0 && mant == 0) {
   148  		return appendSpecialf(b, neg, exp == 0, mant == 0)
   149  	}
   150  
   151  	d, ok := float64ToDecimalExactInt(mant, exp)
   152  	if !ok {
   153  		d = float64ToDecimal(mant, exp)
   154  	}
   155  	return d.appendF(b, neg)
   156  }
   157  
   158  func appendSpecialf(b []byte, neg, expZero, mantZero bool) []byte {
   159  	if !mantZero {
   160  		return append(b, "NaN"...)
   161  	}
   162  	if !expZero {
   163  		if neg {
   164  			return append(b, "-Inf"...)
   165  		} else {
   166  			return append(b, "+Inf"...)
   167  		}
   168  	}
   169  	if neg {
   170  		b = append(b, '-')
   171  	}
   172  	return append(b, '0')
   173  }
   174  
   175  func assert(t bool, msg string) {
   176  	if !t {
   177  		panic(msg)
   178  	}
   179  }
   180  
   181  // log10Pow2 returns floor(log_10(2^e)).
   182  func log10Pow2(e int32) uint32 {
   183  	// The first value this approximation fails for is 2^1651
   184  	// which is just greater than 10^297.
   185  	assert(e >= 0, "e >= 0")
   186  	assert(e <= 1650, "e <= 1650")
   187  	return (uint32(e) * 78913) >> 18
   188  }
   189  
   190  // log10Pow5 returns floor(log_10(5^e)).
   191  func log10Pow5(e int32) uint32 {
   192  	// The first value this approximation fails for is 5^2621
   193  	// which is just greater than 10^1832.
   194  	assert(e >= 0, "e >= 0")
   195  	assert(e <= 2620, "e <= 2620")
   196  	return (uint32(e) * 732923) >> 20
   197  }
   198  
   199  // pow5Bits returns ceil(log_2(5^e)), or else 1 if e==0.
   200  func pow5Bits(e int32) int32 {
   201  	// This approximation works up to the point that the multiplication
   202  	// overflows at e = 3529. If the multiplication were done in 64 bits,
   203  	// it would fail at 5^4004 which is just greater than 2^9297.
   204  	assert(e >= 0, "e >= 0")
   205  	assert(e <= 3528, "e <= 3528")
   206  	return int32((uint32(e)*1217359)>>19 + 1)
   207  }
   208  
   209  // These boolToXxx all inline as a movzx.
   210  
   211  func boolToInt(b bool) int {
   212  	if b {
   213  		return 1
   214  	}
   215  	return 0
   216  }
   217  
   218  func boolToUint32(b bool) uint32 {
   219  	if b {
   220  		return 1
   221  	}
   222  	return 0
   223  }
   224  
   225  func boolToUint64(b bool) uint64 {
   226  	if b {
   227  		return 1
   228  	}
   229  	return 0
   230  }