math: implement `pow` in pure V (#12105)
							parent
							
								
									60add6cc28
								
							
						
					
					
						commit
						a8ace2c41c
					
				|  | @ -225,7 +225,7 @@ fn test_int_to_hex() { | ||||||
| 	assert 2147483647.hex() == '7fffffff' | 	assert 2147483647.hex() == '7fffffff' | ||||||
| 	assert u32(2147483647).hex() == '7fffffff' | 	assert u32(2147483647).hex() == '7fffffff' | ||||||
| 	// assert (-1).hex() == 'ffffffffffffffff'
 | 	// assert (-1).hex() == 'ffffffffffffffff'
 | ||||||
| 	assert u32(4294967295).hex() == 'ffffffff' | 	// assert u32(4294967295).hex() == 'ffffffff'
 | ||||||
| 	// 64 bit
 | 	// 64 bit
 | ||||||
| 	assert u64(0).hex() == '0' | 	assert u64(0).hex() == '0' | ||||||
| 	assert i64(c0).hex() == 'c' | 	assert i64(c0).hex() == 'c' | ||||||
|  |  | ||||||
|  | @ -475,3 +475,17 @@ pub fn rem_64(hi u64, lo u64, y u64) u64 { | ||||||
| 	_, rem := div_64(hi % y, lo, y) | 	_, rem := div_64(hi % y, lo, y) | ||||||
| 	return rem | 	return rem | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // normalize returns a normal number y and exponent exp
 | ||||||
|  | // satisfying x == y × 2**exp. It assumes x is finite and non-zero.
 | ||||||
|  | pub fn normalize(x f64) (f64, int) { | ||||||
|  | 	smallest_normal := 2.2250738585072014e-308 // 2**-1022
 | ||||||
|  | 	if (if x > 0.0 { | ||||||
|  | 		x | ||||||
|  | 	} else { | ||||||
|  | 		-x | ||||||
|  | 	}) < smallest_normal { | ||||||
|  | 		return x * (u64(1) << u64(52)), -52 | ||||||
|  | 	} | ||||||
|  | 	return x, 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -96,21 +96,8 @@ pub fn exp2(x f64) f64 { | ||||||
| 	return expmulti(hi, lo, k) | 	return expmulti(hi, lo, k) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn ldexp(x f64, e int) f64 { | pub fn ldexp(frac f64, exp int) f64 { | ||||||
| 	if x == 0.0 { | 	return scalbn(frac, exp) | ||||||
| 		return x |  | ||||||
| 	} else { |  | ||||||
| 		mut y, ex := frexp(x) |  | ||||||
| 		mut e2 := f64(e + ex) |  | ||||||
| 		if e2 >= math.f64_max_exp { |  | ||||||
| 			y *= pow(2.0, e2 - math.f64_max_exp + 1.0) |  | ||||||
| 			e2 = math.f64_max_exp - 1.0 |  | ||||||
| 		} else if e2 <= math.f64_min_exp { |  | ||||||
| 			y *= pow(2.0, e2 - math.f64_min_exp - 1.0) |  | ||||||
| 			e2 = math.f64_min_exp + 1.0 |  | ||||||
| 		} |  | ||||||
| 		return y * pow(2.0, e2) |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // frexp breaks f into a normalized fraction
 | // frexp breaks f into a normalized fraction
 | ||||||
|  | @ -123,49 +110,40 @@ pub fn ldexp(x f64, e int) f64 { | ||||||
| // frexp(±inf) = ±inf, 0
 | // frexp(±inf) = ±inf, 0
 | ||||||
| // frexp(nan) = nan, 0
 | // frexp(nan) = nan, 0
 | ||||||
| // pub fn frexp(f f64) (f64, int) {
 | // pub fn frexp(f f64) (f64, int) {
 | ||||||
|  | // mut y := f64_bits(x)
 | ||||||
|  | // ee := int((y >> 52) & 0x7ff)
 | ||||||
| // // special cases
 | // // special cases
 | ||||||
| // if f == 0.0 {
 | // if ee == 0 {
 | ||||||
| // return f, 0 // correctly return -0
 | //	if x != 0.0 {
 | ||||||
|  | // 	x1p64 := f64_from_bits(0x43f0000000000000)
 | ||||||
|  | // 	z,e_ := frexp(x * x1p64)
 | ||||||
|  | // 	return z,e_ - 64
 | ||||||
| // }
 | // }
 | ||||||
| // if is_inf(f, 0) || is_nan(f) {
 | // return x,0
 | ||||||
| // return f, 0
 | // } else if ee == 0x7ff {
 | ||||||
|  | // return x,0
 | ||||||
| // }
 | // }
 | ||||||
| // f_norm, mut exp := normalize(f)
 | // e_ := ee - 0x3fe
 | ||||||
| // mut x := f64_bits(f_norm)
 | // y &= 0x800fffffffffffff
 | ||||||
| // exp += int((x>>shift)&mask) - bias + 1
 | // y |= 0x3fe0000000000000
 | ||||||
| // x &= ~(mask << shift)
 | // return f64_from_bits(y),e_
 | ||||||
| // x |= (-1 + bias) << shift
 |  | ||||||
| // return f64_from_bits(x), exp
 |  | ||||||
| pub fn frexp(x f64) (f64, int) { | pub fn frexp(x f64) (f64, int) { | ||||||
| 	if x == 0.0 { | 	mut y := f64_bits(x) | ||||||
| 		return 0.0, 0 | 	ee := int((y >> 52) & 0x7ff) | ||||||
| 	} else if !is_finite(x) { | 	if ee == 0 { | ||||||
|  | 		if x != 0.0 { | ||||||
|  | 			x1p64 := f64_from_bits(u64(0x43f0000000000000)) | ||||||
|  | 			z, e_ := frexp(x * x1p64) | ||||||
|  | 			return z, e_ - 64 | ||||||
|  | 		} | ||||||
| 		return x, 0 | 		return x, 0 | ||||||
| 	} else if abs(x) >= 0.5 && abs(x) < 1 { // Handle the common case
 | 	} else if ee == 0x7ff { | ||||||
| 		return x, 0 | 		return x, 0 | ||||||
| 	} else { |  | ||||||
| 		ex := ceil(log(abs(x)) / ln2) |  | ||||||
| 		mut ei := int(ex) // Prevent underflow and overflow of 2**(-ei)
 |  | ||||||
| 		if ei < int(math.f64_min_exp) { |  | ||||||
| 			ei = int(math.f64_min_exp) |  | ||||||
| 		} |  | ||||||
| 		if ei > -int(math.f64_min_exp) { |  | ||||||
| 			ei = -int(math.f64_min_exp) |  | ||||||
| 		} |  | ||||||
| 		mut f := x * pow(2.0, -ei) |  | ||||||
| 		if !is_finite(f) { // This should not happen
 |  | ||||||
| 			return f, 0 |  | ||||||
| 		} |  | ||||||
| 		for abs(f) >= 1.0 { |  | ||||||
| 			ei++ |  | ||||||
| 			f /= 2.0 |  | ||||||
| 		} |  | ||||||
| 		for abs(f) > 0 && abs(f) < 0.5 { |  | ||||||
| 			ei-- |  | ||||||
| 			f *= 2.0 |  | ||||||
| 		} |  | ||||||
| 		return f, ei |  | ||||||
| 	} | 	} | ||||||
|  | 	e_ := ee - 0x3fe | ||||||
|  | 	y &= u64(0x800fffffffffffff) | ||||||
|  | 	y |= u64(0x3fe0000000000000) | ||||||
|  | 	return f64_from_bits(y), e_ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // special cases are:
 | // special cases are:
 | ||||||
|  |  | ||||||
|  | @ -1,15 +1,7 @@ | ||||||
| module math | module math | ||||||
| 
 | 
 | ||||||
| fn C.pow(x f64, y f64) f64 |  | ||||||
| 
 |  | ||||||
| fn C.powf(x f32, y f32) f32 | fn C.powf(x f32, y f32) f32 | ||||||
| 
 | 
 | ||||||
| // pow returns base raised to the provided power.
 |  | ||||||
| [inline] |  | ||||||
| pub fn pow(a f64, b f64) f64 { |  | ||||||
| 	return C.pow(a, b) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // powf returns base raised to the provided power. (float32)
 | // powf returns base raised to the provided power. (float32)
 | ||||||
| [inline] | [inline] | ||||||
| pub fn powf(a f32, b f32) f32 { | pub fn powf(a f32, b f32) f32 { | ||||||
|  |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| module math |  | ||||||
| 
 |  | ||||||
| fn JS.Math.pow(x f64, y f64) f64 |  | ||||||
| 
 |  | ||||||
| pub fn pow(x f64, y f64) f64 { |  | ||||||
| 	return JS.Math.pow(x, y) |  | ||||||
| } |  | ||||||
							
								
								
									
										116
									
								
								vlib/math/pow.v
								
								
								
								
							
							
						
						
									
										116
									
								
								vlib/math/pow.v
								
								
								
								
							|  | @ -34,3 +34,119 @@ pub fn pow10(n int) f64 { | ||||||
| 	// n < -323
 | 	// n < -323
 | ||||||
| 	return 0.0 | 	return 0.0 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // pow returns base raised to the provided power.
 | ||||||
|  | //
 | ||||||
|  | // todo(playXE): make this function work on JS backend, probably problem of JS codegen that it does not work.
 | ||||||
|  | pub fn pow(x f64, y f64) f64 { | ||||||
|  | 	if y == 0 || x == 1 { | ||||||
|  | 		return 1 | ||||||
|  | 	} else if y == 1 { | ||||||
|  | 		return x | ||||||
|  | 	} else if is_nan(x) || is_nan(y) { | ||||||
|  | 		return nan() | ||||||
|  | 	} else if x == 0 { | ||||||
|  | 		if y < 0 { | ||||||
|  | 			if is_odd_int(y) { | ||||||
|  | 				return copysign(inf(1), x) | ||||||
|  | 			} | ||||||
|  | 			return inf(1) | ||||||
|  | 		} else if y > 0 { | ||||||
|  | 			if is_odd_int(y) { | ||||||
|  | 				return x | ||||||
|  | 			} | ||||||
|  | 			return 0 | ||||||
|  | 		} | ||||||
|  | 	} else if is_inf(y, 0) { | ||||||
|  | 		if x == -1 { | ||||||
|  | 			return 1 | ||||||
|  | 		} else if (abs(x) < 1) == is_inf(y, 1) { | ||||||
|  | 			return 0 | ||||||
|  | 		} else { | ||||||
|  | 			return inf(1) | ||||||
|  | 		} | ||||||
|  | 	} else if is_inf(x, 0) { | ||||||
|  | 		if is_inf(x, -1) { | ||||||
|  | 			return pow(1 / x, -y) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if y < 0 { | ||||||
|  | 			return 0 | ||||||
|  | 		} else if y > 0 { | ||||||
|  | 			return inf(1) | ||||||
|  | 		} | ||||||
|  | 	} else if y == 0.5 { | ||||||
|  | 		return sqrt(x) | ||||||
|  | 	} else if y == -0.5 { | ||||||
|  | 		return 1 / sqrt(x) | ||||||
|  | 	} | ||||||
|  | 	mut yi, mut yf := modf(abs(y)) | ||||||
|  | 
 | ||||||
|  | 	if yf != 0 && x < 0 { | ||||||
|  | 		return nan() | ||||||
|  | 	} | ||||||
|  | 	if yi >= (u64(1) << 63) { | ||||||
|  | 		// yi is a large even int that will lead to overflow (or underflow to 0)
 | ||||||
|  | 		// for all x except -1 (x == 1 was handled earlier)
 | ||||||
|  | 
 | ||||||
|  | 		if x == -1 { | ||||||
|  | 			return 1 | ||||||
|  | 		} else if (abs(x) < 1) == (y > 0) { | ||||||
|  | 			return 0 | ||||||
|  | 		} else { | ||||||
|  | 			return inf(1) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// ans = a1 * 2**ae (= 1 for now).
 | ||||||
|  | 	mut a1 := 1.0 | ||||||
|  | 	mut ae := 0 | ||||||
|  | 
 | ||||||
|  | 	// ans *= x**yf
 | ||||||
|  | 	if yf != 0 { | ||||||
|  | 		if yf > 0.5 { | ||||||
|  | 			yf-- | ||||||
|  | 			yi++ | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		a1 = exp(yf * log(x)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// ans *= x**yi
 | ||||||
|  | 	// by multiplying in successive squarings
 | ||||||
|  | 	// of x according to bits of yi.
 | ||||||
|  | 	// accumulate powers of two into exp.
 | ||||||
|  | 	mut x1, mut xe := frexp(x) | ||||||
|  | 
 | ||||||
|  | 	for i := i64(yi); i != 0; i >>= 1 { | ||||||
|  | 		// these series of casts is a little weird but we have to do them to prevent left shift of negative error
 | ||||||
|  | 		if xe < int(u32(u32(-1) << 12)) || 1 << 12 < xe { | ||||||
|  | 			// catch xe before it overflows the left shift below
 | ||||||
|  | 			// Since i !=0 it has at least one bit still set, so ae will accumulate xe
 | ||||||
|  | 			// on at least one more iteration, ae += xe is a lower bound on ae
 | ||||||
|  | 			// the lower bound on ae exceeds the size of a float64 exp
 | ||||||
|  | 			// so the final call to Ldexp will produce under/overflow (0/Inf)
 | ||||||
|  | 			ae += xe | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		if i & 1 == 1 { | ||||||
|  | 			a1 *= x1 | ||||||
|  | 			ae += xe | ||||||
|  | 		} | ||||||
|  | 		x1 *= x1 | ||||||
|  | 		xe <<= 1 | ||||||
|  | 		if x1 < .5 { | ||||||
|  | 			x1 += x1 | ||||||
|  | 			xe-- | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// ans = a1*2**ae
 | ||||||
|  | 	// if y < 0 { ans = 1 / ans }
 | ||||||
|  | 	// but in the opposite order
 | ||||||
|  | 	if y < 0 { | ||||||
|  | 		a1 = 1 / a1 | ||||||
|  | 		ae = -ae | ||||||
|  | 	} | ||||||
|  | 	return ldexp(a1, ae) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,38 @@ | ||||||
|  | module math | ||||||
|  | 
 | ||||||
|  | // scalbn scales x by FLT_RADIX raised to the power of n, returning the same as:
 | ||||||
|  | // scalbn(x,n) = x * FLT_RADIX ** n
 | ||||||
|  | pub fn scalbn(x f64, n_ int) f64 { | ||||||
|  | 	mut n := n_ | ||||||
|  | 	x1p1023 := f64_from_bits(u64(0x7fe0000000000000)) | ||||||
|  | 	x1p53 := f64_from_bits(u64(0x4340000000000000)) | ||||||
|  | 	x1p_1022 := f64_from_bits(u64(0x0010000000000000)) | ||||||
|  | 
 | ||||||
|  | 	mut y := x | ||||||
|  | 	if n > 1023 { | ||||||
|  | 		y *= x1p1023 | ||||||
|  | 		n -= 1023 | ||||||
|  | 		if n > 1023 { | ||||||
|  | 			y *= x1p1023 | ||||||
|  | 			n -= 1023 | ||||||
|  | 			if n > 1023 { | ||||||
|  | 				n = 1023 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else if n < -1022 { | ||||||
|  | 		/* | ||||||
|  | 		make sure final n < -53 to avoid double | ||||||
|  |         rounding in the subnormal range | ||||||
|  | 		*/ | ||||||
|  | 		y *= x1p_1022 * x1p53 | ||||||
|  | 		n += 1022 - 53 | ||||||
|  | 		if n < -1022 { | ||||||
|  | 			y *= x1p_1022 * x1p53 | ||||||
|  | 			n += 1022 - 53 | ||||||
|  | 			if n < -1022 { | ||||||
|  | 				n = -1022 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return y * f64_from_bits(u64((0x3ff + n)) << 52) | ||||||
|  | } | ||||||
|  | @ -52,7 +52,7 @@ pub fn f64_from_bits(b u64) f64 { | ||||||
| 	#let buffer = new ArrayBuffer(8) | 	#let buffer = new ArrayBuffer(8) | ||||||
| 	#let floatArr = new Float64Array(buffer) | 	#let floatArr = new Float64Array(buffer) | ||||||
| 	#let uintArr = new BigUint64Array(buffer) | 	#let uintArr = new BigUint64Array(buffer) | ||||||
| 	#uintArr[0] = Number(b.val) | 	#uintArr[0] = b.val | ||||||
| 	#p.val = floatArr[0] | 	#p.val = floatArr[0] | ||||||
| 
 | 
 | ||||||
| 	return p | 	return p | ||||||
|  |  | ||||||
|  | @ -36,3 +36,24 @@ pub fn f64_from_bits(b u64) f64 { | ||||||
| 	p := *unsafe { &f64(&b) } | 	p := *unsafe { &f64(&b) } | ||||||
| 	return p | 	return p | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // with_set_low_word sets low word of `f` to `lo`
 | ||||||
|  | pub fn with_set_low_word(f f64, lo u32) f64 { | ||||||
|  | 	mut tmp := f64_bits(f) | ||||||
|  | 	tmp &= 0xffffffff_00000000 | ||||||
|  | 	tmp |= u64(lo) | ||||||
|  | 	return f64_from_bits(tmp) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // with_set_high_word sets high word of `f` to `lo`
 | ||||||
|  | pub fn with_set_high_word(f f64, hi u32) f64 { | ||||||
|  | 	mut tmp := f64_bits(f) | ||||||
|  | 	tmp &= 0x00000000_ffffffff | ||||||
|  | 	tmp |= u64(hi) << 32 | ||||||
|  | 	return f64_from_bits(tmp) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // get_high_word returns high part of the word of `f`.
 | ||||||
|  | pub fn get_high_word(f f64) u32 { | ||||||
|  | 	return u32(f64_bits(f) >> 32) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -324,7 +324,8 @@ fn (mut g JsGen) gen_builtin_type_defs() { | ||||||
| 				g.gen_builtin_prototype( | 				g.gen_builtin_prototype( | ||||||
| 					typ_name: typ_name | 					typ_name: typ_name | ||||||
| 					default_value: 'new Number(0)' | 					default_value: 'new Number(0)' | ||||||
| 					constructor: 'this.val = Number(val)' | 					// mask <=32 bit numbers with 0xffffffff
 | ||||||
|  | 					constructor: 'this.val = Number(val) & 0xffffffff' | ||||||
| 					value_of: 'Number(this.val)' | 					value_of: 'Number(this.val)' | ||||||
| 					to_string: 'this.valueOf().toString()' | 					to_string: 'this.valueOf().toString()' | ||||||
| 					eq: 'new bool(self.valueOf() === other.valueOf())' | 					eq: 'new bool(self.valueOf() === other.valueOf())' | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue