Skip to content

Commit 0e6bb4c

Browse files
committed
py: implement round() for int/long, float() and complex()
1 parent 2bc7a53 commit 0e6bb4c

File tree

5 files changed

+163
-33
lines changed

5 files changed

+163
-33
lines changed

py/bigint.go

+21-7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func (o *BigInt) Type() *Type {
2020
var (
2121
bigInt0 = (*BigInt)(big.NewInt(0))
2222
bigInt1 = (*BigInt)(big.NewInt(1))
23+
bigInt10 = (*BigInt)(big.NewInt(10))
2324
bigIntMin = (*BigInt)(big.NewInt(IntMin))
2425
bigIntMax = (*BigInt)(big.NewInt(IntMax))
2526
)
@@ -456,15 +457,28 @@ func (a *BigInt) M__complex__() (Object, error) {
456457

457458
func (a *BigInt) M__round__(digits Object) (Object, error) {
458459
if b, ok := convertToBigInt(digits); ok {
459-
bb, err := b.GoInt()
460-
if err != nil {
461-
return nil, err
462-
}
463-
if bb >= 0 {
460+
if (*big.Int)(b).Sign() >= 0 {
464461
return a, nil
465462
}
466-
// FIXME return a - (a % 10**(-bb))
467-
return nil, NotImplementedError
463+
negative := false
464+
r := new(big.Int).Set((*big.Int)(a))
465+
if r.Sign() < 0 {
466+
r.Neg(r)
467+
negative = true
468+
}
469+
negB := new(big.Int).Neg((*big.Int)(b))
470+
scale := new(big.Int).Exp((*big.Int)(bigInt10), negB, nil)
471+
digits := new(big.Int).Mod(r, scale)
472+
r.Sub(r, digits)
473+
// Round
474+
digits.Lsh(digits, 1)
475+
if digits.Cmp(scale) >= 0 {
476+
r.Add(r, scale)
477+
}
478+
if negative {
479+
r.Neg(r)
480+
}
481+
return (*BigInt)(r), nil
468482
}
469483
return cantConvert(digits, "int")
470484
}

py/complex.go

+20-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"math/cmplx"
88
)
99

10-
var ComplexType = NewType("complex64", "complex(real[, imag]) -> complex number\n\nCreate a complex number from a real part and an optional imaginary part.\nThis is equivalent to (real + imag*1j) where imag defaults to 0.")
10+
var ComplexType = ObjectType.NewType("complex64", "complex(real[, imag]) -> complex number\n\nCreate a complex number from a real part and an optional imaginary part.\nThis is equivalent to (real + imag*1j) where imag defaults to 0.", ComplexNew, nil)
1111

1212
type Complex complex128
1313

@@ -16,6 +16,25 @@ func (o Complex) Type() *Type {
1616
return ComplexType
1717
}
1818

19+
// ComplexNew
20+
func ComplexNew(metatype *Type, args Tuple, kwargs StringDict) (Object, error) {
21+
var realObj Object = Float(0)
22+
var imagObj Object = Float(0)
23+
err := ParseTupleAndKeywords(args, kwargs, "|OO", []string{"real", "imag"}, &realObj, &imagObj)
24+
if err != nil {
25+
return nil, err
26+
}
27+
real, err := MakeFloat(realObj)
28+
if err != nil {
29+
return nil, err
30+
}
31+
imag, err := MakeFloat(imagObj)
32+
if err != nil {
33+
return nil, err
34+
}
35+
return Complex(complex(real.(Float), imag.(Float))), nil
36+
}
37+
1938
// Convert an Object to an Complex
2039
//
2140
// Retrurns ok as to whether the conversion worked or not

py/float.go

+34-6
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@ package py
55
import (
66
"math"
77
"math/big"
8+
"strconv"
89
)
910

10-
var FloatType = NewType("float", "float(x) -> floating point number\n\nConvert a string or number to a floating point number, if possible.")
11+
var FloatType = ObjectType.NewType("float", "float(x) -> floating point number\n\nConvert a string or number to a floating point number, if possible.", FloatNew, nil)
12+
13+
// Bits of precision in a float64
14+
const (
15+
float64precision = 53
16+
float64MaxExponent = 1023
17+
)
1118

1219
type Float float64
1320

@@ -16,11 +23,32 @@ func (o Float) Type() *Type {
1623
return FloatType
1724
}
1825

19-
// Bits of precision in a float64
20-
const (
21-
float64precision = 53
22-
float64MaxExponent = 1023
23-
)
26+
// FloatNew
27+
func FloatNew(metatype *Type, args Tuple, kwargs StringDict) (Object, error) {
28+
var xObj Object = Float(0)
29+
err := ParseTupleAndKeywords(args, kwargs, "|O", []string{"x"}, &xObj)
30+
if err != nil {
31+
return nil, err
32+
}
33+
// Special case converting string types
34+
switch x := xObj.(type) {
35+
// FIXME Bytearray
36+
case Bytes:
37+
return FloatFromString(string(x))
38+
case String:
39+
return FloatFromString(string(x))
40+
}
41+
return MakeFloat(xObj)
42+
}
43+
44+
// FloatFromString turns a string into a Float
45+
func FloatFromString(str string) (Object, error) {
46+
f, err := strconv.ParseFloat(str, 64)
47+
if err != nil {
48+
return nil, ExceptionNewf(ValueError, "could not convert string to float: '%s'", str)
49+
}
50+
return Float(f), nil
51+
}
2452

2553
// Arithmetic
2654

py/int.go

+22-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package py
44

55
import (
6+
"math"
67
"math/big"
78
"strconv"
89
"strings"
@@ -599,8 +600,27 @@ func (a Int) M__round__(digits Object) (Object, error) {
599600
if b >= 0 {
600601
return a, nil
601602
}
602-
// FIXME return a - (a % 10**(-bb))
603-
return nil, NotImplementedError
603+
// Promote to BigInt if 10**-b > 2**63
604+
if b <= -19 {
605+
return (*BigInt)(big.NewInt(int64(a))).M__round__(digits)
606+
}
607+
negative := false
608+
r := a
609+
if r < 0 {
610+
r = -r
611+
negative = true
612+
}
613+
scale := Int(math.Pow(10, float64(-b)))
614+
digits := r % scale
615+
r -= digits
616+
// Round
617+
if 2*digits >= scale {
618+
r += scale
619+
}
620+
if negative {
621+
r = -r
622+
}
623+
return r, nil
604624
}
605625
return cantConvert(digits, "int")
606626
}

py/tests/int.py

+66-17
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,15 @@ def assertRaises(expecting, s, base=None):
155155
assert (~-6763809348657986856) == 6763809348657986855
156156
assert (~-874993321372974196107431462962) == 874993321372974196107431462961
157157

158-
#FIXME
159-
#doc='unop float'
160-
#assert (float(-6450284370390530229)) == -6.45028437039053e+18
161-
#assert (float(499622087565400960139933324516)) == 4.99622087565401e+29
162-
#assert (float(-2669664172783636500)) == -2.6696641727836365e+18
163-
#assert (float(586226139343958088885818537791)) == 5.862261393439581e+29
164-
165-
# FIXME
166-
# doc='unop complex'
167-
# assert (complex(8933717091386849423)) == (8.933717091386849e+18+0j)
168-
# assert (complex(-112003440633045743638450994095)) == (-1.1200344063304574e+29+0j)
169-
# assert (complex(-1231948596602428130)) == (-1.2319485966024282e+18+0j)
170-
# assert (complex(-870659418246097554269256403682)) == (-8.706594182460975e+29+0j)
158+
doc='unop float'
159+
assert (float(-6450284370390530229)) == -6.45028437039053e+18
160+
assert (float(499622087565400960139933324516)) == 4.99622087565401e+29
161+
assert (float(-2669664172783636500)) == -2.6696641727836365e+18
162+
assert (float(586226139343958088885818537791)) == 5.862261393439581e+29
163+
164+
doc='unop complex'
165+
assert (complex(8933717091386849423, -1231948596602428130)) == (8.933717091386849e+18+-1.2319485966024282e+18j)
166+
assert (complex(-112003440633045743638450994095, -870659418246097554269256403682)) == (-1.1200344063304574e+29+-8.706594182460975e+29j)
171167

172168
doc='unop int'
173169
assert (int(2996679482204208157)) == 2996679482204208157
@@ -548,10 +544,63 @@ def approxEqual(a, b):
548544
a **= 100000000000000000001
549545
assert a == -1
550546

551-
# FIXME
552-
# pow
553-
# divmod
554-
# round
547+
doc="pow(x,y)"
548+
assert pow(2,10) == 1024
549+
assert pow(-2,10) == 1024
550+
approxEqual(pow(2,-10), 0.0009765625)
551+
approxEqual(pow(-2,-10), 0.0009765625)
552+
assert pow(3,100) == 515377520732011331036461129765621272702107522001
553+
approxEqual(pow(515377520732011331036461129765621272702107522001,-1), 1.9403252174826328e-48)
554+
assert pow(-1,100000000000000000000) == 1
555+
assert pow(-1,100000000000000000001) == -1
556+
557+
doc="pow(x,y,z)"
558+
assert pow(1938019302983,283019283019238,91283091283012938) == 90917306668848727
559+
pow(193801930298311111111111111111111,28301928301923822222222222222222222,9128309128301293822222222222222222222) == 2810220059867374937460899006752893533
560+
assert pow(True, 10) == 1
561+
try:
562+
pow(1,-1,1)
563+
except TypeError:
564+
pass
565+
else:
566+
assert False, "TypeError not raised"
567+
568+
doc="round"
569+
assert round(12345678, 10) == 12345678
570+
assert round(12345678, 0) == 12345678
571+
assert round(12345678, -2) == 12345700
572+
assert round(12345678, -4) == 12350000
573+
assert round(12345678, -6) == 12000000
574+
assert round(12345678, -8) == 0
575+
assert round(9223372036854775807, -17) == 9200000000000000000
576+
assert round(9223372036854775807, -18) == 9000000000000000000
577+
assert round(9223372036854775807, -19) == 10000000000000000000
578+
579+
assert round(-12345678, 10) == -12345678
580+
assert round(-12345678, 0) == -12345678
581+
assert round(-12345678, -2) == -12345700
582+
assert round(-12345678, -4) == -12350000
583+
assert round(-12345678, -6) == -12000000
584+
assert round(-12345678, -8) == 0
585+
assert round(-9223372036854775808, -17) == -9200000000000000000
586+
assert round(-9223372036854775808, -18) == -9000000000000000000
587+
assert round(-9223372036854775808, -19) == -10000000000000000000
588+
589+
assert round(123456789012345678901, 10) == 123456789012345678901
590+
assert round(123456789012345678901, 0) == 123456789012345678901
591+
assert round(123456789012345678901, -2) == 123456789012345678900
592+
assert round(123456789012345678901, -4) == 123456789012345680000
593+
assert round(123456789012345678901, -6) == 123456789012346000000
594+
assert round(123456789012345678901,-19) == 120000000000000000000
595+
assert round(123456789012345678901,-21) == 0
596+
597+
assert round(-123456789012345678901, 10) == -123456789012345678901
598+
assert round(-123456789012345678901, 0) == -123456789012345678901
599+
assert round(-123456789012345678901, -2) == -123456789012345678900
600+
assert round(-123456789012345678901, -4) == -123456789012345680000
601+
assert round(-123456789012345678901, -6) == -123456789012346000000
602+
assert round(-123456789012345678901,-19) == -120000000000000000000
603+
assert round(-123456789012345678901,-21) == 0
555604

556605
doc="finished"
557606

0 commit comments

Comments
 (0)