github.com/tobgu/qframe@v0.4.0/internal/ryu/ryu32.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
    19  
    20  import (
    21  	"math"
    22  	"math/bits"
    23  )
    24  
    25  // dec32 is a floating decimal type representing m * 10^e.
    26  type dec32 struct {
    27  	m uint32
    28  	e int32
    29  }
    30  
    31  func (d dec32) append(b []byte, neg bool) []byte {
    32  	// Step 5: Print the decimal representation.
    33  	if neg {
    34  		b = append(b, '-')
    35  	}
    36  
    37  	out := d.m
    38  	outLen := decimalLen32(out)
    39  	bufLen := outLen
    40  	if bufLen > 1 {
    41  		bufLen++ // extra space for '.'
    42  	}
    43  
    44  	// Print the decimal digits.
    45  	n := len(b)
    46  	b = append(b, make([]byte, bufLen)...)
    47  	for i := 0; i < outLen-1; i++ {
    48  		b[n+outLen-i] = '0' + byte(out%10)
    49  		out /= 10
    50  	}
    51  	b[n] = '0' + byte(out%10)
    52  
    53  	// Print the '.' if needed.
    54  	if outLen > 1 {
    55  		b[n+1] = '.'
    56  	}
    57  
    58  	// Print the exponent.
    59  	b = append(b, 'e')
    60  	exp := d.e + int32(outLen) - 1
    61  	if exp < 0 {
    62  		b = append(b, '-')
    63  		exp = -exp
    64  	} else {
    65  		// Unconditionally print a + here to match strconv's formatting.
    66  		b = append(b, '+')
    67  	}
    68  	// Always print two digits to match strconv's formatting.
    69  	d1 := exp % 10
    70  	d0 := exp / 10
    71  	b = append(b, '0'+byte(d0), '0'+byte(d1))
    72  
    73  	return b
    74  }
    75  
    76  func float32ToDecimalExactInt(mant, exp uint32) (d dec32, ok bool) {
    77  	e := exp - bias32
    78  	if e > mantBits32 {
    79  		return d, false
    80  	}
    81  	shift := mantBits32 - e
    82  	mant |= 1 << mantBits32 // implicit 1
    83  	d.m = mant >> shift
    84  	if d.m<<shift != mant {
    85  		return d, false
    86  	}
    87  	for d.m%10 == 0 {
    88  		d.m /= 10
    89  		d.e++
    90  	}
    91  	return d, true
    92  }
    93  
    94  func float32ToDecimal(mant, exp uint32) dec32 {
    95  	var e2 int32
    96  	var m2 uint32
    97  	if exp == 0 {
    98  		// We subtract 2 so that the bounds computation has
    99  		// 2 additional bits.
   100  		e2 = 1 - bias32 - mantBits32 - 2
   101  		m2 = mant
   102  	} else {
   103  		e2 = int32(exp) - bias32 - mantBits32 - 2
   104  		m2 = uint32(1)<<mantBits32 | mant
   105  	}
   106  	even := m2&1 == 0
   107  	acceptBounds := even
   108  
   109  	// Step 2: Determine the interval of valid decimal representations.
   110  	var (
   111  		mv      = 4 * m2
   112  		mp      = 4*m2 + 2
   113  		mmShift = boolToUint32(mant != 0 || exp <= 1)
   114  		mm      = 4*m2 - 1 - mmShift
   115  	)
   116  
   117  	// Step 3: Convert to a decimal power base using 64-bit arithmetic.
   118  	var (
   119  		vr, vp, vm        uint32
   120  		e10               int32
   121  		vmIsTrailingZeros bool
   122  		vrIsTrailingZeros bool
   123  		lastRemovedDigit  uint8
   124  	)
   125  	if e2 >= 0 {
   126  		q := log10Pow2(e2)
   127  		e10 = int32(q)
   128  		k := pow5InvNumBits32 + pow5Bits(int32(q)) - 1
   129  		i := -e2 + int32(q) + k
   130  		vr = mulPow5InvDivPow2(mv, q, i)
   131  		vp = mulPow5InvDivPow2(mp, q, i)
   132  		vm = mulPow5InvDivPow2(mm, q, i)
   133  		if q != 0 && (vp-1)/10 <= vm/10 {
   134  			// We need to know one removed digit even if we are not
   135  			// going to loop below. We could use q = X - 1 above,
   136  			// except that would require 33 bits for the result, and
   137  			// we've found that 32-bit arithmetic is faster even on
   138  			// 64-bit machines.
   139  			l := pow5InvNumBits32 + pow5Bits(int32(q-1)) - 1
   140  			lastRemovedDigit = uint8(mulPow5InvDivPow2(mv, q-1, -e2+int32(q-1)+l) % 10)
   141  		}
   142  		if q <= 9 {
   143  			// The largest power of 5 that fits in 24 bits is 5^10,
   144  			// but q <= 9 seems to be safe as well. Only one of mp,
   145  			// mv, and mm can be a multiple of 5, if any.
   146  			if mv%5 == 0 {
   147  				vrIsTrailingZeros = multipleOfPowerOfFive32(mv, q)
   148  			} else if acceptBounds {
   149  				vmIsTrailingZeros = multipleOfPowerOfFive32(mm, q)
   150  			} else if multipleOfPowerOfFive32(mp, q) {
   151  				vp--
   152  			}
   153  		}
   154  	} else {
   155  		q := log10Pow5(-e2)
   156  		e10 = int32(q) + e2
   157  		i := -e2 - int32(q)
   158  		k := pow5Bits(i) - pow5NumBits32
   159  		j := int32(q) - k
   160  		vr = mulPow5DivPow2(mv, uint32(i), j)
   161  		vp = mulPow5DivPow2(mp, uint32(i), j)
   162  		vm = mulPow5DivPow2(mm, uint32(i), j)
   163  		if q != 0 && (vp-1)/10 <= vm/10 {
   164  			j = int32(q) - 1 - (pow5Bits(i+1) - pow5NumBits32)
   165  			lastRemovedDigit = uint8(mulPow5DivPow2(mv, uint32(i+1), j) % 10)
   166  		}
   167  		if q <= 1 {
   168  			// {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at
   169  			// least q trailing 0 bits. mv = 4 * m2, so it always
   170  			// has at least two trailing 0 bits.
   171  			vrIsTrailingZeros = true
   172  			if acceptBounds {
   173  				// mm = mv - 1 - mmShift, so it has 1 trailing 0 bit
   174  				// iff mmShift == 1.
   175  				vmIsTrailingZeros = mmShift == 1
   176  			} else {
   177  				// mp = mv + 2, so it always has at least one
   178  				// trailing 0 bit.
   179  				vp--
   180  			}
   181  		} else if q < 31 {
   182  			vrIsTrailingZeros = multipleOfPowerOfTwo32(mv, q-1)
   183  		}
   184  	}
   185  
   186  	// Step 4: Find the shortest decimal representation
   187  	// in the interval of valid representations.
   188  	var removed int32
   189  	var out uint32
   190  	if vmIsTrailingZeros || vrIsTrailingZeros {
   191  		// General case, which happens rarely (~4.0%).
   192  		for vp/10 > vm/10 {
   193  			vmIsTrailingZeros = vmIsTrailingZeros && vm%10 == 0
   194  			vrIsTrailingZeros = vrIsTrailingZeros && lastRemovedDigit == 0
   195  			lastRemovedDigit = uint8(vr % 10)
   196  			vr /= 10
   197  			vp /= 10
   198  			vm /= 10
   199  			removed++
   200  		}
   201  		if vmIsTrailingZeros {
   202  			for vm%10 == 0 {
   203  				vrIsTrailingZeros = vrIsTrailingZeros && lastRemovedDigit == 0
   204  				lastRemovedDigit = uint8(vr % 10)
   205  				vr /= 10
   206  				vp /= 10
   207  				vm /= 10
   208  				removed++
   209  			}
   210  		}
   211  		if vrIsTrailingZeros && lastRemovedDigit == 5 && vr%2 == 0 {
   212  			// Round even if the exact number is .....50..0.
   213  			lastRemovedDigit = 4
   214  		}
   215  		out = vr
   216  		// We need to take vr + 1 if vr is outside bounds
   217  		// or we need to round up.
   218  		if (vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || lastRemovedDigit >= 5 {
   219  			out++
   220  		}
   221  	} else {
   222  		// Specialized for the common case (~96.0%). Percentages below
   223  		// are relative to this. Loop iterations below (approximately):
   224  		// 0: 13.6%, 1: 70.7%, 2: 14.1%, 3: 1.39%, 4: 0.14%, 5+: 0.01%
   225  		for vp/10 > vm/10 {
   226  			lastRemovedDigit = uint8(vr % 10)
   227  			vr /= 10
   228  			vp /= 10
   229  			vm /= 10
   230  			removed++
   231  		}
   232  		// We need to take vr + 1 if vr is outside bounds
   233  		// or we need to round up.
   234  		out = vr + boolToUint32(vr == vm || lastRemovedDigit >= 5)
   235  	}
   236  
   237  	return dec32{m: out, e: e10 + removed}
   238  }
   239  
   240  func decimalLen32(u uint32) int {
   241  	// Function precondition: u is not a 10-digit number.
   242  	// (9 digits are sufficient for round-tripping.)
   243  	// This benchmarked faster than the log2 approach used for uint64s.
   244  	assert(u < 1000000000, "too big")
   245  	switch {
   246  	case u >= 100000000:
   247  		return 9
   248  	case u >= 10000000:
   249  		return 8
   250  	case u >= 1000000:
   251  		return 7
   252  	case u >= 100000:
   253  		return 6
   254  	case u >= 10000:
   255  		return 5
   256  	case u >= 1000:
   257  		return 4
   258  	case u >= 100:
   259  		return 3
   260  	case u >= 10:
   261  		return 2
   262  	default:
   263  		return 1
   264  	}
   265  }
   266  
   267  func mulShift32(m uint32, mul uint64, shift int32) uint32 {
   268  	assert(shift > 32, "shift > 32")
   269  
   270  	hi, lo := bits.Mul64(uint64(m), mul)
   271  	shiftedSum := (lo >> uint(shift)) + (hi << uint(64-shift))
   272  	assert(shiftedSum <= math.MaxUint32, "shiftedSum <= math.MaxUint32")
   273  	return uint32(shiftedSum)
   274  }
   275  
   276  func mulPow5InvDivPow2(m, q uint32, j int32) uint32 {
   277  	return mulShift32(m, pow5InvSplit32[q], j)
   278  }
   279  
   280  func mulPow5DivPow2(m, i uint32, j int32) uint32 {
   281  	return mulShift32(m, pow5Split32[i], j)
   282  }
   283  
   284  func pow5Factor32(v uint32) uint32 {
   285  	for n := uint32(0); ; n++ {
   286  		q, r := v/5, v%5
   287  		if r != 0 {
   288  			return n
   289  		}
   290  		v = q
   291  	}
   292  }
   293  
   294  // multipleOfPowerOfFive32 reports whether v is divisible by 5^p.
   295  func multipleOfPowerOfFive32(v, p uint32) bool {
   296  	return pow5Factor32(v) >= p
   297  }
   298  
   299  // multipleOfPowerOfTwo32 reports whether v is divisible by 2^p.
   300  func multipleOfPowerOfTwo32(v, p uint32) bool {
   301  	return uint32(bits.TrailingZeros32(v)) >= p
   302  }