Skip to content
This repository was archived by the owner on Mar 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/expr_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ def visit_Yield(self, node):
ast.LShift: 'πg.LShift(πF, {lhs}, {rhs})',
ast.Mod: 'πg.Mod(πF, {lhs}, {rhs})',
ast.Mult: 'πg.Mul(πF, {lhs}, {rhs})',
ast.Pow: 'πg.Pow(πF, {lhs}, {rhs})',
ast.RShift: 'πg.RShift(πF, {lhs}, {rhs})',
ast.Sub: 'πg.Sub(πF, {lhs}, {rhs})',
}
Expand Down
5 changes: 1 addition & 4 deletions compiler/expr_visitor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,10 @@ class Foo(object):
testBinOpArithmeticMod = _MakeExprTest('9 % 5')
testBinOpArithmeticMul = _MakeExprTest('3 * 2')
testBinOpArithmeticOr = _MakeExprTest('2 | 6')
testBinOpArithmeticPow = _MakeExprTest('2 ** 16')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple more test cases would be good here, e.g. negative numbers, floats, etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the parser part. Does this really need more test?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this is consistent with what's there. It's fine to leave it the way it is.

testBinOpArithmeticSub = _MakeExprTest('10 - 3')
testBinOpArithmeticXor = _MakeExprTest('3 ^ 5')

def testBinOpNotImplemented(self):
self.assertRaisesRegexp(util.ParseError, 'binary op not implemented',
_ParseAndVisitExpr, 'x ** y')

testBoolOpTrueAndFalse = _MakeExprTest('True and False')
testBoolOpTrueAndTrue = _MakeExprTest('True and True')
testBoolOpTrueAndExpr = _MakeExprTest('True and 2 == 2')
Expand Down
4 changes: 4 additions & 0 deletions runtime/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@ func Mul(f *Frame, v, w *Object) (*Object, *BaseException) {
return binaryOp(f, v, w, v.typ.slots.Mul, v.typ.slots.RMul, w.typ.slots.RMul, "*")
}

func Pow(f *Frame, v, w *Object) (*Object, *BaseException) {
return binaryOp(f, v, w, v.typ.slots.Pow, v.typ.slots.RPow, w.typ.slots.RPow, "**")
}

// Or returns the result of the bitwise or operator v | w according to
// __or/ror__.
func Or(f *Frame, v, w *Object) (*Object, *BaseException) {
Expand Down
2 changes: 2 additions & 0 deletions runtime/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ func TestBinaryOps(t *testing.T) {
{Mul, newObject(ObjectType), newObject(fooType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for *: 'object' and 'Foo'")},
{Or, NewInt(-42).ToObject(), NewInt(244).ToObject(), NewInt(-10).ToObject(), nil},
{Or, NewInt(42).ToObject(), NewStr("foo").ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for |: 'int' and 'str'")},
{Pow, NewInt(2).ToObject(), NewInt(-2).ToObject(), NewFloat(0.25).ToObject(), nil},
{Pow, NewInt(2).ToObject(), newObject(fooType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'int' and 'Foo'")},
{Sub, NewInt(3).ToObject(), NewInt(-3).ToObject(), NewInt(6).ToObject(), nil},
{Xor, NewInt(-42).ToObject(), NewInt(244).ToObject(), NewInt(-222).ToObject(), nil},
{Xor, NewInt(42).ToObject(), NewStr("foo").ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for ^: 'int' and 'str'")},
Expand Down
10 changes: 10 additions & 0 deletions runtime/float.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ func floatNonZero(f *Frame, o *Object) (*Object, *BaseException) {
return GetBool(toFloatUnsafe(o).Value() != 0).ToObject(), nil
}

func floatPow(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__pow__", v, w, func(v, w float64) float64 { return math.Pow(v, w) })
Copy link
Copy Markdown
Contributor

@ns-cweber ns-cweber Jan 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

math.Pow() already takes 2 floats and returns another, so I think this could be reduced:

func floatPow(f *Frame, v, w *Object) (*Object, *BaseException) {
    return floatArithmeticOp(f, "__pow__", v, w, math.Pow)
}

Same goes for RPow below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RPow below can not changed this way, because the order of the arguments is reversed.

}

func floatRAdd(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__radd__", v, w, func(v, w float64) float64 { return w + v })
}
Expand Down Expand Up @@ -211,6 +215,10 @@ func floatRMul(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__rmul__", v, w, func(v, w float64) float64 { return w * v })
}

func floatRPow(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__rpow", v, w, func(v, w float64) float64 { return math.Pow(w, v) })
}

func floatRSub(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__rsub__", v, w, func(v, w float64) float64 { return w - v })
}
Expand All @@ -237,11 +245,13 @@ func initFloatType(dict map[string]*Object) {
FloatType.slots.NE = &binaryOpSlot{floatNE}
FloatType.slots.New = &newSlot{floatNew}
FloatType.slots.NonZero = &unaryOpSlot{floatNonZero}
FloatType.slots.Pow = &binaryOpSlot{floatPow}
FloatType.slots.RAdd = &binaryOpSlot{floatRAdd}
FloatType.slots.RDiv = &binaryOpSlot{floatRDiv}
FloatType.slots.Repr = &unaryOpSlot{floatRepr}
FloatType.slots.RMod = &binaryOpSlot{floatRMod}
FloatType.slots.RMul = &binaryOpSlot{floatRMul}
FloatType.slots.RPow = &binaryOpSlot{floatRPow}
FloatType.slots.RSub = &binaryOpSlot{floatRSub}
FloatType.slots.Sub = &binaryOpSlot{floatSub}
}
Expand Down
4 changes: 4 additions & 0 deletions runtime/float_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func TestFloatArithmeticOps(t *testing.T) {
{Mul, NewFloat(math.Inf(1)).ToObject(), NewInt(-5).ToObject(), NewFloat(math.Inf(-1)).ToObject(), nil},
{Mul, False.ToObject(), NewFloat(math.Inf(1)).ToObject(), NewFloat(math.NaN()).ToObject(), nil},
{Mul, None, NewFloat(1.5).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for *: 'NoneType' and 'float'")},
{Pow, NewFloat(2.0).ToObject(), NewInt(10).ToObject(), NewFloat(1024.0).ToObject(), nil},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "unsupported operand type" case would be good here.

{Pow, NewFloat(2.0).ToObject(), NewFloat(-2.0).ToObject(), NewFloat(0.25).ToObject(), nil},
{Pow, newObject(ObjectType), NewFloat(2.0).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'object' and 'float'")},
{Pow, NewFloat(2.0).ToObject(), newObject(ObjectType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'float' and 'object'")},
{Sub, NewFloat(21.3).ToObject(), NewFloat(35.6).ToObject(), NewFloat(-14.3).ToObject(), nil},
{Sub, True.ToObject(), NewFloat(1.5).ToObject(), NewFloat(-0.5).ToObject(), nil},
{Sub, NewFloat(1.0).ToObject(), NewList().ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for -: 'float' and 'list'")},
Expand Down
39 changes: 39 additions & 0 deletions runtime/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package grumpy

import (
"fmt"
"math"
"math/big"
"reflect"
"strconv"
Expand Down Expand Up @@ -226,6 +227,43 @@ func intOr(f *Frame, v, w *Object) (*Object, *BaseException) {
return NewInt(toIntUnsafe(v).Value() | toIntUnsafe(w).Value()).ToObject(), nil
}

func intPow(f *Frame, v, w *Object) (*Object, *BaseException) {
if w.isInstance(IntType) {
// First try to use the faster floating point arithmetic
// on the CPU, then falls back to slower methods.
// IEEE float64 has 52bit of precision, so the result should be
// less than MaxInt32 to be representable as an exact integer.
// This assumes that int is at least 32bit.
vInt := toIntUnsafe(v).Value()
wInt := toIntUnsafe(w).Value()
if 0 < vInt && vInt <= math.MaxInt32 && 0 < wInt && wInt <= math.MaxInt32 {
res := math.Pow(float64(vInt), float64(wInt))
// Can the result be interpreted as an int?
if !math.IsNaN(res) && !math.IsInf(res, 0) && res <= math.MaxInt32 {
return NewInt(int(res)).ToObject(), nil
}
}
// Special cases.
if vInt == 0 {
if wInt < 0 {
return nil, f.RaiseType(ZeroDivisionErrorType, "0.0 cannot be raised to a negative power")
}
if wInt == 0 {
return NewInt(1).ToObject(), nil
}
return NewInt(0).ToObject(), nil
}
// If w < 0, the result must be a floating point number.
// We convert both arguments to float and continue.
if wInt < 0 {
return floatPow(f, NewFloat(float64(vInt)).ToObject(), NewFloat(float64(wInt)).ToObject())
}
// Else we convert to Long and continue there.
return longPow(f, NewLong(big.NewInt(int64(vInt))).ToObject(), NewLong(big.NewInt(int64(wInt))).ToObject())
}
return NotImplemented, nil
}

func intRAdd(f *Frame, v, w *Object) (*Object, *BaseException) {
return intAddMulOp(f, "__radd__", v, w, intCheckedAdd, longAdd)
}
Expand Down Expand Up @@ -309,6 +347,7 @@ func initIntType(dict map[string]*Object) {
IntType.slots.New = &newSlot{intNew}
IntType.slots.NonZero = &unaryOpSlot{intNonZero}
IntType.slots.Or = &binaryOpSlot{intOr}
IntType.slots.Pow = &binaryOpSlot{intPow}
IntType.slots.RAdd = &binaryOpSlot{intRAdd}
IntType.slots.RAnd = &binaryOpSlot{intAnd}
IntType.slots.RDiv = &binaryOpSlot{intRDiv}
Expand Down
4 changes: 4 additions & 0 deletions runtime/int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ func TestIntBinaryOps(t *testing.T) {
{Or, NewInt(-100).ToObject(), NewInt(50).ToObject(), NewInt(-66).ToObject(), nil},
{Or, NewInt(MaxInt).ToObject(), NewInt(MinInt).ToObject(), NewInt(-1).ToObject(), nil},
{Or, newObject(ObjectType), NewInt(-100).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for |: 'object' and 'int'")},
{Pow, NewInt(2).ToObject(), NewInt(128).ToObject(), NewLong(big.NewInt(0).Exp(big.NewInt(2), big.NewInt(128), nil)).ToObject(), nil},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple more test cases (e.g. TypeError case) wouldn't go amiss.

{Pow, NewInt(2).ToObject(), newObject(ObjectType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'int' and 'object'")},
{Pow, NewInt(2).ToObject(), NewInt(-2).ToObject(), NewFloat(0.25).ToObject(), nil},
{Pow, newObject(ObjectType), NewInt(2).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'object' and 'int'")},
{Sub, NewInt(22).ToObject(), NewInt(18).ToObject(), NewInt(4).ToObject(), nil},
{Sub, IntType.ToObject(), NewInt(42).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for -: 'type' and 'int'")},
{Sub, NewInt(MinInt).ToObject(), NewInt(1).ToObject(), NewLong(new(big.Int).Sub(minIntBig, big.NewInt(1))).ToObject(), nil},
Expand Down
54 changes: 54 additions & 0 deletions runtime/long.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ func initLongType(dict map[string]*Object) {
LongType.slots.New = &newSlot{longNew}
LongType.slots.NonZero = longUnaryBoolOpSlot(longNonZero)
LongType.slots.Or = longBinaryOpSlot(longOr)
// This operation can return a float, it must use binaryOpSlot directly.
LongType.slots.Pow = &binaryOpSlot{longPow}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason not to use longBinaryOpSlot() here? It's a well established pattern here and it takes care of a little bit of the type coercion for you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

longBinaryOpSlot expect a function which always return a Long. But in **, when the exponent is negative, we have to return a Float. So this function can not be used (or I'm missing something here).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. No I don't think you're missing anything. It's probably worth adding a comment about why we don't use longBinaryOpSlot for this method.

LongType.slots.RAdd = longRBinaryOpSlot(longAdd)
LongType.slots.RAnd = longRBinaryOpSlot(longAnd)
LongType.slots.RDiv = longRDivModOpSlot(longDiv)
Expand All @@ -329,6 +331,8 @@ func initLongType(dict map[string]*Object) {
LongType.slots.RMul = longRBinaryOpSlot(longMul)
LongType.slots.ROr = longRBinaryOpSlot(longOr)
LongType.slots.RLShift = longRShiftOpSlot(longLShift)
// This operation can return a float, it must use binaryOpSlot directly.
LongType.slots.RPow = &binaryOpSlot{longRPow}
LongType.slots.RRShift = longRShiftOpSlot(longRShift)
LongType.slots.RShift = longShiftOpSlot(longRShift)
LongType.slots.RSub = longRBinaryOpSlot(longSub)
Expand Down Expand Up @@ -487,6 +491,56 @@ func longRBinaryBoolOpSlot(fun func(x, y *big.Int) bool) *binaryOpSlot {
return &binaryOpSlot{f}
}

func longPow(f *Frame, v, w *Object) (*Object, *BaseException) {
var wLong *big.Int

vLong := toLongUnsafe(v).Value()
if w.isInstance(LongType) {
wLong = toLongUnsafe(w).Value()
} else if w.isInstance(IntType) {
wLong = big.NewInt(int64(toIntUnsafe(w).Value()))
} else {
return NotImplemented, nil
}

if wLong.Sign() < 0 {
// The result will be a float, so we call the floating point function.
var vFloat, wFloat *Object
var raised *BaseException

vFloat, raised = longFloat(f, v)
if raised != nil {
return nil, raised
}
// w might be an int or a long
if w.isInstance(LongType) {
wFloat, raised = longFloat(f, w)
if raised != nil {
return nil, raised
}
} else if w.isInstance(IntType) {
wFloat = NewFloat(float64(toIntUnsafe(w).Value())).ToObject()
} else {
// This point should not be reachable
return nil, f.RaiseType(SystemErrorType, "internal error in longPow")
}
return floatPow(f, vFloat, wFloat)
}

return NewLong(big.NewInt(0).Exp(vLong, wLong, nil)).ToObject(), nil
}

func longRPow(f *Frame, v, w *Object) (*Object, *BaseException) {
if w.isInstance(LongType) {
return longPow(f, w, v)
}
if w.isInstance(IntType) {
wLong := NewLong(big.NewInt(int64(toIntUnsafe(w).Value()))).ToObject()
return longPow(f, wLong, v)
}
return NotImplemented, nil
}

func longDivMod(x, y, z, m *big.Int) {
z.QuoRem(x, y, m)
if m.Sign() == -y.Sign() {
Expand Down
3 changes: 3 additions & 0 deletions runtime/long_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ func TestLongBinaryOps(t *testing.T) {
{Or, -100, 50, NewLong(big.NewInt(-66)).ToObject(), nil},
{Or, MaxInt, MinInt, NewLong(big.NewInt(-1)).ToObject(), nil},
{Or, newObject(ObjectType), 100, nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for |: 'object' and 'long'")},
{Pow, 2, 128, NewLong(big.NewInt(0).Exp(big.NewInt(2), big.NewInt(128), nil)).ToObject(), nil},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple more test cases would be good

{Pow, 2, -2, NewFloat(0.25).ToObject(), nil},
{Pow, 2, newObject(ObjectType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'long' and 'object'")},
{Sub, 22, 18, NewLong(big.NewInt(4)).ToObject(), nil},
{Sub, IntType.ToObject(), 42, nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for -: 'type' and 'long'")},
{Sub, MinInt, 1, NewLong(new(big.Int).Sub(minIntBig, big.NewInt(1))).ToObject(), nil},
Expand Down
3 changes: 3 additions & 0 deletions runtime/slots.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ type typeSlots struct {
Int *unaryOpSlot
Invert *unaryOpSlot
IOr *binaryOpSlot
IPow *binaryOpSlot
ISub *binaryOpSlot
Iter *unaryOpSlot
IXor *binaryOpSlot
Expand All @@ -413,6 +414,7 @@ type typeSlots struct {
Next *unaryOpSlot
NonZero *unaryOpSlot
Or *binaryOpSlot
Pow *binaryOpSlot
RAdd *binaryOpSlot
RAnd *binaryOpSlot
RDiv *binaryOpSlot
Expand All @@ -421,6 +423,7 @@ type typeSlots struct {
RMod *binaryOpSlot
RMul *binaryOpSlot
ROr *binaryOpSlot
RPow *binaryOpSlot
RRShift *binaryOpSlot
RShift *binaryOpSlot
RSub *binaryOpSlot
Expand Down
80 changes: 80 additions & 0 deletions testing/pow_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the file header:

// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit, but I don't think we need to create a whole new file for just this one pow test. Put it in testing/op_test.py, maybe?

assert 2.0 ** -2 == 0.25, "2.0 ** -2"
assert 2.0 ** -1 == 0.5, "2.0 ** -1"
assert 2.0 ** 0 == 1, "2.0 ** 0"
assert 2.0 ** 1 == 2, "2.0 ** 1"
assert 2.0 ** 2 == 4, "2.0 ** 2"

assert (-2.0) ** -2 == 0.25, "(-2.0) ** -2"
assert (-2.0) ** -1 == -0.5, "(-2.0) ** -1"
assert (-2.0) ** 0 == 1, "(-2.0) ** 0"
assert (-2.0) ** 1 == -2, "(-2.0) ** 1"
assert (-2.0) ** 2 == 4, "(-2.0) ** 2"

assert 2 ** -2 == 0.25, "2 ** -2"
assert 2 ** -1 == 0.5, "2 ** -1"
assert 2 ** 0 == 1, "2 ** 0"
assert 2 ** 1 == 2, "2 ** 1"
assert 2 ** 2 == 4, "2 ** 2"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to have some explicit long tests here, e.g. assert 2L ** 2 == 4, "2L ** 2"


assert 2L ** -2 == 0.25, "2L ** -2"
assert 2L ** -1 == 0.5, "2L ** -1"
assert 2L ** 0 == 1, "2L ** 0"
assert 2L ** 1 == 2, "2L ** 1"
assert 2L ** 2 == 4, "2L ** 2"

# Test the rpow operator on long
assert 2 ** -2L == 0.25, "2 ** -2L"
assert 2 ** -1L == 0.5, "2 ** -1L"
assert 2 ** 0L == 1, "2 ** 0L"
assert 2 ** 1L == 2, "2 ** 1L"
assert 2 ** 2L == 4, "2 ** 2L"

for zero in (0, 0L, 0.0):
try:
result = zero ** -2
assert "0 ** -2"
except ZeroDivisionError:
pass

try:
result = zero ** -1
assert "0 ** -1"
except ZeroDivisionError:
pass

assert zero ** 0 == 1, '0 ** 0'
assert zero ** 1 == 0, '0 ** 1'
assert zero ** 2 == 0, '0 ** 2'

assert 2 ** zero == 1
assert (-2.0) ** zero == 1
assert 3L ** zero == 1

assert (-2) ** -2 == 0.25, '(-2) ** -2'
assert (-2) ** -1 == -0.5, '(-2) ** -1'
assert (-2) ** 0 == 1, '(-2) ** 0'
assert (-2) ** 1 == -2, '(-2) ** 1'
assert (-2) ** 2 == 4, '(-2) ** 2'

assert 2 ** 128 == 340282366920938463463374607431768211456, "2 ** 128"

# chose something which can be represented exact as an IEEE floating point number
large_number = (2 ** 128 + 2 ** 127)

assert large_number ** -1 == (1.0 / large_number), "large_number ** -1 == (1.0 / large_number)"
assert large_number ** 0 == 1, "large_number ** 0 == 1"
assert large_number ** 1 == large_number, "large_number ** 1 == large_number"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove excess trailing newlines (one is OK).