Skip to content

Conversation

@rhettinger
Copy link
Contributor

@rhettingerrhettinger commented Jul 26, 2018

Copy link
Member

@tim-onetim-one left a comment

Choose a reason for hiding this comment

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

Looks useful to me! Left some minor comments.


# Test corner cases
self.assertEqual(hypot(0.0, 0.0), 0.0) # Max input is zero
self.assertEqual(hypot(-10.5), 10.5) # Negative input
Copy link
Member

Choose a reason for hiding this comment

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

Add self.assertEqual(hypot(), 0.0)?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, added an explict test for this case. FWIW, this was already tested in the section "Test different numbers of arguments (from zero to five)".

# Verify scaling for extremely small values
for exp in range(32):
scale = FLOAT_MIN / 2.0 ** exp
self.assertEqual(math.hypot(12*scale, 5*scale), 13*scale)
Copy link
Member

@tim-onetim-oneJul 26, 2018

Choose a reason for hiding this comment

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

This makes me nervous, because it depends on that

12 * math.sqrt(1 + (5/12)**2) 

happens to be exactly 13.0 despite that 5/12 isn't exactly representable. Would be happier with:

self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) 

because every intermediate value in

4 * math.sqrt(1 + (3/4)**2) 

is exactly representable in binary.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, changed to a 3-4-5 triangle.

}
for (i=0 ; i<n ; i++){
item = PyTuple_GET_ITEM(args, i);
x = PyFloat_AsDouble(item) / max;
Copy link
Member

Choose a reason for hiding this comment

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

Are we quite sure a malicious __float__() implementation can't mutate other values in the argument tuple? That is, that no conversion failed the first time through the tuple guarantees none will fail the second time through?

I don't know. In which case checking for a -1.0 error return here too would be safest.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, added the -1.0 test.

x = PyFloat_AsDouble(item) / max;
csum += x * x;
}
return PyFloat_FromDouble(max * sqrt(csum)); // XXX Handle overflow
Copy link
Member

Choose a reason for hiding this comment

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

It's good enough for me that +Inf will result from overflow here, but that it is a change from, e.g., what 3.7 does on Windows:

>>> import sys >>> import math >>> x = sys.float_info.max >>> math.hypot(x, x) Traceback (most recent call last): ... OverflowError: math range error 

Copy link
ContributorAuthor

@rhettingerrhettingerJul 27, 2018

Choose a reason for hiding this comment

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

That is good enough for me too :-) I like that it matches the behavior of the pure python code:

>>> import sys >>> coordinates = [sys.float_info.max] * 1 >>> scale * sqrt(sum((x/scale) ** x for x in coordinates)) 1.7976931348623157e+308 >>> coordinates = [sys.float_info.max] * 2 >>> scale * sqrt(sum((x/scale) ** x for x in coordinates)) inf 

* Made test for the zero argument case explicit. * Use exactly representable numbers in the extreme small value tests. * Guard against a malicious __float__ that succeeds on the first call and fails on the second.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rhettinger@tim-one@the-knights-who-say-ni@bedevere-bot