Commit bda6df5b authored by Dong-hee Na's avatar Dong-hee Na Committed by Dylan Trotter

Implemented float.__hash__(). (#261)

parent 004b7a84
......@@ -20,6 +20,8 @@ import (
"math/big"
"reflect"
"strconv"
"sync/atomic"
"unsafe"
)
// FloatType is the object representing the Python 'float' type.
......@@ -29,11 +31,12 @@ var FloatType = newBasisType("float", reflect.TypeOf(Float{}), toFloatUnsafe, Ob
type Float struct {
Object
value float64
hash int
}
// NewFloat returns a new Float holding the given floating point value.
func NewFloat(value float64) *Float {
return &Float{Object{typ: FloatType}, value}
return &Float{Object: Object{typ: FloatType}, value: value}
}
func toFloatUnsafe(o *Object) *Float {
......@@ -91,6 +94,22 @@ func floatGT(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatCompare(toFloatUnsafe(v), w, False, False, True), nil
}
func floatHash(f *Frame, o *Object) (*Object, *BaseException) {
v := toFloatUnsafe(o)
p := (*unsafe.Pointer)(unsafe.Pointer(&v.hash))
if lp := atomic.LoadPointer(p); lp != unsafe.Pointer(nil) {
return (*Int)(lp).ToObject(), nil
}
hash := hashFloat(v.Value())
if hash == -1 {
hash--
}
h := NewInt(hash)
atomic.StorePointer(p, unsafe.Pointer(h))
return h.ToObject(), nil
}
func floatInt(f *Frame, o *Object) (*Object, *BaseException) {
val := toFloatUnsafe(o).Value()
if math.IsInf(val, 0) {
......@@ -250,6 +269,7 @@ func initFloatType(dict map[string]*Object) {
FloatType.slots.Float = &unaryOpSlot{floatFloat}
FloatType.slots.GE = &binaryOpSlot{floatGE}
FloatType.slots.GT = &binaryOpSlot{floatGT}
FloatType.slots.Hash = &unaryOpSlot{floatHash}
FloatType.slots.Int = &unaryOpSlot{floatInt}
FloatType.slots.Long = &unaryOpSlot{floatLong}
FloatType.slots.LE = &binaryOpSlot{floatLE}
......@@ -355,6 +375,40 @@ func floatDivModOp(f *Frame, method string, v, w *Object, fun func(v, w float64)
return NewFloat(x).ToObject(), nil
}
func hashFloat(v float64) int {
if math.IsNaN(v) {
return 0
}
if math.IsInf(v, 0) {
if math.IsInf(v, 1) {
return 314159
}
if math.IsInf(v, -1) {
return -271828
}
return 0
}
_, fracPart := math.Modf(v)
if fracPart == 0.0 {
i := big.Int{}
big.NewFloat(v).Int(&i)
if numInIntRange(&i) {
return int(i.Int64())
}
// TODO: hashBigInt() is not yet matched that of cpython or pypy.
return hashBigInt(&i)
}
v, expo := math.Frexp(v)
v *= 2147483648.0
hiPart := int(v)
v = (v - float64(hiPart)) * 2147483648.0
x := int(hiPart + int(v) + (expo << 15))
return x
}
func floatModFunc(v, w float64) (float64, bool) {
if w == 0.0 {
return 0, false
......
......@@ -151,6 +151,22 @@ func TestFloatLong(t *testing.T) {
}
}
func TestFloatHash(t *testing.T) {
cases := []invokeTestCase{
{args: wrapArgs(NewFloat(0.0)), want: NewInt(0).ToObject()},
{args: wrapArgs(NewFloat(3.14)), want: NewInt(3146129223).ToObject()},
{args: wrapArgs(NewFloat(42.0)), want: NewInt(42).ToObject()},
{args: wrapArgs(NewFloat(42.125)), want: NewInt(1413677056).ToObject()},
{args: wrapArgs(NewFloat(math.Inf(1))), want: NewInt(314159).ToObject()},
{args: wrapArgs(NewFloat(math.Inf(-1))), want: NewInt(-271828).ToObject()},
{args: wrapArgs(NewFloat(math.NaN())), want: NewInt(0).ToObject()},
}
for _, cas := range cases {
if err := runInvokeTestCase(wrapFuncForTest(floatHash), &cas); err != "" {
t.Error(err)
}
}
}
func TestFloatIsTrue(t *testing.T) {
cases := []invokeTestCase{
{args: wrapArgs(0.0), want: False.ToObject()},
......@@ -167,6 +183,10 @@ func TestFloatIsTrue(t *testing.T) {
func TestFloatNew(t *testing.T) {
floatNew := mustNotRaise(GetAttr(NewRootFrame(), FloatType.ToObject(), NewStr("__new__"), nil))
strictEqType := newTestClassStrictEq("StrictEq", FloatType)
newStrictEq := func(v float64) *Object {
f := Float{Object: Object{typ: strictEqType}, value: v}
return f.ToObject()
}
subType := newTestClass("SubType", []*Type{FloatType}, newStringDict(map[string]*Object{}))
subTypeObject := (&Float{Object: Object{typ: subType}, value: 3.14}).ToObject()
goodSlotType := newTestClass("GoodSlot", []*Type{ObjectType}, newStringDict(map[string]*Object{
......@@ -203,8 +223,8 @@ func TestFloatNew(t *testing.T) {
{args: wrapArgs(FloatType, newObject(goodSlotType)), want: NewFloat(3.14).ToObject()},
{args: wrapArgs(FloatType, newObject(badSlotType)), wantExc: mustCreateException(TypeErrorType, "__float__ returned non-float (type object)")},
{args: wrapArgs(FloatType, newObject(slotSubTypeType)), want: subTypeObject},
{args: wrapArgs(strictEqType, 3.14), want: (&Float{Object{typ: strictEqType}, 3.14}).ToObject()},
{args: wrapArgs(strictEqType, newObject(goodSlotType)), want: (&Float{Object{typ: strictEqType}, 3.14}).ToObject()},
{args: wrapArgs(strictEqType, 3.14), want: newStrictEq(3.14)},
{args: wrapArgs(strictEqType, newObject(goodSlotType)), want: newStrictEq(3.14)},
{args: wrapArgs(strictEqType, newObject(badSlotType)), wantExc: mustCreateException(TypeErrorType, "__float__ returned non-float (type object)")},
{args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'__new__' requires 1 arguments")},
{args: wrapArgs(IntType), wantExc: mustCreateException(TypeErrorType, "float.__new__(int): int is not a subtype of float")},
......
......@@ -184,10 +184,10 @@ func TestSetIter(t *testing.T) {
cases := []invokeTestCase{
{args: wrapArgs(NewSet()), want: NewTuple().ToObject()},
{args: wrapArgs(newTestSet(1, 2, 3)), want: newTestTuple(1, 2, 3).ToObject()},
{args: wrapArgs(newTestSet("foo", 3.14)), want: newTestTuple(3.14, "foo").ToObject()},
{args: wrapArgs(newTestSet("foo", 3.14)), want: newTestTuple("foo", 3.14).ToObject()},
{args: wrapArgs(newTestFrozenSet()), want: NewTuple().ToObject()},
{args: wrapArgs(newTestFrozenSet(1, 2, 3)), want: newTestTuple(1, 2, 3).ToObject()},
{args: wrapArgs(newTestFrozenSet("foo", 3.14)), want: newTestTuple(3.14, "foo").ToObject()},
{args: wrapArgs(newTestFrozenSet("foo", 3.14)), want: newTestTuple("foo", 3.14).ToObject()},
}
for _, cas := range cases {
if err := runInvokeTestCase(fun, &cas); err != "" {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment